bug 772897 - implement addon manager ui for click-to-play plugins r=unfocused
[gecko.git] / image / decoders / nsJPEGDecoder.cpp
blob8fa8200f2171bec1ae972b7a092751325896856d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #include "nsJPEGDecoder.h"
8 #include "ImageLogging.h"
10 #include "imgIContainerObserver.h"
12 #include "nsIInputStream.h"
14 #include "nspr.h"
15 #include "nsCRT.h"
16 #include "gfxColor.h"
18 #include "jerror.h"
20 #include "gfxPlatform.h"
22 extern "C" {
23 #include "iccjpeg.h"
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
32 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width);
34 namespace mozilla {
35 namespace image {
37 #if defined(PR_LOGGING)
38 PRLogModuleInfo *gJPEGlog = PR_NewLogModule("JPEGDecoder");
39 static PRLogModuleInfo *gJPEGDecoderAccountingLog = PR_NewLogModule("JPEGDecoderAccounting");
40 #else
41 #define gJPEGlog
42 #define gJPEGDecoderAccountingLog
43 #endif
45 static qcms_profile*
46 GetICCProfile(struct jpeg_decompress_struct &info)
48 JOCTET* profilebuf;
49 uint32_t profileLength;
50 qcms_profile* profile = nullptr;
52 if (read_icc_profile(&info, &profilebuf, &profileLength)) {
53 profile = qcms_profile_from_memory(profilebuf, profileLength);
54 free(profilebuf);
57 return profile;
60 METHODDEF(void) init_source (j_decompress_ptr jd);
61 METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd);
62 METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes);
63 METHODDEF(void) term_source (j_decompress_ptr jd);
64 METHODDEF(void) my_error_exit (j_common_ptr cinfo);
66 /* Normal JFIF markers can't have more bytes than this. */
67 #define MAX_JPEG_MARKER_LENGTH (((uint32_t)1 << 16) - 1)
70 nsJPEGDecoder::nsJPEGDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
71 : Decoder(aImage, aObserver)
73 mState = JPEG_HEADER;
74 mReading = true;
75 mImageData = nullptr;
77 mBytesToSkip = 0;
78 memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
79 memset(&mSourceMgr, 0, sizeof(mSourceMgr));
80 mInfo.client_data = (void*)this;
82 mSegment = nullptr;
83 mSegmentLen = 0;
85 mBackBuffer = nullptr;
86 mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
88 mInProfile = nullptr;
89 mTransform = nullptr;
91 mCMSMode = 0;
93 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
94 ("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p",
95 this));
98 nsJPEGDecoder::~nsJPEGDecoder()
100 // Step 8: Release JPEG decompression object
101 mInfo.src = nullptr;
102 jpeg_destroy_decompress(&mInfo);
104 PR_FREEIF(mBackBuffer);
105 if (mTransform)
106 qcms_transform_release(mTransform);
107 if (mInProfile)
108 qcms_profile_release(mInProfile);
110 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
111 ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
112 this));
115 Telemetry::ID
116 nsJPEGDecoder::SpeedHistogram()
118 return Telemetry::IMAGE_DECODE_SPEED_JPEG;
121 void
122 nsJPEGDecoder::InitInternal()
124 mCMSMode = gfxPlatform::GetCMSMode();
125 if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0)
126 mCMSMode = eCMSMode_Off;
128 /* We set up the normal JPEG error routines, then override error_exit. */
129 mInfo.err = jpeg_std_error(&mErr.pub);
130 /* mInfo.err = jpeg_std_error(&mErr.pub); */
131 mErr.pub.error_exit = my_error_exit;
132 /* Establish the setjmp return context for my_error_exit to use. */
133 if (setjmp(mErr.setjmp_buffer)) {
134 /* If we get here, the JPEG code has signaled an error.
135 * We need to clean up the JPEG object, close the input file, and return.
137 PostDecoderError(NS_ERROR_FAILURE);
138 return;
141 /* Step 1: allocate and initialize JPEG decompression object */
142 jpeg_create_decompress(&mInfo);
143 /* Set the source manager */
144 mInfo.src = &mSourceMgr;
146 /* Step 2: specify data source (eg, a file) */
148 /* Setup callback functions. */
149 mSourceMgr.init_source = init_source;
150 mSourceMgr.fill_input_buffer = fill_input_buffer;
151 mSourceMgr.skip_input_data = skip_input_data;
152 mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
153 mSourceMgr.term_source = term_source;
155 /* Record app markers for ICC data */
156 for (uint32_t m = 0; m < 16; m++)
157 jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
160 void
161 nsJPEGDecoder::FinishInternal()
163 /* If we're not in any sort of error case, flush the decoder.
165 * XXXbholley - It seems wrong that this should be necessary, but at the
166 * moment I'm just folding the contents of Flush() into Close() so that
167 * we can get rid of it.
169 if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) &&
170 (mState != JPEG_ERROR) &&
171 !IsSizeDecode())
172 this->Write(nullptr, 0);
175 void
176 nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount)
178 mSegment = (const JOCTET *)aBuffer;
179 mSegmentLen = aCount;
181 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
183 /* Return here if there is a fatal error within libjpeg. */
184 nsresult error_code;
185 // This cast to nsresult makes sense because setjmp() returns whatever we
186 // passed to longjmp(), which was actually an nsresult.
187 if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) {
188 if (error_code == NS_ERROR_FAILURE) {
189 PostDataError();
190 /* Error due to corrupt stream - return NS_OK and consume silently
191 so that libpr0n doesn't throw away a partial image load */
192 mState = JPEG_SINK_NON_JPEG_TRAILER;
193 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
194 ("} (setjmp returned NS_ERROR_FAILURE)"));
195 return;
196 } else {
197 /* Error due to reasons external to the stream (probably out of
198 memory) - let libpr0n attempt to clean up, even though
199 mozilla is seconds away from falling flat on its face. */
200 PostDecoderError(error_code);
201 mState = JPEG_ERROR;
202 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
203 ("} (setjmp returned an error)"));
204 return;
208 PR_LOG(gJPEGlog, PR_LOG_DEBUG,
209 ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this));
211 switch (mState) {
212 case JPEG_HEADER:
214 LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- entering JPEG_HEADER case");
216 /* Step 3: read file parameters with jpeg_read_header() */
217 if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
218 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
219 ("} (JPEG_SUSPENDED)"));
220 return; /* I/O suspension */
223 // Post our size to the superclass
224 PostSize(mInfo.image_width, mInfo.image_height);
225 if (HasError()) {
226 // Setting the size led to an error.
227 mState = JPEG_ERROR;
228 return;
231 /* If we're doing a size decode, we're done. */
232 if (IsSizeDecode())
233 return;
235 /* We're doing a full decode. */
236 if (mCMSMode != eCMSMode_Off &&
237 (mInProfile = GetICCProfile(mInfo)) != nullptr) {
238 uint32_t profileSpace = qcms_profile_get_color_space(mInProfile);
239 bool mismatch = false;
241 #ifdef DEBUG_tor
242 fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace);
243 #endif
244 switch (mInfo.jpeg_color_space) {
245 case JCS_GRAYSCALE:
246 if (profileSpace == icSigRgbData)
247 mInfo.out_color_space = JCS_RGB;
248 else if (profileSpace != icSigGrayData)
249 mismatch = true;
250 break;
251 case JCS_RGB:
252 if (profileSpace != icSigRgbData)
253 mismatch = true;
254 break;
255 case JCS_YCbCr:
256 if (profileSpace == icSigRgbData)
257 mInfo.out_color_space = JCS_RGB;
258 else
259 // qcms doesn't support ycbcr
260 mismatch = true;
261 break;
262 case JCS_CMYK:
263 case JCS_YCCK:
264 // qcms doesn't support cmyk
265 mismatch = true;
266 break;
267 default:
268 mState = JPEG_ERROR;
269 PostDataError();
270 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
271 ("} (unknown colorpsace (1))"));
272 return;
275 if (!mismatch) {
276 qcms_data_type type;
277 switch (mInfo.out_color_space) {
278 case JCS_GRAYSCALE:
279 type = QCMS_DATA_GRAY_8;
280 break;
281 case JCS_RGB:
282 type = QCMS_DATA_RGB_8;
283 break;
284 default:
285 mState = JPEG_ERROR;
286 PostDataError();
287 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
288 ("} (unknown colorpsace (2))"));
289 return;
291 #if 0
292 /* We don't currently support CMYK profiles. The following
293 * code dealt with lcms types. Add something like this
294 * back when we gain support for CMYK.
296 /* Adobe Photoshop writes YCCK/CMYK files with inverted data */
297 if (mInfo.out_color_space == JCS_CMYK)
298 type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0);
299 #endif
301 if (gfxPlatform::GetCMSOutputProfile()) {
303 /* Calculate rendering intent. */
304 int intent = gfxPlatform::GetRenderingIntent();
305 if (intent == -1)
306 intent = qcms_profile_get_rendering_intent(mInProfile);
308 /* Create the color management transform. */
309 mTransform = qcms_transform_create(mInProfile,
310 type,
311 gfxPlatform::GetCMSOutputProfile(),
312 QCMS_DATA_RGB_8,
313 (qcms_intent)intent);
315 } else {
316 #ifdef DEBUG_tor
317 fprintf(stderr, "ICM profile colorspace mismatch\n");
318 #endif
322 if (!mTransform) {
323 switch (mInfo.jpeg_color_space) {
324 case JCS_GRAYSCALE:
325 case JCS_RGB:
326 case JCS_YCbCr:
327 // if we're not color managing we can decode directly to
328 // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB
329 if (mCMSMode != eCMSMode_All) {
330 mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB;
331 mInfo.out_color_components = 4;
332 } else {
333 mInfo.out_color_space = JCS_RGB;
335 break;
336 case JCS_CMYK:
337 case JCS_YCCK:
338 /* libjpeg can convert from YCCK to CMYK, but not to RGB */
339 mInfo.out_color_space = JCS_CMYK;
340 break;
341 default:
342 mState = JPEG_ERROR;
343 PostDataError();
344 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
345 ("} (unknown colorpsace (3))"));
346 return;
347 break;
352 * Don't allocate a giant and superfluous memory buffer
353 * when the image is a sequential JPEG.
355 mInfo.buffered_image = jpeg_has_multiple_scans(&mInfo);
357 /* Used to set up image size so arrays can be allocated */
358 jpeg_calc_output_dimensions(&mInfo);
360 uint32_t imagelength;
361 if (NS_FAILED(mImage.EnsureFrame(0, 0, 0, mInfo.image_width, mInfo.image_height,
362 gfxASurface::ImageFormatRGB24,
363 &mImageData, &imagelength))) {
364 mState = JPEG_ERROR;
365 PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
366 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
367 ("} (could not initialize image frame)"));
368 return;
371 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
372 (" JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels",
373 mInfo.image_width, mInfo.image_height));
375 // Tell the superclass we're starting a frame
376 PostFrameStart();
378 mState = JPEG_START_DECOMPRESS;
381 case JPEG_START_DECOMPRESS:
383 LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case");
384 /* Step 4: set parameters for decompression */
386 /* FIXME -- Should reset dct_method and dither mode
387 * for final pass of progressive JPEG
389 mInfo.dct_method = JDCT_ISLOW;
390 mInfo.dither_mode = JDITHER_FS;
391 mInfo.do_fancy_upsampling = TRUE;
392 mInfo.enable_2pass_quant = FALSE;
393 mInfo.do_block_smoothing = TRUE;
395 /* Step 5: Start decompressor */
396 if (jpeg_start_decompress(&mInfo) == FALSE) {
397 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
398 ("} (I/O suspension after jpeg_start_decompress())"));
399 return; /* I/O suspension */
403 /* If this is a progressive JPEG ... */
404 mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
407 case JPEG_DECOMPRESS_SEQUENTIAL:
409 if (mState == JPEG_DECOMPRESS_SEQUENTIAL)
411 LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case");
413 bool suspend;
414 OutputScanlines(&suspend);
416 if (suspend) {
417 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
418 ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
419 return; /* I/O suspension */
422 /* If we've completed image output ... */
423 NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!");
424 mState = JPEG_DONE;
428 case JPEG_DECOMPRESS_PROGRESSIVE:
430 if (mState == JPEG_DECOMPRESS_PROGRESSIVE)
432 LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case");
434 int status;
435 do {
436 status = jpeg_consume_input(&mInfo);
437 } while ((status != JPEG_SUSPENDED) &&
438 (status != JPEG_REACHED_EOI));
440 for (;;) {
441 if (mInfo.output_scanline == 0) {
442 int scan = mInfo.input_scan_number;
444 /* if we haven't displayed anything yet (output_scan_number==0)
445 and we have enough data for a complete scan, force output
446 of the last full scan */
447 if ((mInfo.output_scan_number == 0) &&
448 (scan > 1) &&
449 (status != JPEG_REACHED_EOI))
450 scan--;
452 if (!jpeg_start_output(&mInfo, scan)) {
453 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
454 ("} (I/O suspension after jpeg_start_output() - PROGRESSIVE)"));
455 return; /* I/O suspension */
459 if (mInfo.output_scanline == 0xffffff)
460 mInfo.output_scanline = 0;
462 bool suspend;
463 OutputScanlines(&suspend);
465 if (suspend) {
466 if (mInfo.output_scanline == 0) {
467 /* didn't manage to read any lines - flag so we don't call
468 jpeg_start_output() multiple times for the same scan */
469 mInfo.output_scanline = 0xffffff;
471 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
472 ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)"));
473 return; /* I/O suspension */
476 if (mInfo.output_scanline == mInfo.output_height)
478 if (!jpeg_finish_output(&mInfo)) {
479 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
480 ("} (I/O suspension after jpeg_finish_output() - PROGRESSIVE)"));
481 return; /* I/O suspension */
484 if (jpeg_input_complete(&mInfo) &&
485 (mInfo.input_scan_number == mInfo.output_scan_number))
486 break;
488 mInfo.output_scanline = 0;
492 mState = JPEG_DONE;
496 case JPEG_DONE:
498 LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case");
500 /* Step 7: Finish decompression */
502 if (jpeg_finish_decompress(&mInfo) == FALSE) {
503 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
504 ("} (I/O suspension after jpeg_finish_decompress() - DONE)"));
505 return; /* I/O suspension */
508 mState = JPEG_SINK_NON_JPEG_TRAILER;
510 /* we're done dude */
511 break;
513 case JPEG_SINK_NON_JPEG_TRAILER:
514 PR_LOG(gJPEGlog, PR_LOG_DEBUG,
515 ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
517 break;
519 case JPEG_ERROR:
520 NS_ABORT_IF_FALSE(0, "Should always return immediately after error and not re-enter decoder");
523 PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
524 ("} (end of function)"));
525 return;
528 void
529 nsJPEGDecoder::NotifyDone()
531 PostFrameStop();
532 PostDecodeDone();
535 void
536 nsJPEGDecoder::OutputScanlines(bool* suspend)
538 *suspend = false;
540 const uint32_t top = mInfo.output_scanline;
542 while ((mInfo.output_scanline < mInfo.output_height)) {
543 /* Use the Cairo image buffer as scanline buffer */
544 uint32_t *imageRow = ((uint32_t*)mImageData) +
545 (mInfo.output_scanline * mInfo.output_width);
547 if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) {
548 /* Special case: scanline will be directly converted into packed ARGB */
549 if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) {
550 *suspend = true; /* suspend */
551 break;
553 continue; /* all done for this row! */
556 JSAMPROW sampleRow = (JSAMPROW)imageRow;
557 if (mInfo.output_components == 3) {
558 /* Put the pixels at end of row to enable in-place expansion */
559 sampleRow += mInfo.output_width;
562 /* Request one scanline. Returns 0 or 1 scanlines. */
563 if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) {
564 *suspend = true; /* suspend */
565 break;
568 if (mTransform) {
569 JSAMPROW source = sampleRow;
570 if (mInfo.out_color_space == JCS_GRAYSCALE) {
571 /* Convert from the 1byte grey pixels at begin of row
572 to the 3byte RGB byte pixels at 'end' of row */
573 sampleRow += mInfo.output_width;
575 qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width);
576 /* Move 3byte RGB data to end of row */
577 if (mInfo.out_color_space == JCS_CMYK) {
578 memmove(sampleRow + mInfo.output_width,
579 sampleRow,
580 3 * mInfo.output_width);
581 sampleRow += mInfo.output_width;
583 } else {
584 if (mInfo.out_color_space == JCS_CMYK) {
585 /* Convert from CMYK to RGB */
586 /* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */
587 /* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */
588 cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width);
589 sampleRow += mInfo.output_width;
591 if (mCMSMode == eCMSMode_All) {
592 /* No embedded ICC profile - treat as sRGB */
593 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
594 if (transform) {
595 qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width);
600 // counter for while() loops below
601 uint32_t idx = mInfo.output_width;
603 // copy as bytes until source pointer is 32-bit-aligned
604 for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) {
605 *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
606 sampleRow += 3;
609 // copy pixels in blocks of 4
610 while (idx >= 4) {
611 GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow);
612 idx -= 4;
613 sampleRow += 12;
614 imageRow += 4;
617 // copy remaining pixel(s)
618 while (idx--) {
619 // 32-bit read of final pixel will exceed buffer, so read bytes
620 *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
621 sampleRow += 3;
625 if (top != mInfo.output_scanline) {
626 nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
627 PostInvalidation(r);
633 /* Override the standard error method in the IJG JPEG decoder code. */
634 METHODDEF(void)
635 my_error_exit (j_common_ptr cinfo)
637 decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err;
639 /* Convert error to a browser error code */
640 nsresult error_code = err->pub.msg_code == JERR_OUT_OF_MEMORY
641 ? NS_ERROR_OUT_OF_MEMORY
642 : NS_ERROR_FAILURE;
644 #ifdef DEBUG
645 char buffer[JMSG_LENGTH_MAX];
647 /* Create the message */
648 (*err->pub.format_message) (cinfo, buffer);
650 fprintf(stderr, "JPEG decoding error:\n%s\n", buffer);
651 #endif
653 /* Return control to the setjmp point. We pass an nsresult masquerading as
654 * an int, which works because the setjmp() caller casts it back. */
655 longjmp(err->setjmp_buffer, static_cast<int>(error_code));
658 /******************************************************************************/
659 /*-----------------------------------------------------------------------------
660 * This is the callback routine from the IJG JPEG library used to supply new
661 * data to the decompressor when its input buffer is exhausted. It juggles
662 * multiple buffers in an attempt to avoid unnecessary copying of input data.
664 * (A simpler scheme is possible: It's much easier to use only a single
665 * buffer; when fill_input_buffer() is called, move any unconsumed data
666 * (beyond the current pointer/count) down to the beginning of this buffer and
667 * then load new data into the remaining buffer space. This approach requires
668 * a little more data copying but is far easier to get right.)
670 * At any one time, the JPEG decompressor is either reading from the necko
671 * input buffer, which is volatile across top-level calls to the IJG library,
672 * or the "backtrack" buffer. The backtrack buffer contains the remaining
673 * unconsumed data from the necko buffer after parsing was suspended due
674 * to insufficient data in some previous call to the IJG library.
676 * When suspending, the decompressor will back up to a convenient restart
677 * point (typically the start of the current MCU). The variables
678 * next_input_byte & bytes_in_buffer indicate where the restart point will be
679 * if the current call returns FALSE. Data beyond this point must be
680 * rescanned after resumption, so it must be preserved in case the decompressor
681 * decides to backtrack.
683 * Returns:
684 * TRUE if additional data is available, FALSE if no data present and
685 * the JPEG library should therefore suspend processing of input stream
686 *---------------------------------------------------------------------------*/
688 /******************************************************************************/
689 /* data source manager method */
690 /******************************************************************************/
693 /******************************************************************************/
694 /* data source manager method
695 Initialize source. This is called by jpeg_read_header() before any
696 data is actually read. May leave
697 bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
698 will occur immediately).
700 METHODDEF(void)
701 init_source (j_decompress_ptr jd)
705 /******************************************************************************/
706 /* data source manager method
707 Skip num_bytes worth of data. The buffer pointer and count should
708 be advanced over num_bytes input bytes, refilling the buffer as
709 needed. This is used to skip over a potentially large amount of
710 uninteresting data (such as an APPn marker). In some applications
711 it may be possible to optimize away the reading of the skipped data,
712 but it's not clear that being smart is worth much trouble; large
713 skips are uncommon. bytes_in_buffer may be zero on return.
714 A zero or negative skip count should be treated as a no-op.
716 METHODDEF(void)
717 skip_input_data (j_decompress_ptr jd, long num_bytes)
719 struct jpeg_source_mgr *src = jd->src;
720 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
722 if (num_bytes > (long)src->bytes_in_buffer) {
724 * Can't skip it all right now until we get more data from
725 * network stream. Set things up so that fill_input_buffer
726 * will skip remaining amount.
728 decoder->mBytesToSkip = (size_t)num_bytes - src->bytes_in_buffer;
729 src->next_input_byte += src->bytes_in_buffer;
730 src->bytes_in_buffer = 0;
732 } else {
733 /* Simple case. Just advance buffer pointer */
735 src->bytes_in_buffer -= (size_t)num_bytes;
736 src->next_input_byte += num_bytes;
741 /******************************************************************************/
742 /* data source manager method
743 This is called whenever bytes_in_buffer has reached zero and more
744 data is wanted. In typical applications, it should read fresh data
745 into the buffer (ignoring the current state of next_input_byte and
746 bytes_in_buffer), reset the pointer & count to the start of the
747 buffer, and return TRUE indicating that the buffer has been reloaded.
748 It is not necessary to fill the buffer entirely, only to obtain at
749 least one more byte. bytes_in_buffer MUST be set to a positive value
750 if TRUE is returned. A FALSE return should only be used when I/O
751 suspension is desired.
753 METHODDEF(boolean)
754 fill_input_buffer (j_decompress_ptr jd)
756 struct jpeg_source_mgr *src = jd->src;
757 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
759 if (decoder->mReading) {
760 const JOCTET *new_buffer = decoder->mSegment;
761 uint32_t new_buflen = decoder->mSegmentLen;
763 if (!new_buffer || new_buflen == 0)
764 return false; /* suspend */
766 decoder->mSegmentLen = 0;
768 if (decoder->mBytesToSkip) {
769 if (decoder->mBytesToSkip < new_buflen) {
770 /* All done skipping bytes; Return what's left. */
771 new_buffer += decoder->mBytesToSkip;
772 new_buflen -= decoder->mBytesToSkip;
773 decoder->mBytesToSkip = 0;
774 } else {
775 /* Still need to skip some more data in the future */
776 decoder->mBytesToSkip -= (size_t)new_buflen;
777 return false; /* suspend */
781 decoder->mBackBufferUnreadLen = src->bytes_in_buffer;
783 src->next_input_byte = new_buffer;
784 src->bytes_in_buffer = (size_t)new_buflen;
785 decoder->mReading = false;
787 return true;
790 if (src->next_input_byte != decoder->mSegment) {
791 /* Backtrack data has been permanently consumed. */
792 decoder->mBackBufferUnreadLen = 0;
793 decoder->mBackBufferLen = 0;
796 /* Save remainder of netlib buffer in backtrack buffer */
797 const uint32_t new_backtrack_buflen = src->bytes_in_buffer + decoder->mBackBufferLen;
799 /* Make sure backtrack buffer is big enough to hold new data. */
800 if (decoder->mBackBufferSize < new_backtrack_buflen) {
801 /* Check for malformed MARKER segment lengths, before allocating space for it */
802 if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) {
803 my_error_exit((j_common_ptr)(&decoder->mInfo));
806 /* Round up to multiple of 256 bytes. */
807 const size_t roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8;
808 JOCTET *buf = (JOCTET *)PR_REALLOC(decoder->mBackBuffer, roundup_buflen);
809 /* Check for OOM */
810 if (!buf) {
811 decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY;
812 my_error_exit((j_common_ptr)(&decoder->mInfo));
814 decoder->mBackBuffer = buf;
815 decoder->mBackBufferSize = roundup_buflen;
818 /* Copy remainder of netlib segment into backtrack buffer. */
819 memmove(decoder->mBackBuffer + decoder->mBackBufferLen,
820 src->next_input_byte,
821 src->bytes_in_buffer);
823 /* Point to start of data to be rescanned. */
824 src->next_input_byte = decoder->mBackBuffer + decoder->mBackBufferLen - decoder->mBackBufferUnreadLen;
825 src->bytes_in_buffer += decoder->mBackBufferUnreadLen;
826 decoder->mBackBufferLen = (size_t)new_backtrack_buflen;
827 decoder->mReading = true;
829 return false;
832 /******************************************************************************/
833 /* data source manager method */
835 * Terminate source --- called by jpeg_finish_decompress() after all
836 * data has been read to clean up JPEG source manager. NOT called by
837 * jpeg_abort() or jpeg_destroy().
839 METHODDEF(void)
840 term_source (j_decompress_ptr jd)
842 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
844 // This function shouldn't be called if we ran into an error we didn't
845 // recover from.
846 NS_ABORT_IF_FALSE(decoder->mState != JPEG_ERROR,
847 "Calling term_source on a JPEG with mState == JPEG_ERROR!");
849 // Notify using a helper method to get around protectedness issues.
850 decoder->NotifyDone();
853 } // namespace image
854 } // namespace mozilla
857 /**************** Inverted CMYK -> RGB conversion **************/
859 * Input is (Inverted) CMYK stored as 4 bytes per pixel.
860 * Output is RGB stored as 3 bytes per pixel.
861 * @param row Points to row buffer containing the CMYK bytes for each pixel in the row.
862 * @param width Number of pixels in the row.
864 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width)
866 /* Work from end to front to shrink from 4 bytes per pixel to 3 */
867 JSAMPROW in = row + width*4;
868 JSAMPROW out = in;
870 for (uint32_t i = width; i > 0; i--) {
871 in -= 4;
872 out -= 3;
874 // Source is 'Inverted CMYK', output is RGB.
875 // See: http://www.easyrgb.com/math.php?MATH=M12#text12
876 // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb
878 // From CMYK to CMY
879 // C = ( C * ( 1 - K ) + K )
880 // M = ( M * ( 1 - K ) + K )
881 // Y = ( Y * ( 1 - K ) + K )
883 // From Inverted CMYK to CMY is thus:
884 // C = ( (1-iC) * (1 - (1-iK)) + (1-iK) ) => 1 - iC*iK
885 // Same for M and Y
887 // Convert from CMY (0..1) to RGB (0..1)
888 // R = 1 - C => 1 - (1 - iC*iK) => iC*iK
889 // G = 1 - M => 1 - (1 - iM*iK) => iM*iK
890 // B = 1 - Y => 1 - (1 - iY*iK) => iY*iK
892 // Convert from Inverted CMYK (0..255) to RGB (0..255)
893 const uint32_t iC = in[0];
894 const uint32_t iM = in[1];
895 const uint32_t iY = in[2];
896 const uint32_t iK = in[3];
897 out[0] = iC*iK/255; // Red
898 out[1] = iM*iK/255; // Green
899 out[2] = iY*iK/255; // Blue