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"
20 #include "gfxPlatform.h"
26 #if defined(IS_BIG_ENDIAN)
27 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_XRGB
29 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX
32 static void cmyk_convert_rgb(JSAMPROW row
, JDIMENSION width
);
37 #if defined(PR_LOGGING)
38 PRLogModuleInfo
*gJPEGlog
= PR_NewLogModule("JPEGDecoder");
39 static PRLogModuleInfo
*gJPEGDecoderAccountingLog
= PR_NewLogModule("JPEGDecoderAccounting");
42 #define gJPEGDecoderAccountingLog
46 GetICCProfile(struct jpeg_decompress_struct
&info
)
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
);
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
)
78 memset(&mInfo
, 0, sizeof(jpeg_decompress_struct
));
79 memset(&mSourceMgr
, 0, sizeof(mSourceMgr
));
80 mInfo
.client_data
= (void*)this;
85 mBackBuffer
= nullptr;
86 mBackBufferLen
= mBackBufferSize
= mBackBufferUnreadLen
= 0;
93 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
94 ("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p",
98 nsJPEGDecoder::~nsJPEGDecoder()
100 // Step 8: Release JPEG decompression object
102 jpeg_destroy_decompress(&mInfo
);
104 PR_FREEIF(mBackBuffer
);
106 qcms_transform_release(mTransform
);
108 qcms_profile_release(mInProfile
);
110 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
111 ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
116 nsJPEGDecoder::SpeedHistogram()
118 return Telemetry::IMAGE_DECODE_SPEED_JPEG
;
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
);
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);
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
) &&
172 this->Write(nullptr, 0);
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. */
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
) {
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)"));
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
);
202 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
203 ("} (setjmp returned an error)"));
208 PR_LOG(gJPEGlog
, PR_LOG_DEBUG
,
209 ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this));
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
);
226 // Setting the size led to an error.
231 /* If we're doing a size decode, we're done. */
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;
242 fprintf(stderr
, "JPEG profileSpace: 0x%08X\n", profileSpace
);
244 switch (mInfo
.jpeg_color_space
) {
246 if (profileSpace
== icSigRgbData
)
247 mInfo
.out_color_space
= JCS_RGB
;
248 else if (profileSpace
!= icSigGrayData
)
252 if (profileSpace
!= icSigRgbData
)
256 if (profileSpace
== icSigRgbData
)
257 mInfo
.out_color_space
= JCS_RGB
;
259 // qcms doesn't support ycbcr
264 // qcms doesn't support cmyk
270 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
271 ("} (unknown colorpsace (1))"));
277 switch (mInfo
.out_color_space
) {
279 type
= QCMS_DATA_GRAY_8
;
282 type
= QCMS_DATA_RGB_8
;
287 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
288 ("} (unknown colorpsace (2))"));
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);
301 if (gfxPlatform::GetCMSOutputProfile()) {
303 /* Calculate rendering intent. */
304 int intent
= gfxPlatform::GetRenderingIntent();
306 intent
= qcms_profile_get_rendering_intent(mInProfile
);
308 /* Create the color management transform. */
309 mTransform
= qcms_transform_create(mInProfile
,
311 gfxPlatform::GetCMSOutputProfile(),
313 (qcms_intent
)intent
);
317 fprintf(stderr
, "ICM profile colorspace mismatch\n");
323 switch (mInfo
.jpeg_color_space
) {
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;
333 mInfo
.out_color_space
= JCS_RGB
;
338 /* libjpeg can convert from YCCK to CMYK, but not to RGB */
339 mInfo
.out_color_space
= JCS_CMYK
;
344 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
345 ("} (unknown colorpsace (3))"));
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
))) {
365 PostDecoderError(NS_ERROR_OUT_OF_MEMORY
);
366 PR_LOG(gJPEGDecoderAccountingLog
, PR_LOG_DEBUG
,
367 ("} (could not initialize image frame)"));
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
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");
414 OutputScanlines(&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!");
428 case JPEG_DECOMPRESS_PROGRESSIVE
:
430 if (mState
== JPEG_DECOMPRESS_PROGRESSIVE
)
432 LOG_SCOPE(gJPEGlog
, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case");
436 status
= jpeg_consume_input(&mInfo
);
437 } while ((status
!= JPEG_SUSPENDED
) &&
438 (status
!= JPEG_REACHED_EOI
));
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) &&
449 (status
!= JPEG_REACHED_EOI
))
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;
463 OutputScanlines(&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
))
488 mInfo
.output_scanline
= 0;
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 */
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));
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)"));
529 nsJPEGDecoder::NotifyDone()
536 nsJPEGDecoder::OutputScanlines(bool* suspend
)
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 */
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 */
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
,
580 3 * mInfo
.output_width
);
581 sampleRow
+= mInfo
.output_width
;
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();
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]);
609 // copy pixels in blocks of 4
611 GFX_BLOCK_RGB_TO_FRGB(sampleRow
, imageRow
);
617 // copy remaining pixel(s)
619 // 32-bit read of final pixel will exceed buffer, so read bytes
620 *imageRow
++ = gfxPackedPixel(0xFF, sampleRow
[0], sampleRow
[1], sampleRow
[2]);
625 if (top
!= mInfo
.output_scanline
) {
626 nsIntRect
r(0, top
, mInfo
.output_width
, mInfo
.output_scanline
-top
);
633 /* Override the standard error method in the IJG JPEG decoder code. */
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
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
);
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.
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).
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.
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;
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.
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;
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;
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
);
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;
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().
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
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();
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;
870 for (uint32_t i
= width
; i
> 0; i
--) {
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
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
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