1 | /* |
2 | * Copyright (C) 2019 Igalia S.L. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "JPEG2000ImageDecoder.h" |
28 | |
29 | #if USE(OPENJPEG) |
30 | |
31 | #include <openjpeg.h> |
32 | |
33 | namespace WebCore { |
34 | |
35 | // SYCC to RGB conversion code from libopenjpeg (BSD), adapted to WebKit coding style. |
36 | // -------------------------------------------------------- |
37 | // Matrix for sYCC, Amendment 1 to IEC 61966-2-1 |
38 | // |
39 | // Y : 0.299 0.587 0.114 :R |
40 | // Cb: -0.1687 -0.3312 0.5 :G |
41 | // Cr: 0.5 -0.4187 -0.0812 :B |
42 | // |
43 | // Inverse: |
44 | // |
45 | // R: 1 -3.68213e-05 1.40199 :Y |
46 | // G: 1.00003 -0.344125 -0.714128 :Cb - 2^(prec - 1) |
47 | // B: 0.999823 1.77204 -8.04142e-06 :Cr - 2^(prec - 1) |
48 | // |
49 | // -----------------------------------------------------------*/ |
50 | static void syccToRGB(int offset, int upb, int y, int cb, int cr, int* r, int* g, int* b) |
51 | { |
52 | cb -= offset; |
53 | cr -= offset; |
54 | |
55 | *r = static_cast<int>(clampTo<float>(y - 0.0000368 * cb + 1.40199 * cr + 0.5, 0, upb)); |
56 | *g = static_cast<int>(clampTo<float>(1.0003 * y - 0.344125 * cb - 0.7141128 * cr + 0.5, 0, upb)); |
57 | *b = static_cast<int>(clampTo<float>(0.999823 * y + 1.77204 * cb - 0.000008 * cr + 0.5, 0, upb)); |
58 | } |
59 | |
60 | static void sycc444ToRGB(opj_image_t* img) |
61 | { |
62 | Checked<int> upb = static_cast<int>(img->comps[0].prec); |
63 | int offset = 1 << (upb.unsafeGet() - 1); |
64 | upb = (1 << upb.unsafeGet()) - 1; |
65 | |
66 | Checked<size_t> maxw = static_cast<size_t>(img->comps[0].w); |
67 | Checked<size_t> maxh = static_cast<size_t>(img->comps[0].h); |
68 | size_t max = (maxw * maxh).unsafeGet(); |
69 | |
70 | const int* y = img->comps[0].data; |
71 | const int* cb = img->comps[1].data; |
72 | const int* cr = img->comps[2].data; |
73 | |
74 | auto* d0 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
75 | auto* d1 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
76 | auto* d2 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
77 | auto* r = d0; |
78 | auto* g = d1; |
79 | auto* b = d2; |
80 | |
81 | if (!r || !g || !b) { |
82 | opj_image_data_free(r); |
83 | opj_image_data_free(g); |
84 | opj_image_data_free(b); |
85 | return; |
86 | } |
87 | |
88 | for (size_t i = 0; i < max; ++i) { |
89 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
90 | ++y; |
91 | ++cb; |
92 | ++cr; |
93 | ++r; |
94 | ++g; |
95 | ++b; |
96 | } |
97 | |
98 | opj_image_data_free(img->comps[0].data); |
99 | img->comps[0].data = d0; |
100 | opj_image_data_free(img->comps[1].data); |
101 | img->comps[1].data = d1; |
102 | opj_image_data_free(img->comps[2].data); |
103 | img->comps[2].data = d2; |
104 | img->color_space = OPJ_CLRSPC_SRGB; |
105 | } |
106 | |
107 | static void sycc422ToRGB(opj_image_t* img) |
108 | { |
109 | Checked<int> upb = static_cast<int>(img->comps[0].prec); |
110 | int offset = 1 << (upb.unsafeGet() - 1); |
111 | upb = (1 << upb.unsafeGet()) - 1; |
112 | |
113 | Checked<size_t> maxw = static_cast<size_t>(img->comps[0].w); |
114 | Checked<size_t> maxh = static_cast<size_t>(img->comps[0].h); |
115 | size_t max = (maxw * maxh).unsafeGet(); |
116 | |
117 | const int* y = img->comps[0].data; |
118 | const int* cb = img->comps[1].data; |
119 | const int* cr = img->comps[2].data; |
120 | |
121 | auto* d0 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
122 | auto* d1 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
123 | auto* d2 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
124 | auto* r = d0; |
125 | auto* g = d1; |
126 | auto* b = d2; |
127 | |
128 | if (!r || !g || !b) { |
129 | opj_image_data_free(r); |
130 | opj_image_data_free(g); |
131 | opj_image_data_free(b); |
132 | return; |
133 | } |
134 | |
135 | // if img->x0 is odd, then first column shall use Cb/Cr = 0. |
136 | size_t offx = img->x0 & 1U; |
137 | size_t loopmaxw = maxw.unsafeGet() - offx; |
138 | |
139 | for (size_t i = 0; i < maxh.unsafeGet(); ++i) { |
140 | size_t j; |
141 | |
142 | if (offx > 0) { |
143 | syccToRGB(offset, upb.unsafeGet(), *y, 0, 0, r, g, b); |
144 | ++y; |
145 | ++r; |
146 | ++g; |
147 | ++b; |
148 | } |
149 | |
150 | for (j = 0; j < (loopmaxw & ~static_cast<size_t>(1U)); j += 2U) { |
151 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
152 | ++y; |
153 | ++r; |
154 | ++g; |
155 | ++b; |
156 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
157 | ++y; |
158 | ++r; |
159 | ++g; |
160 | ++b; |
161 | ++cb; |
162 | ++cr; |
163 | } |
164 | if (j < loopmaxw) { |
165 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
166 | ++y; |
167 | ++r; |
168 | ++g; |
169 | ++b; |
170 | ++cb; |
171 | ++cr; |
172 | } |
173 | } |
174 | |
175 | opj_image_data_free(img->comps[0].data); |
176 | img->comps[0].data = d0; |
177 | opj_image_data_free(img->comps[1].data); |
178 | img->comps[1].data = d1; |
179 | opj_image_data_free(img->comps[2].data); |
180 | img->comps[2].data = d2; |
181 | |
182 | img->comps[1].w = img->comps[2].w = img->comps[0].w; |
183 | img->comps[1].h = img->comps[2].h = img->comps[0].h; |
184 | img->comps[1].dx = img->comps[2].dx = img->comps[0].dx; |
185 | img->comps[1].dy = img->comps[2].dy = img->comps[0].dy; |
186 | img->color_space = OPJ_CLRSPC_SRGB; |
187 | } |
188 | |
189 | static void sycc420ToRGB(opj_image_t* img) |
190 | { |
191 | Checked<int> upb = static_cast<int>(img->comps[0].prec); |
192 | int offset = 1 << (upb.unsafeGet() - 1); |
193 | upb = (1 << upb.unsafeGet()) - 1; |
194 | |
195 | Checked<size_t> maxw = static_cast<size_t>(img->comps[0].w); |
196 | Checked<size_t> maxh = static_cast<size_t>(img->comps[0].h); |
197 | size_t max = (maxw * maxh).unsafeGet(); |
198 | |
199 | const int* y = img->comps[0].data; |
200 | const int* cb = img->comps[1].data; |
201 | const int* cr = img->comps[2].data; |
202 | |
203 | auto* d0 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
204 | auto* d1 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
205 | auto* d2 = static_cast<int*>(opj_image_data_alloc(sizeof(int) * max)); |
206 | auto* r = d0; |
207 | auto* g = d1; |
208 | auto* b = d2; |
209 | |
210 | if (!r || !g || !b) { |
211 | opj_image_data_free(r); |
212 | opj_image_data_free(g); |
213 | opj_image_data_free(b); |
214 | return; |
215 | } |
216 | |
217 | // if img->x0 is odd, then first column shall use Cb/Cr = 0. |
218 | size_t offx = img->x0 & 1U; |
219 | size_t loopmaxw = maxw.unsafeGet() - offx; |
220 | // if img->y0 is odd, then first line shall use Cb/Cr = 0. |
221 | size_t offy = img->y0 & 1U; |
222 | size_t loopmaxh = maxh.unsafeGet() - offy; |
223 | |
224 | if (offy > 0) { |
225 | size_t j; |
226 | for (j = 0; j < maxw.unsafeGet(); ++j) { |
227 | syccToRGB(offset, upb.unsafeGet(), *y, 0, 0, r, g, b); |
228 | ++y; |
229 | ++r; |
230 | ++g; |
231 | ++b; |
232 | } |
233 | } |
234 | |
235 | size_t i; |
236 | for (i = 0; i < (loopmaxh & ~static_cast<size_t>(1U)); i += 2U) { |
237 | const int* ny = y + maxw.unsafeGet(); |
238 | int* nr = r + maxw.unsafeGet(); |
239 | int* ng = g + maxw.unsafeGet(); |
240 | int* nb = b + maxw.unsafeGet(); |
241 | |
242 | if (offx > 0) { |
243 | syccToRGB(offset, upb.unsafeGet(), *y, 0, 0, r, g, b); |
244 | ++y; |
245 | ++r; |
246 | ++g; |
247 | ++b; |
248 | syccToRGB(offset, upb.unsafeGet(), *ny, *cb, *cr, nr, ng, nb); |
249 | ++ny; |
250 | ++nr; |
251 | ++ng; |
252 | ++nb; |
253 | } |
254 | |
255 | size_t j; |
256 | for (j = 0; j < (loopmaxw & ~static_cast<size_t>(1U)); j += 2U) { |
257 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
258 | ++y; |
259 | ++r; |
260 | ++g; |
261 | ++b; |
262 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
263 | ++y; |
264 | ++r; |
265 | ++g; |
266 | ++b; |
267 | |
268 | syccToRGB(offset, upb.unsafeGet(), *ny, *cb, *cr, nr, ng, nb); |
269 | ++ny; |
270 | ++nr; |
271 | ++ng; |
272 | ++nb; |
273 | syccToRGB(offset, upb.unsafeGet(), *ny, *cb, *cr, nr, ng, nb); |
274 | ++ny; |
275 | ++nr; |
276 | ++ng; |
277 | ++nb; |
278 | ++cb; |
279 | ++cr; |
280 | } |
281 | if (j < loopmaxw) { |
282 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
283 | ++y; |
284 | ++r; |
285 | ++g; |
286 | ++b; |
287 | |
288 | syccToRGB(offset, upb.unsafeGet(), *ny, *cb, *cr, nr, ng, nb); |
289 | ++ny; |
290 | ++nr; |
291 | ++ng; |
292 | ++nb; |
293 | ++cb; |
294 | ++cr; |
295 | } |
296 | y += maxw.unsafeGet(); |
297 | r += maxw.unsafeGet(); |
298 | g += maxw.unsafeGet(); |
299 | b += maxw.unsafeGet(); |
300 | } |
301 | if (i < loopmaxh) { |
302 | size_t j; |
303 | for (j = 0; j < (maxw.unsafeGet() & ~static_cast<size_t>(1U)); j += 2U) { |
304 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
305 | |
306 | ++y; |
307 | ++r; |
308 | ++g; |
309 | ++b; |
310 | |
311 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
312 | |
313 | ++y; |
314 | ++r; |
315 | ++g; |
316 | ++b; |
317 | ++cb; |
318 | ++cr; |
319 | } |
320 | if (j < maxw.unsafeGet()) |
321 | syccToRGB(offset, upb.unsafeGet(), *y, *cb, *cr, r, g, b); |
322 | } |
323 | |
324 | opj_image_data_free(img->comps[0].data); |
325 | img->comps[0].data = d0; |
326 | opj_image_data_free(img->comps[1].data); |
327 | img->comps[1].data = d1; |
328 | opj_image_data_free(img->comps[2].data); |
329 | img->comps[2].data = d2; |
330 | |
331 | img->comps[1].w = img->comps[2].w = img->comps[0].w; |
332 | img->comps[1].h = img->comps[2].h = img->comps[0].h; |
333 | img->comps[1].dx = img->comps[2].dx = img->comps[0].dx; |
334 | img->comps[1].dy = img->comps[2].dy = img->comps[0].dy; |
335 | img->color_space = OPJ_CLRSPC_SRGB; |
336 | } |
337 | |
338 | JPEG2000ImageDecoder::JPEG2000ImageDecoder(Format format, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) |
339 | : ScalableImageDecoder(alphaOption, gammaAndColorProfileOption) |
340 | , m_format(format) |
341 | { |
342 | } |
343 | |
344 | ScalableImageDecoderFrame* JPEG2000ImageDecoder::frameBufferAtIndex(size_t index) |
345 | { |
346 | if (index) |
347 | return nullptr; |
348 | |
349 | if (m_frameBufferCache.isEmpty()) |
350 | m_frameBufferCache.grow(1); |
351 | |
352 | auto& frame = m_frameBufferCache[0]; |
353 | if (!frame.isComplete()) |
354 | decode(false, isAllDataReceived()); |
355 | return &frame; |
356 | } |
357 | |
358 | void JPEG2000ImageDecoder::decode(bool onlySize, bool allDataReceived) |
359 | { |
360 | if (failed()) |
361 | return; |
362 | |
363 | std::unique_ptr<opj_codec_t, void(*)(opj_codec_t*)> decoder(opj_create_decompress(m_format == Format::JP2 ? OPJ_CODEC_JP2 : OPJ_CODEC_J2K), opj_destroy_codec); |
364 | if (!decoder) { |
365 | setFailed(); |
366 | return; |
367 | } |
368 | |
369 | opj_dparameters_t parameters; |
370 | opj_set_default_decoder_parameters(¶meters); |
371 | if (!opj_setup_decoder(decoder.get(), ¶meters)) { |
372 | setFailed(); |
373 | return; |
374 | } |
375 | |
376 | std::unique_ptr<opj_stream_t, void(*)(opj_stream_t*)> stream(opj_stream_default_create(OPJ_TRUE), opj_stream_destroy); |
377 | if (!stream) { |
378 | setFailed(); |
379 | return; |
380 | } |
381 | |
382 | struct Reader { |
383 | SharedBuffer& data; |
384 | size_t offset; |
385 | } reader = { *m_data, 0 }; |
386 | |
387 | opj_stream_set_user_data(stream.get(), &reader, nullptr); |
388 | opj_stream_set_user_data_length(stream.get(), m_data->size()); |
389 | opj_stream_set_read_function(stream.get(), [](void* buffer, OPJ_SIZE_T bytes, void* userData) -> OPJ_SIZE_T { |
390 | auto& reader = *static_cast<Reader*>(userData); |
391 | if (reader.offset == reader.data.size()) |
392 | return -1; |
393 | |
394 | OPJ_SIZE_T length = reader.offset + bytes > reader.data.size() ? reader.data.size() - reader.offset : bytes; |
395 | memcpy(buffer, reader.data.data(), length); |
396 | reader.offset += length; |
397 | |
398 | return length; |
399 | }); |
400 | opj_stream_set_skip_function(stream.get(), [](OPJ_OFF_T bytes, void* userData) -> OPJ_OFF_T { |
401 | auto& reader = *static_cast<Reader*>(userData); |
402 | |
403 | OPJ_OFF_T skip = reader.offset + bytes > reader.data.size() ? reader.data.size() - reader.offset : bytes; |
404 | reader.offset += skip; |
405 | |
406 | return skip; |
407 | }); |
408 | opj_stream_set_seek_function(stream.get(), [](OPJ_OFF_T bytes, void* userData) -> OPJ_BOOL { |
409 | auto& reader = *static_cast<Reader*>(userData); |
410 | |
411 | if (static_cast<unsigned>(bytes) > reader.data.size()) |
412 | return OPJ_FALSE; |
413 | |
414 | reader.offset = bytes; |
415 | |
416 | return OPJ_TRUE; |
417 | }); |
418 | |
419 | opj_image_t* imagePtr = nullptr; |
420 | if (!opj_read_header(stream.get(), decoder.get(), &imagePtr)) { |
421 | if (allDataReceived) |
422 | setFailed(); |
423 | opj_image_destroy(imagePtr); |
424 | return; |
425 | } |
426 | |
427 | std::unique_ptr<opj_image_t, void(*)(opj_image_t*)> image(imagePtr, opj_image_destroy); |
428 | setSize({ static_cast<int>(image->x1 - image->x0), static_cast<int>(image->y1 - image->y0) }); |
429 | if (onlySize) |
430 | return; |
431 | |
432 | if (!opj_decode(decoder.get(), stream.get(), image.get())) { |
433 | if (allDataReceived) |
434 | setFailed(); |
435 | return; |
436 | } |
437 | |
438 | if (image->color_space == OPJ_CLRSPC_UNSPECIFIED) { |
439 | if (image->numcomps == 3 && image->comps[0].dx == image->comps[0].dy && image->comps[1].dx != 1) |
440 | image->color_space = OPJ_CLRSPC_SYCC; |
441 | else if (image->numcomps <= 2) |
442 | image->color_space = OPJ_CLRSPC_GRAY; |
443 | } |
444 | |
445 | if (image->color_space == OPJ_CLRSPC_SYCC) { |
446 | if (image->numcomps < 3) |
447 | image->color_space = OPJ_CLRSPC_GRAY; |
448 | else if ((image->comps[0].dx == 1) |
449 | && (image->comps[1].dx == 2) |
450 | && (image->comps[2].dx == 2) |
451 | && (image->comps[0].dy == 1) |
452 | && (image->comps[1].dy == 2) |
453 | && (image->comps[2].dy == 2)) { |
454 | // Horizontal and vertical sub-sample. |
455 | sycc420ToRGB(image.get()); |
456 | } else if ((image->comps[0].dx == 1) |
457 | && (image->comps[1].dx == 2) |
458 | && (image->comps[2].dx == 2) |
459 | && (image->comps[0].dy == 1) |
460 | && (image->comps[1].dy == 1) |
461 | && (image->comps[2].dy == 1)) { |
462 | // Horizontal sub-sample only. |
463 | sycc422ToRGB(image.get()); |
464 | } else if ((image->comps[0].dx == 1) |
465 | && (image->comps[1].dx == 1) |
466 | && (image->comps[2].dx == 1) |
467 | && (image->comps[0].dy == 1) |
468 | && (image->comps[1].dy == 1) |
469 | && (image->comps[2].dy == 1)) { |
470 | // No sub-sample. |
471 | sycc444ToRGB(image.get()); |
472 | } |
473 | } |
474 | |
475 | if (image->color_space != OPJ_CLRSPC_SRGB || image->numcomps > 4 || image->numcomps < 3) { |
476 | // Unsupported format. |
477 | setFailed(); |
478 | return; |
479 | } |
480 | |
481 | auto& buffer = m_frameBufferCache[0]; |
482 | if (!buffer.initialize(scaledSize(), m_premultiplyAlpha)) { |
483 | setFailed(); |
484 | return; |
485 | } |
486 | |
487 | buffer.setDecodingStatus(DecodingStatus::Partial); |
488 | buffer.setHasAlpha(false); |
489 | |
490 | int adjust[4] = { 0, 0, 0, 0 }; |
491 | for (OPJ_UINT32 component = 0; component < image->numcomps; ++component) { |
492 | if (!image->comps[component].data) { |
493 | setFailed(); |
494 | break; |
495 | } |
496 | |
497 | if (image->comps[component].prec > 8) |
498 | adjust[component] = image->comps[component].prec - 8; |
499 | } |
500 | |
501 | bool subsampling = image->comps[0].dx != 1 || image->comps[0].dy != 1 || image->comps[1].dx != 1 || image->comps[1].dy != 1 || image->comps[2].dx != 1 || image->comps[2].dy != 1; |
502 | unsigned char nonTrivialAlphaMask = 0; |
503 | const IntRect& frameRect = buffer.backingStore()->frameRect(); |
504 | for (int y = 0; y < frameRect.height(); ++y) { |
505 | for (int x = 0; x < frameRect.width(); ++x) { |
506 | int r, g, b, a; |
507 | |
508 | int offset; |
509 | if (subsampling) { |
510 | int subX = frameRect.width() / image->comps[0].w; |
511 | int subY = frameRect.height() / image->comps[0].h; |
512 | offset = (y / subY) * image->comps[0].w + (x / subX); |
513 | } else |
514 | offset = y * frameRect.width() + x; |
515 | |
516 | r = image->comps[0].data[offset]; |
517 | r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); |
518 | if (subsampling) { |
519 | int subX = frameRect.width() / image->comps[1].w; |
520 | int subY = frameRect.height() / image->comps[1].h; |
521 | offset = (y / subY) * image->comps[1].w + (x / subX); |
522 | } |
523 | g = image->comps[1].data[offset]; |
524 | g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); |
525 | if (subsampling) { |
526 | int subX = frameRect.width() / image->comps[2].w; |
527 | int subY = frameRect.height() / image->comps[2].h; |
528 | offset = (y / subY) * image->comps[2].w + (x / subX); |
529 | } |
530 | b = image->comps[2].data[offset]; |
531 | b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); |
532 | |
533 | if (image->numcomps > 3) { |
534 | if (subsampling) { |
535 | int subX = frameRect.width() / image->comps[3].w; |
536 | int subY = frameRect.height() / image->comps[3].h; |
537 | offset = (y / subY) * image->comps[3].w + (x / subX); |
538 | } |
539 | a = image->comps[3].data[offset]; |
540 | a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); |
541 | } |
542 | |
543 | int adjustedRed = (r >> adjust[0]) + ((r >> (adjust[0] - 1)) % 2); |
544 | int adjustedGreen = (g >> adjust[1]) + ((g >> (adjust[1] - 1)) % 2); |
545 | int adjustedBlue = (b >> adjust[2]) + ((b >> (adjust[2] - 1)) % 2); |
546 | int adjustedAlpha = image->numcomps > 3 ? (a >> adjust[3]) + ((a >> (adjust[3] - 1)) % 2) : 0xFF; |
547 | buffer.backingStore()->setPixel(x, y, adjustedRed, adjustedGreen, adjustedBlue, adjustedAlpha); |
548 | nonTrivialAlphaMask |= (255 - adjustedAlpha); |
549 | } |
550 | } |
551 | |
552 | buffer.setDecodingStatus(DecodingStatus::Complete); |
553 | if (nonTrivialAlphaMask && !buffer.hasAlpha()) |
554 | buffer.setHasAlpha(true); |
555 | } |
556 | |
557 | } // namespace WebCore |
558 | |
559 | #endif // USE(OPENJPEG) |
560 | |