|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "ImageLogging.h" |
|
8 #include "nsJPEGDecoder.h" |
|
9 #include "Orientation.h" |
|
10 #include "EXIF.h" |
|
11 |
|
12 #include "nsIInputStream.h" |
|
13 |
|
14 #include "nspr.h" |
|
15 #include "nsCRT.h" |
|
16 #include "gfxColor.h" |
|
17 |
|
18 #include "jerror.h" |
|
19 |
|
20 #include "gfxPlatform.h" |
|
21 |
|
22 extern "C" { |
|
23 #include "iccjpeg.h" |
|
24 } |
|
25 |
|
26 #if defined(IS_BIG_ENDIAN) |
|
27 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_XRGB |
|
28 #else |
|
29 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX |
|
30 #endif |
|
31 |
|
32 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width); |
|
33 |
|
34 namespace mozilla { |
|
35 namespace image { |
|
36 |
|
37 #if defined(PR_LOGGING) |
|
38 static PRLogModuleInfo * |
|
39 GetJPEGLog() |
|
40 { |
|
41 static PRLogModuleInfo *sJPEGLog; |
|
42 if (!sJPEGLog) |
|
43 sJPEGLog = PR_NewLogModule("JPEGDecoder"); |
|
44 return sJPEGLog; |
|
45 } |
|
46 |
|
47 static PRLogModuleInfo * |
|
48 GetJPEGDecoderAccountingLog() |
|
49 { |
|
50 static PRLogModuleInfo *sJPEGDecoderAccountingLog; |
|
51 if (!sJPEGDecoderAccountingLog) |
|
52 sJPEGDecoderAccountingLog = PR_NewLogModule("JPEGDecoderAccounting"); |
|
53 return sJPEGDecoderAccountingLog; |
|
54 } |
|
55 #else |
|
56 #define GetJPEGLog() |
|
57 #define GetJPEGDecoderAccountingLog() |
|
58 #endif |
|
59 |
|
60 static qcms_profile* |
|
61 GetICCProfile(struct jpeg_decompress_struct &info) |
|
62 { |
|
63 JOCTET* profilebuf; |
|
64 uint32_t profileLength; |
|
65 qcms_profile* profile = nullptr; |
|
66 |
|
67 if (read_icc_profile(&info, &profilebuf, &profileLength)) { |
|
68 profile = qcms_profile_from_memory(profilebuf, profileLength); |
|
69 free(profilebuf); |
|
70 } |
|
71 |
|
72 return profile; |
|
73 } |
|
74 |
|
75 METHODDEF(void) init_source (j_decompress_ptr jd); |
|
76 METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd); |
|
77 METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes); |
|
78 METHODDEF(void) term_source (j_decompress_ptr jd); |
|
79 METHODDEF(void) my_error_exit (j_common_ptr cinfo); |
|
80 |
|
81 /* Normal JFIF markers can't have more bytes than this. */ |
|
82 #define MAX_JPEG_MARKER_LENGTH (((uint32_t)1 << 16) - 1) |
|
83 |
|
84 |
|
85 nsJPEGDecoder::nsJPEGDecoder(RasterImage& aImage, Decoder::DecodeStyle aDecodeStyle) |
|
86 : Decoder(aImage) |
|
87 , mDecodeStyle(aDecodeStyle) |
|
88 { |
|
89 mState = JPEG_HEADER; |
|
90 mReading = true; |
|
91 mImageData = nullptr; |
|
92 |
|
93 mBytesToSkip = 0; |
|
94 memset(&mInfo, 0, sizeof(jpeg_decompress_struct)); |
|
95 memset(&mSourceMgr, 0, sizeof(mSourceMgr)); |
|
96 mInfo.client_data = (void*)this; |
|
97 |
|
98 mSegment = nullptr; |
|
99 mSegmentLen = 0; |
|
100 |
|
101 mBackBuffer = nullptr; |
|
102 mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0; |
|
103 |
|
104 mInProfile = nullptr; |
|
105 mTransform = nullptr; |
|
106 |
|
107 mCMSMode = 0; |
|
108 |
|
109 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
110 ("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p", |
|
111 this)); |
|
112 } |
|
113 |
|
114 nsJPEGDecoder::~nsJPEGDecoder() |
|
115 { |
|
116 // Step 8: Release JPEG decompression object |
|
117 mInfo.src = nullptr; |
|
118 jpeg_destroy_decompress(&mInfo); |
|
119 |
|
120 PR_FREEIF(mBackBuffer); |
|
121 if (mTransform) |
|
122 qcms_transform_release(mTransform); |
|
123 if (mInProfile) |
|
124 qcms_profile_release(mInProfile); |
|
125 |
|
126 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
127 ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p", |
|
128 this)); |
|
129 } |
|
130 |
|
131 Telemetry::ID |
|
132 nsJPEGDecoder::SpeedHistogram() |
|
133 { |
|
134 return Telemetry::IMAGE_DECODE_SPEED_JPEG; |
|
135 } |
|
136 |
|
137 void |
|
138 nsJPEGDecoder::InitInternal() |
|
139 { |
|
140 mCMSMode = gfxPlatform::GetCMSMode(); |
|
141 if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) |
|
142 mCMSMode = eCMSMode_Off; |
|
143 |
|
144 /* We set up the normal JPEG error routines, then override error_exit. */ |
|
145 mInfo.err = jpeg_std_error(&mErr.pub); |
|
146 /* mInfo.err = jpeg_std_error(&mErr.pub); */ |
|
147 mErr.pub.error_exit = my_error_exit; |
|
148 /* Establish the setjmp return context for my_error_exit to use. */ |
|
149 if (setjmp(mErr.setjmp_buffer)) { |
|
150 /* If we get here, the JPEG code has signaled an error. |
|
151 * We need to clean up the JPEG object, close the input file, and return. |
|
152 */ |
|
153 PostDecoderError(NS_ERROR_FAILURE); |
|
154 return; |
|
155 } |
|
156 |
|
157 /* Step 1: allocate and initialize JPEG decompression object */ |
|
158 jpeg_create_decompress(&mInfo); |
|
159 /* Set the source manager */ |
|
160 mInfo.src = &mSourceMgr; |
|
161 |
|
162 /* Step 2: specify data source (eg, a file) */ |
|
163 |
|
164 /* Setup callback functions. */ |
|
165 mSourceMgr.init_source = init_source; |
|
166 mSourceMgr.fill_input_buffer = fill_input_buffer; |
|
167 mSourceMgr.skip_input_data = skip_input_data; |
|
168 mSourceMgr.resync_to_restart = jpeg_resync_to_restart; |
|
169 mSourceMgr.term_source = term_source; |
|
170 |
|
171 /* Record app markers for ICC data */ |
|
172 for (uint32_t m = 0; m < 16; m++) |
|
173 jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF); |
|
174 } |
|
175 |
|
176 void |
|
177 nsJPEGDecoder::FinishInternal() |
|
178 { |
|
179 /* If we're not in any sort of error case, flush the decoder. |
|
180 * |
|
181 * XXXbholley - It seems wrong that this should be necessary, but at the |
|
182 * moment I'm just folding the contents of Flush() into Close() so that |
|
183 * we can get rid of it. |
|
184 * |
|
185 * XXX(seth): It'd be great to get rid of this. For now, we treat this as a |
|
186 * write to a synchronous decoder, which means that this must be called only |
|
187 * on the main thread. (That's asserted in Decoder::Finish and |
|
188 * Decoder::FinishSharedDecoder.) |
|
189 */ |
|
190 if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) && |
|
191 (mState != JPEG_ERROR) && |
|
192 !IsSizeDecode()) |
|
193 this->Write(nullptr, 0, DECODE_SYNC); |
|
194 } |
|
195 |
|
196 void |
|
197 nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy) |
|
198 { |
|
199 mSegment = (const JOCTET *)aBuffer; |
|
200 mSegmentLen = aCount; |
|
201 |
|
202 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); |
|
203 |
|
204 /* Return here if there is a fatal error within libjpeg. */ |
|
205 nsresult error_code; |
|
206 // This cast to nsresult makes sense because setjmp() returns whatever we |
|
207 // passed to longjmp(), which was actually an nsresult. |
|
208 if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) { |
|
209 if (error_code == NS_ERROR_FAILURE) { |
|
210 PostDataError(); |
|
211 /* Error due to corrupt stream - return NS_OK and consume silently |
|
212 so that libpr0n doesn't throw away a partial image load */ |
|
213 mState = JPEG_SINK_NON_JPEG_TRAILER; |
|
214 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
215 ("} (setjmp returned NS_ERROR_FAILURE)")); |
|
216 return; |
|
217 } else { |
|
218 /* Error due to reasons external to the stream (probably out of |
|
219 memory) - let libpr0n attempt to clean up, even though |
|
220 mozilla is seconds away from falling flat on its face. */ |
|
221 PostDecoderError(error_code); |
|
222 mState = JPEG_ERROR; |
|
223 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
224 ("} (setjmp returned an error)")); |
|
225 return; |
|
226 } |
|
227 } |
|
228 |
|
229 PR_LOG(GetJPEGLog(), PR_LOG_DEBUG, |
|
230 ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); |
|
231 |
|
232 switch (mState) { |
|
233 case JPEG_HEADER: |
|
234 { |
|
235 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_HEADER case"); |
|
236 |
|
237 /* Step 3: read file parameters with jpeg_read_header() */ |
|
238 if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) { |
|
239 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
240 ("} (JPEG_SUSPENDED)")); |
|
241 return; /* I/O suspension */ |
|
242 } |
|
243 |
|
244 int sampleSize = mImage.GetRequestedSampleSize(); |
|
245 if (sampleSize > 0) { |
|
246 mInfo.scale_num = 1; |
|
247 mInfo.scale_denom = sampleSize; |
|
248 } |
|
249 |
|
250 /* Used to set up image size so arrays can be allocated */ |
|
251 jpeg_calc_output_dimensions(&mInfo); |
|
252 |
|
253 // Post our size to the superclass |
|
254 PostSize(mInfo.output_width, mInfo.output_height, ReadOrientationFromEXIF()); |
|
255 if (HasError()) { |
|
256 // Setting the size led to an error. |
|
257 mState = JPEG_ERROR; |
|
258 return; |
|
259 } |
|
260 |
|
261 /* If we're doing a size decode, we're done. */ |
|
262 if (IsSizeDecode()) |
|
263 return; |
|
264 |
|
265 /* We're doing a full decode. */ |
|
266 if (mCMSMode != eCMSMode_Off && |
|
267 (mInProfile = GetICCProfile(mInfo)) != nullptr) { |
|
268 uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); |
|
269 bool mismatch = false; |
|
270 |
|
271 #ifdef DEBUG_tor |
|
272 fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace); |
|
273 #endif |
|
274 switch (mInfo.jpeg_color_space) { |
|
275 case JCS_GRAYSCALE: |
|
276 if (profileSpace == icSigRgbData) |
|
277 mInfo.out_color_space = JCS_RGB; |
|
278 else if (profileSpace != icSigGrayData) |
|
279 mismatch = true; |
|
280 break; |
|
281 case JCS_RGB: |
|
282 if (profileSpace != icSigRgbData) |
|
283 mismatch = true; |
|
284 break; |
|
285 case JCS_YCbCr: |
|
286 if (profileSpace == icSigRgbData) |
|
287 mInfo.out_color_space = JCS_RGB; |
|
288 else |
|
289 // qcms doesn't support ycbcr |
|
290 mismatch = true; |
|
291 break; |
|
292 case JCS_CMYK: |
|
293 case JCS_YCCK: |
|
294 // qcms doesn't support cmyk |
|
295 mismatch = true; |
|
296 break; |
|
297 default: |
|
298 mState = JPEG_ERROR; |
|
299 PostDataError(); |
|
300 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
301 ("} (unknown colorpsace (1))")); |
|
302 return; |
|
303 } |
|
304 |
|
305 if (!mismatch) { |
|
306 qcms_data_type type; |
|
307 switch (mInfo.out_color_space) { |
|
308 case JCS_GRAYSCALE: |
|
309 type = QCMS_DATA_GRAY_8; |
|
310 break; |
|
311 case JCS_RGB: |
|
312 type = QCMS_DATA_RGB_8; |
|
313 break; |
|
314 default: |
|
315 mState = JPEG_ERROR; |
|
316 PostDataError(); |
|
317 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
318 ("} (unknown colorpsace (2))")); |
|
319 return; |
|
320 } |
|
321 #if 0 |
|
322 /* We don't currently support CMYK profiles. The following |
|
323 * code dealt with lcms types. Add something like this |
|
324 * back when we gain support for CMYK. |
|
325 */ |
|
326 /* Adobe Photoshop writes YCCK/CMYK files with inverted data */ |
|
327 if (mInfo.out_color_space == JCS_CMYK) |
|
328 type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); |
|
329 #endif |
|
330 |
|
331 if (gfxPlatform::GetCMSOutputProfile()) { |
|
332 |
|
333 /* Calculate rendering intent. */ |
|
334 int intent = gfxPlatform::GetRenderingIntent(); |
|
335 if (intent == -1) |
|
336 intent = qcms_profile_get_rendering_intent(mInProfile); |
|
337 |
|
338 /* Create the color management transform. */ |
|
339 mTransform = qcms_transform_create(mInProfile, |
|
340 type, |
|
341 gfxPlatform::GetCMSOutputProfile(), |
|
342 QCMS_DATA_RGB_8, |
|
343 (qcms_intent)intent); |
|
344 } |
|
345 } else { |
|
346 #ifdef DEBUG_tor |
|
347 fprintf(stderr, "ICM profile colorspace mismatch\n"); |
|
348 #endif |
|
349 } |
|
350 } |
|
351 |
|
352 if (!mTransform) { |
|
353 switch (mInfo.jpeg_color_space) { |
|
354 case JCS_GRAYSCALE: |
|
355 case JCS_RGB: |
|
356 case JCS_YCbCr: |
|
357 // if we're not color managing we can decode directly to |
|
358 // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB |
|
359 if (mCMSMode != eCMSMode_All) { |
|
360 mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB; |
|
361 mInfo.out_color_components = 4; |
|
362 } else { |
|
363 mInfo.out_color_space = JCS_RGB; |
|
364 } |
|
365 break; |
|
366 case JCS_CMYK: |
|
367 case JCS_YCCK: |
|
368 /* libjpeg can convert from YCCK to CMYK, but not to RGB */ |
|
369 mInfo.out_color_space = JCS_CMYK; |
|
370 break; |
|
371 default: |
|
372 mState = JPEG_ERROR; |
|
373 PostDataError(); |
|
374 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
375 ("} (unknown colorpsace (3))")); |
|
376 return; |
|
377 break; |
|
378 } |
|
379 } |
|
380 |
|
381 /* |
|
382 * Don't allocate a giant and superfluous memory buffer |
|
383 * when not doing a progressive decode. |
|
384 */ |
|
385 mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo); |
|
386 |
|
387 if (!mImageData) { |
|
388 mState = JPEG_ERROR; |
|
389 PostDecoderError(NS_ERROR_OUT_OF_MEMORY); |
|
390 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
391 ("} (could not initialize image frame)")); |
|
392 return; |
|
393 } |
|
394 |
|
395 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
396 (" JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels", |
|
397 mInfo.output_width, mInfo.output_height)); |
|
398 |
|
399 mState = JPEG_START_DECOMPRESS; |
|
400 } |
|
401 |
|
402 case JPEG_START_DECOMPRESS: |
|
403 { |
|
404 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case"); |
|
405 /* Step 4: set parameters for decompression */ |
|
406 |
|
407 /* FIXME -- Should reset dct_method and dither mode |
|
408 * for final pass of progressive JPEG |
|
409 */ |
|
410 mInfo.dct_method = JDCT_ISLOW; |
|
411 mInfo.dither_mode = JDITHER_FS; |
|
412 mInfo.do_fancy_upsampling = TRUE; |
|
413 mInfo.enable_2pass_quant = FALSE; |
|
414 mInfo.do_block_smoothing = TRUE; |
|
415 |
|
416 /* Step 5: Start decompressor */ |
|
417 if (jpeg_start_decompress(&mInfo) == FALSE) { |
|
418 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
419 ("} (I/O suspension after jpeg_start_decompress())")); |
|
420 return; /* I/O suspension */ |
|
421 } |
|
422 |
|
423 |
|
424 /* If this is a progressive JPEG ... */ |
|
425 mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; |
|
426 } |
|
427 |
|
428 case JPEG_DECOMPRESS_SEQUENTIAL: |
|
429 { |
|
430 if (mState == JPEG_DECOMPRESS_SEQUENTIAL) |
|
431 { |
|
432 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case"); |
|
433 |
|
434 bool suspend; |
|
435 OutputScanlines(&suspend); |
|
436 |
|
437 if (suspend) { |
|
438 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
439 ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)")); |
|
440 return; /* I/O suspension */ |
|
441 } |
|
442 |
|
443 /* If we've completed image output ... */ |
|
444 NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!"); |
|
445 mState = JPEG_DONE; |
|
446 } |
|
447 } |
|
448 |
|
449 case JPEG_DECOMPRESS_PROGRESSIVE: |
|
450 { |
|
451 if (mState == JPEG_DECOMPRESS_PROGRESSIVE) |
|
452 { |
|
453 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case"); |
|
454 |
|
455 int status; |
|
456 do { |
|
457 status = jpeg_consume_input(&mInfo); |
|
458 } while ((status != JPEG_SUSPENDED) && |
|
459 (status != JPEG_REACHED_EOI)); |
|
460 |
|
461 for (;;) { |
|
462 if (mInfo.output_scanline == 0) { |
|
463 int scan = mInfo.input_scan_number; |
|
464 |
|
465 /* if we haven't displayed anything yet (output_scan_number==0) |
|
466 and we have enough data for a complete scan, force output |
|
467 of the last full scan */ |
|
468 if ((mInfo.output_scan_number == 0) && |
|
469 (scan > 1) && |
|
470 (status != JPEG_REACHED_EOI)) |
|
471 scan--; |
|
472 |
|
473 if (!jpeg_start_output(&mInfo, scan)) { |
|
474 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
475 ("} (I/O suspension after jpeg_start_output() - PROGRESSIVE)")); |
|
476 return; /* I/O suspension */ |
|
477 } |
|
478 } |
|
479 |
|
480 if (mInfo.output_scanline == 0xffffff) |
|
481 mInfo.output_scanline = 0; |
|
482 |
|
483 bool suspend; |
|
484 OutputScanlines(&suspend); |
|
485 |
|
486 if (suspend) { |
|
487 if (mInfo.output_scanline == 0) { |
|
488 /* didn't manage to read any lines - flag so we don't call |
|
489 jpeg_start_output() multiple times for the same scan */ |
|
490 mInfo.output_scanline = 0xffffff; |
|
491 } |
|
492 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
493 ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)")); |
|
494 return; /* I/O suspension */ |
|
495 } |
|
496 |
|
497 if (mInfo.output_scanline == mInfo.output_height) |
|
498 { |
|
499 if (!jpeg_finish_output(&mInfo)) { |
|
500 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
501 ("} (I/O suspension after jpeg_finish_output() - PROGRESSIVE)")); |
|
502 return; /* I/O suspension */ |
|
503 } |
|
504 |
|
505 if (jpeg_input_complete(&mInfo) && |
|
506 (mInfo.input_scan_number == mInfo.output_scan_number)) |
|
507 break; |
|
508 |
|
509 mInfo.output_scanline = 0; |
|
510 } |
|
511 } |
|
512 |
|
513 mState = JPEG_DONE; |
|
514 } |
|
515 } |
|
516 |
|
517 case JPEG_DONE: |
|
518 { |
|
519 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case"); |
|
520 |
|
521 /* Step 7: Finish decompression */ |
|
522 |
|
523 if (jpeg_finish_decompress(&mInfo) == FALSE) { |
|
524 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
525 ("} (I/O suspension after jpeg_finish_decompress() - DONE)")); |
|
526 return; /* I/O suspension */ |
|
527 } |
|
528 |
|
529 mState = JPEG_SINK_NON_JPEG_TRAILER; |
|
530 |
|
531 /* we're done dude */ |
|
532 break; |
|
533 } |
|
534 case JPEG_SINK_NON_JPEG_TRAILER: |
|
535 PR_LOG(GetJPEGLog(), PR_LOG_DEBUG, |
|
536 ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this)); |
|
537 |
|
538 break; |
|
539 |
|
540 case JPEG_ERROR: |
|
541 NS_ABORT_IF_FALSE(0, "Should always return immediately after error and not re-enter decoder"); |
|
542 } |
|
543 |
|
544 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, |
|
545 ("} (end of function)")); |
|
546 return; |
|
547 } |
|
548 |
|
549 Orientation |
|
550 nsJPEGDecoder::ReadOrientationFromEXIF() |
|
551 { |
|
552 jpeg_saved_marker_ptr marker; |
|
553 |
|
554 // Locate the APP1 marker, where EXIF data is stored, in the marker list. |
|
555 for (marker = mInfo.marker_list ; marker != nullptr ; marker = marker->next) { |
|
556 if (marker->marker == JPEG_APP0 + 1) |
|
557 break; |
|
558 } |
|
559 |
|
560 // If we're at the end of the list, there's no EXIF data. |
|
561 if (!marker) |
|
562 return Orientation(); |
|
563 |
|
564 // Extract the orientation information. |
|
565 EXIFData exif = EXIFParser::Parse(marker->data, |
|
566 static_cast<uint32_t>(marker->data_length)); |
|
567 return exif.orientation; |
|
568 } |
|
569 |
|
570 void |
|
571 nsJPEGDecoder::NotifyDone() |
|
572 { |
|
573 PostFrameStop(FrameBlender::kFrameOpaque); |
|
574 PostDecodeDone(); |
|
575 } |
|
576 |
|
577 void |
|
578 nsJPEGDecoder::OutputScanlines(bool* suspend) |
|
579 { |
|
580 *suspend = false; |
|
581 |
|
582 const uint32_t top = mInfo.output_scanline; |
|
583 |
|
584 while ((mInfo.output_scanline < mInfo.output_height)) { |
|
585 /* Use the Cairo image buffer as scanline buffer */ |
|
586 uint32_t *imageRow = ((uint32_t*)mImageData) + |
|
587 (mInfo.output_scanline * mInfo.output_width); |
|
588 |
|
589 if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) { |
|
590 /* Special case: scanline will be directly converted into packed ARGB */ |
|
591 if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) { |
|
592 *suspend = true; /* suspend */ |
|
593 break; |
|
594 } |
|
595 continue; /* all done for this row! */ |
|
596 } |
|
597 |
|
598 JSAMPROW sampleRow = (JSAMPROW)imageRow; |
|
599 if (mInfo.output_components == 3) { |
|
600 /* Put the pixels at end of row to enable in-place expansion */ |
|
601 sampleRow += mInfo.output_width; |
|
602 } |
|
603 |
|
604 /* Request one scanline. Returns 0 or 1 scanlines. */ |
|
605 if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) { |
|
606 *suspend = true; /* suspend */ |
|
607 break; |
|
608 } |
|
609 |
|
610 if (mTransform) { |
|
611 JSAMPROW source = sampleRow; |
|
612 if (mInfo.out_color_space == JCS_GRAYSCALE) { |
|
613 /* Convert from the 1byte grey pixels at begin of row |
|
614 to the 3byte RGB byte pixels at 'end' of row */ |
|
615 sampleRow += mInfo.output_width; |
|
616 } |
|
617 qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width); |
|
618 /* Move 3byte RGB data to end of row */ |
|
619 if (mInfo.out_color_space == JCS_CMYK) { |
|
620 memmove(sampleRow + mInfo.output_width, |
|
621 sampleRow, |
|
622 3 * mInfo.output_width); |
|
623 sampleRow += mInfo.output_width; |
|
624 } |
|
625 } else { |
|
626 if (mInfo.out_color_space == JCS_CMYK) { |
|
627 /* Convert from CMYK to RGB */ |
|
628 /* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */ |
|
629 /* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */ |
|
630 cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width); |
|
631 sampleRow += mInfo.output_width; |
|
632 } |
|
633 if (mCMSMode == eCMSMode_All) { |
|
634 /* No embedded ICC profile - treat as sRGB */ |
|
635 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); |
|
636 if (transform) { |
|
637 qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width); |
|
638 } |
|
639 } |
|
640 } |
|
641 |
|
642 // counter for while() loops below |
|
643 uint32_t idx = mInfo.output_width; |
|
644 |
|
645 // copy as bytes until source pointer is 32-bit-aligned |
|
646 for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) { |
|
647 *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]); |
|
648 sampleRow += 3; |
|
649 } |
|
650 |
|
651 // copy pixels in blocks of 4 |
|
652 while (idx >= 4) { |
|
653 GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow); |
|
654 idx -= 4; |
|
655 sampleRow += 12; |
|
656 imageRow += 4; |
|
657 } |
|
658 |
|
659 // copy remaining pixel(s) |
|
660 while (idx--) { |
|
661 // 32-bit read of final pixel will exceed buffer, so read bytes |
|
662 *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]); |
|
663 sampleRow += 3; |
|
664 } |
|
665 } |
|
666 |
|
667 if (top != mInfo.output_scanline) { |
|
668 nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top); |
|
669 PostInvalidation(r); |
|
670 } |
|
671 |
|
672 } |
|
673 |
|
674 |
|
675 /* Override the standard error method in the IJG JPEG decoder code. */ |
|
676 METHODDEF(void) |
|
677 my_error_exit (j_common_ptr cinfo) |
|
678 { |
|
679 decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err; |
|
680 |
|
681 /* Convert error to a browser error code */ |
|
682 nsresult error_code = err->pub.msg_code == JERR_OUT_OF_MEMORY |
|
683 ? NS_ERROR_OUT_OF_MEMORY |
|
684 : NS_ERROR_FAILURE; |
|
685 |
|
686 #ifdef DEBUG |
|
687 char buffer[JMSG_LENGTH_MAX]; |
|
688 |
|
689 /* Create the message */ |
|
690 (*err->pub.format_message) (cinfo, buffer); |
|
691 |
|
692 fprintf(stderr, "JPEG decoding error:\n%s\n", buffer); |
|
693 #endif |
|
694 |
|
695 /* Return control to the setjmp point. We pass an nsresult masquerading as |
|
696 * an int, which works because the setjmp() caller casts it back. */ |
|
697 longjmp(err->setjmp_buffer, static_cast<int>(error_code)); |
|
698 } |
|
699 |
|
700 /******************************************************************************/ |
|
701 /*----------------------------------------------------------------------------- |
|
702 * This is the callback routine from the IJG JPEG library used to supply new |
|
703 * data to the decompressor when its input buffer is exhausted. It juggles |
|
704 * multiple buffers in an attempt to avoid unnecessary copying of input data. |
|
705 * |
|
706 * (A simpler scheme is possible: It's much easier to use only a single |
|
707 * buffer; when fill_input_buffer() is called, move any unconsumed data |
|
708 * (beyond the current pointer/count) down to the beginning of this buffer and |
|
709 * then load new data into the remaining buffer space. This approach requires |
|
710 * a little more data copying but is far easier to get right.) |
|
711 * |
|
712 * At any one time, the JPEG decompressor is either reading from the necko |
|
713 * input buffer, which is volatile across top-level calls to the IJG library, |
|
714 * or the "backtrack" buffer. The backtrack buffer contains the remaining |
|
715 * unconsumed data from the necko buffer after parsing was suspended due |
|
716 * to insufficient data in some previous call to the IJG library. |
|
717 * |
|
718 * When suspending, the decompressor will back up to a convenient restart |
|
719 * point (typically the start of the current MCU). The variables |
|
720 * next_input_byte & bytes_in_buffer indicate where the restart point will be |
|
721 * if the current call returns FALSE. Data beyond this point must be |
|
722 * rescanned after resumption, so it must be preserved in case the decompressor |
|
723 * decides to backtrack. |
|
724 * |
|
725 * Returns: |
|
726 * TRUE if additional data is available, FALSE if no data present and |
|
727 * the JPEG library should therefore suspend processing of input stream |
|
728 *---------------------------------------------------------------------------*/ |
|
729 |
|
730 /******************************************************************************/ |
|
731 /* data source manager method */ |
|
732 /******************************************************************************/ |
|
733 |
|
734 |
|
735 /******************************************************************************/ |
|
736 /* data source manager method |
|
737 Initialize source. This is called by jpeg_read_header() before any |
|
738 data is actually read. May leave |
|
739 bytes_in_buffer set to 0 (in which case a fill_input_buffer() call |
|
740 will occur immediately). |
|
741 */ |
|
742 METHODDEF(void) |
|
743 init_source (j_decompress_ptr jd) |
|
744 { |
|
745 } |
|
746 |
|
747 /******************************************************************************/ |
|
748 /* data source manager method |
|
749 Skip num_bytes worth of data. The buffer pointer and count should |
|
750 be advanced over num_bytes input bytes, refilling the buffer as |
|
751 needed. This is used to skip over a potentially large amount of |
|
752 uninteresting data (such as an APPn marker). In some applications |
|
753 it may be possible to optimize away the reading of the skipped data, |
|
754 but it's not clear that being smart is worth much trouble; large |
|
755 skips are uncommon. bytes_in_buffer may be zero on return. |
|
756 A zero or negative skip count should be treated as a no-op. |
|
757 */ |
|
758 METHODDEF(void) |
|
759 skip_input_data (j_decompress_ptr jd, long num_bytes) |
|
760 { |
|
761 struct jpeg_source_mgr *src = jd->src; |
|
762 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data); |
|
763 |
|
764 if (num_bytes > (long)src->bytes_in_buffer) { |
|
765 /* |
|
766 * Can't skip it all right now until we get more data from |
|
767 * network stream. Set things up so that fill_input_buffer |
|
768 * will skip remaining amount. |
|
769 */ |
|
770 decoder->mBytesToSkip = (size_t)num_bytes - src->bytes_in_buffer; |
|
771 src->next_input_byte += src->bytes_in_buffer; |
|
772 src->bytes_in_buffer = 0; |
|
773 |
|
774 } else { |
|
775 /* Simple case. Just advance buffer pointer */ |
|
776 |
|
777 src->bytes_in_buffer -= (size_t)num_bytes; |
|
778 src->next_input_byte += num_bytes; |
|
779 } |
|
780 } |
|
781 |
|
782 |
|
783 /******************************************************************************/ |
|
784 /* data source manager method |
|
785 This is called whenever bytes_in_buffer has reached zero and more |
|
786 data is wanted. In typical applications, it should read fresh data |
|
787 into the buffer (ignoring the current state of next_input_byte and |
|
788 bytes_in_buffer), reset the pointer & count to the start of the |
|
789 buffer, and return TRUE indicating that the buffer has been reloaded. |
|
790 It is not necessary to fill the buffer entirely, only to obtain at |
|
791 least one more byte. bytes_in_buffer MUST be set to a positive value |
|
792 if TRUE is returned. A FALSE return should only be used when I/O |
|
793 suspension is desired. |
|
794 */ |
|
795 METHODDEF(boolean) |
|
796 fill_input_buffer (j_decompress_ptr jd) |
|
797 { |
|
798 struct jpeg_source_mgr *src = jd->src; |
|
799 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data); |
|
800 |
|
801 if (decoder->mReading) { |
|
802 const JOCTET *new_buffer = decoder->mSegment; |
|
803 uint32_t new_buflen = decoder->mSegmentLen; |
|
804 |
|
805 if (!new_buffer || new_buflen == 0) |
|
806 return false; /* suspend */ |
|
807 |
|
808 decoder->mSegmentLen = 0; |
|
809 |
|
810 if (decoder->mBytesToSkip) { |
|
811 if (decoder->mBytesToSkip < new_buflen) { |
|
812 /* All done skipping bytes; Return what's left. */ |
|
813 new_buffer += decoder->mBytesToSkip; |
|
814 new_buflen -= decoder->mBytesToSkip; |
|
815 decoder->mBytesToSkip = 0; |
|
816 } else { |
|
817 /* Still need to skip some more data in the future */ |
|
818 decoder->mBytesToSkip -= (size_t)new_buflen; |
|
819 return false; /* suspend */ |
|
820 } |
|
821 } |
|
822 |
|
823 decoder->mBackBufferUnreadLen = src->bytes_in_buffer; |
|
824 |
|
825 src->next_input_byte = new_buffer; |
|
826 src->bytes_in_buffer = (size_t)new_buflen; |
|
827 decoder->mReading = false; |
|
828 |
|
829 return true; |
|
830 } |
|
831 |
|
832 if (src->next_input_byte != decoder->mSegment) { |
|
833 /* Backtrack data has been permanently consumed. */ |
|
834 decoder->mBackBufferUnreadLen = 0; |
|
835 decoder->mBackBufferLen = 0; |
|
836 } |
|
837 |
|
838 /* Save remainder of netlib buffer in backtrack buffer */ |
|
839 const uint32_t new_backtrack_buflen = src->bytes_in_buffer + decoder->mBackBufferLen; |
|
840 |
|
841 /* Make sure backtrack buffer is big enough to hold new data. */ |
|
842 if (decoder->mBackBufferSize < new_backtrack_buflen) { |
|
843 /* Check for malformed MARKER segment lengths, before allocating space for it */ |
|
844 if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) { |
|
845 my_error_exit((j_common_ptr)(&decoder->mInfo)); |
|
846 } |
|
847 |
|
848 /* Round up to multiple of 256 bytes. */ |
|
849 const size_t roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8; |
|
850 JOCTET *buf = (JOCTET *)PR_REALLOC(decoder->mBackBuffer, roundup_buflen); |
|
851 /* Check for OOM */ |
|
852 if (!buf) { |
|
853 decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY; |
|
854 my_error_exit((j_common_ptr)(&decoder->mInfo)); |
|
855 } |
|
856 decoder->mBackBuffer = buf; |
|
857 decoder->mBackBufferSize = roundup_buflen; |
|
858 } |
|
859 |
|
860 /* Copy remainder of netlib segment into backtrack buffer. */ |
|
861 memmove(decoder->mBackBuffer + decoder->mBackBufferLen, |
|
862 src->next_input_byte, |
|
863 src->bytes_in_buffer); |
|
864 |
|
865 /* Point to start of data to be rescanned. */ |
|
866 src->next_input_byte = decoder->mBackBuffer + decoder->mBackBufferLen - decoder->mBackBufferUnreadLen; |
|
867 src->bytes_in_buffer += decoder->mBackBufferUnreadLen; |
|
868 decoder->mBackBufferLen = (size_t)new_backtrack_buflen; |
|
869 decoder->mReading = true; |
|
870 |
|
871 return false; |
|
872 } |
|
873 |
|
874 /******************************************************************************/ |
|
875 /* data source manager method */ |
|
876 /* |
|
877 * Terminate source --- called by jpeg_finish_decompress() after all |
|
878 * data has been read to clean up JPEG source manager. NOT called by |
|
879 * jpeg_abort() or jpeg_destroy(). |
|
880 */ |
|
881 METHODDEF(void) |
|
882 term_source (j_decompress_ptr jd) |
|
883 { |
|
884 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data); |
|
885 |
|
886 // This function shouldn't be called if we ran into an error we didn't |
|
887 // recover from. |
|
888 NS_ABORT_IF_FALSE(decoder->mState != JPEG_ERROR, |
|
889 "Calling term_source on a JPEG with mState == JPEG_ERROR!"); |
|
890 |
|
891 // Notify using a helper method to get around protectedness issues. |
|
892 decoder->NotifyDone(); |
|
893 } |
|
894 |
|
895 } // namespace image |
|
896 } // namespace mozilla |
|
897 |
|
898 |
|
899 /**************** Inverted CMYK -> RGB conversion **************/ |
|
900 /* |
|
901 * Input is (Inverted) CMYK stored as 4 bytes per pixel. |
|
902 * Output is RGB stored as 3 bytes per pixel. |
|
903 * @param row Points to row buffer containing the CMYK bytes for each pixel in the row. |
|
904 * @param width Number of pixels in the row. |
|
905 */ |
|
906 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) |
|
907 { |
|
908 /* Work from end to front to shrink from 4 bytes per pixel to 3 */ |
|
909 JSAMPROW in = row + width*4; |
|
910 JSAMPROW out = in; |
|
911 |
|
912 for (uint32_t i = width; i > 0; i--) { |
|
913 in -= 4; |
|
914 out -= 3; |
|
915 |
|
916 // Source is 'Inverted CMYK', output is RGB. |
|
917 // See: http://www.easyrgb.com/math.php?MATH=M12#text12 |
|
918 // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb |
|
919 |
|
920 // From CMYK to CMY |
|
921 // C = ( C * ( 1 - K ) + K ) |
|
922 // M = ( M * ( 1 - K ) + K ) |
|
923 // Y = ( Y * ( 1 - K ) + K ) |
|
924 |
|
925 // From Inverted CMYK to CMY is thus: |
|
926 // C = ( (1-iC) * (1 - (1-iK)) + (1-iK) ) => 1 - iC*iK |
|
927 // Same for M and Y |
|
928 |
|
929 // Convert from CMY (0..1) to RGB (0..1) |
|
930 // R = 1 - C => 1 - (1 - iC*iK) => iC*iK |
|
931 // G = 1 - M => 1 - (1 - iM*iK) => iM*iK |
|
932 // B = 1 - Y => 1 - (1 - iY*iK) => iY*iK |
|
933 |
|
934 // Convert from Inverted CMYK (0..255) to RGB (0..255) |
|
935 const uint32_t iC = in[0]; |
|
936 const uint32_t iM = in[1]; |
|
937 const uint32_t iY = in[2]; |
|
938 const uint32_t iK = in[3]; |
|
939 out[0] = iC*iK/255; // Red |
|
940 out[1] = iM*iK/255; // Green |
|
941 out[2] = iY*iK/255; // Blue |
|
942 } |
|
943 } |