void nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy) { mSegment = (const JOCTET *)aBuffer; mSegmentLen = aCount; NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); /* Return here if there is a fatal error within libjpeg. */ nsresult error_code; // This cast to nsresult makes sense because setjmp() returns whatever we // passed to longjmp(), which was actually an nsresult. if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) { if (error_code == NS_ERROR_FAILURE) { PostDataError(); /* Error due to corrupt stream - return NS_OK and consume silently so that libpr0n doesn't throw away a partial image load */ mState = JPEG_SINK_NON_JPEG_TRAILER; PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (setjmp returned NS_ERROR_FAILURE)")); return; } else { /* Error due to reasons external to the stream (probably out of memory) - let libpr0n attempt to clean up, even though mozilla is seconds away from falling flat on its face. */ PostDecoderError(error_code); mState = JPEG_ERROR; PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (setjmp returned an error)")); return; } } PR_LOG(GetJPEGLog(), PR_LOG_DEBUG, ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); switch (mState) { case JPEG_HEADER: { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_HEADER case"); /* Step 3: read file parameters with jpeg_read_header() */ if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) { PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (JPEG_SUSPENDED)")); return; /* I/O suspension */ } int sampleSize = mImage.GetRequestedSampleSize(); if (sampleSize > 0) { mInfo.scale_num = 1; mInfo.scale_denom = sampleSize; } /* Used to set up image size so arrays can be allocated */ jpeg_calc_output_dimensions(&mInfo); // Post our size to the superclass PostSize(mInfo.output_width, mInfo.output_height, ReadOrientationFromEXIF()); if (HasError()) { // Setting the size led to an error. mState = JPEG_ERROR; return; } /* If we're doing a size decode, we're done. */ if (IsSizeDecode()) return; /* We're doing a full decode. */ if (mCMSMode != eCMSMode_Off && (mInProfile = GetICCProfile(mInfo)) != nullptr) { uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); bool mismatch = false; #ifdef DEBUG_tor fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace); #endif switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: if (profileSpace == icSigRgbData) mInfo.out_color_space = JCS_RGB; else if (profileSpace != icSigGrayData) mismatch = true; break; case JCS_RGB: if (profileSpace != icSigRgbData) mismatch = true; break; case JCS_YCbCr: if (profileSpace == icSigRgbData) mInfo.out_color_space = JCS_RGB; else // qcms doesn't support ycbcr mismatch = true; break; case JCS_CMYK: case JCS_YCCK: // qcms doesn't support cmyk mismatch = true; break; default: mState = JPEG_ERROR; PostDataError(); PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (unknown colorpsace (1))")); return; } if (!mismatch) { qcms_data_type type; switch (mInfo.out_color_space) { case JCS_GRAYSCALE: type = QCMS_DATA_GRAY_8; break; case JCS_RGB: type = QCMS_DATA_RGB_8; break; default: mState = JPEG_ERROR; PostDataError(); PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (unknown colorpsace (2))")); return; } #if 0 /* We don't currently support CMYK profiles. The following * code dealt with lcms types. Add something like this * back when we gain support for CMYK. */ /* Adobe Photoshop writes YCCK/CMYK files with inverted data */ if (mInfo.out_color_space == JCS_CMYK) type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); #endif if (gfxPlatform::GetCMSOutputProfile()) { /* Calculate rendering intent. */ int intent = gfxPlatform::GetRenderingIntent(); if (intent == -1) intent = qcms_profile_get_rendering_intent(mInProfile); /* Create the color management transform. */ mTransform = qcms_transform_create(mInProfile, type, gfxPlatform::GetCMSOutputProfile(), QCMS_DATA_RGB_8, (qcms_intent)intent); } } else { #ifdef DEBUG_tor fprintf(stderr, "ICM profile colorspace mismatch\n"); #endif } } if (!mTransform) { switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: // if we're not color managing we can decode directly to // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB if (mCMSMode != eCMSMode_All) { mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB; mInfo.out_color_components = 4; } else { mInfo.out_color_space = JCS_RGB; } break; case JCS_CMYK: case JCS_YCCK: /* libjpeg can convert from YCCK to CMYK, but not to RGB */ mInfo.out_color_space = JCS_CMYK; break; default: mState = JPEG_ERROR; PostDataError(); PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (unknown colorpsace (3))")); return; break; } } /* * Don't allocate a giant and superfluous memory buffer * when not doing a progressive decode. */ mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo); if (!mImageData) { mState = JPEG_ERROR; PostDecoderError(NS_ERROR_OUT_OF_MEMORY); PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (could not initialize image frame)")); return; } PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, (" JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels", mInfo.output_width, mInfo.output_height)); mState = JPEG_START_DECOMPRESS; } case JPEG_START_DECOMPRESS: { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case"); /* Step 4: set parameters for decompression */ /* FIXME -- Should reset dct_method and dither mode * for final pass of progressive JPEG */ mInfo.dct_method = JDCT_ISLOW; mInfo.dither_mode = JDITHER_FS; mInfo.do_fancy_upsampling = TRUE; mInfo.enable_2pass_quant = FALSE; mInfo.do_block_smoothing = TRUE; /* Step 5: Start decompressor */ if (jpeg_start_decompress(&mInfo) == FALSE) { PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (I/O suspension after jpeg_start_decompress())")); return; /* I/O suspension */ } /* If this is a progressive JPEG ... */ mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; } case JPEG_DECOMPRESS_SEQUENTIAL: { if (mState == JPEG_DECOMPRESS_SEQUENTIAL) { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case"); bool suspend; OutputScanlines(&suspend); if (suspend) { PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)")); return; /* I/O suspension */ } /* If we've completed image output ... */ NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!"); mState = JPEG_DONE; } } case JPEG_DECOMPRESS_PROGRESSIVE: { if (mState == JPEG_DECOMPRESS_PROGRESSIVE) { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case"); int status; do { status = jpeg_consume_input(&mInfo); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (mInfo.output_scanline == 0) { int scan = mInfo.input_scan_number; /* if we haven't displayed anything yet (output_scan_number==0) and we have enough data for a complete scan, force output of the last full scan */ if ((mInfo.output_scan_number == 0) && (scan > 1) && (status != JPEG_REACHED_EOI)) scan--; if (!jpeg_start_output(&mInfo, scan)) { PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (I/O suspension after jpeg_start_output() - PROGRESSIVE)")); return; /* I/O suspension */ } } if (mInfo.output_scanline == 0xffffff) mInfo.output_scanline = 0; bool suspend; OutputScanlines(&suspend); if (suspend) { if (mInfo.output_scanline == 0) { /* didn't manage to read any lines - flag so we don't call jpeg_start_output() multiple times for the same scan */ mInfo.output_scanline = 0xffffff; } PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)")); return; /* I/O suspension */ } if (mInfo.output_scanline == mInfo.output_height) { if (!jpeg_finish_output(&mInfo)) { PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (I/O suspension after jpeg_finish_output() - PROGRESSIVE)")); return; /* I/O suspension */ } if (jpeg_input_complete(&mInfo) && (mInfo.input_scan_number == mInfo.output_scan_number)) break; mInfo.output_scanline = 0; } } mState = JPEG_DONE; } } case JPEG_DONE: { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case"); /* Step 7: Finish decompression */ if (jpeg_finish_decompress(&mInfo) == FALSE) { PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (I/O suspension after jpeg_finish_decompress() - DONE)")); return; /* I/O suspension */ } mState = JPEG_SINK_NON_JPEG_TRAILER; /* we're done dude */ break; } case JPEG_SINK_NON_JPEG_TRAILER: PR_LOG(GetJPEGLog(), PR_LOG_DEBUG, ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this)); break; case JPEG_ERROR: NS_ABORT_IF_FALSE(0, "Should always return immediately after error and not re-enter decoder"); } PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, ("} (end of function)")); return; }
void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) { /* int number_passes; NOT USED */ png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; unsigned int channels; png_bytep trans = NULL; int num_trans = 0; nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); /* always decode to 24-bit RGB or 32-bit RGBA */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); /* Are we too big? */ if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) longjmp(png_jmpbuf(decoder->mPNG), 1); // Post our size to the superclass decoder->PostSize(width, height); if (decoder->HasError()) { // Setting the size lead to an error; this can happen when for example // a multipart channel sends an image of a different size. longjmp(png_jmpbuf(decoder->mPNG), 1); } if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { int sample_max = (1 << bit_depth); png_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); /* libpng doesn't reject a tRNS chunk with out-of-range samples so we check it here to avoid setting up a useless opacity channel or producing unexpected transparent pixels when using libpng-1.2.19 through 1.2.26 (bug #428045) */ if ((color_type == PNG_COLOR_TYPE_GRAY && (int)trans_values->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && ((int)trans_values->red > sample_max || (int)trans_values->green > sample_max || (int)trans_values->blue > sample_max))) { /* clear the tRNS valid flag and release tRNS memory */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); } else png_set_expand(png_ptr); } if (bit_depth == 16) png_set_strip_16(png_ptr); qcms_data_type inType; PRUint32 intent = -1; PRUint32 pIntent; if (decoder->mCMSMode != eCMSMode_Off) { intent = gfxPlatform::GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent); /* If we're not mandating an intent, use the one from the image. */ if (intent == PRUint32(-1)) intent = pIntent; } if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { qcms_data_type outType; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) outType = QCMS_DATA_RGBA_8; else outType = QCMS_DATA_RGB_8; decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(), outType, (qcms_intent)intent); } else { png_set_gray_to_rgb(png_ptr); // only do gamma correction if CMS isn't entirely disabled if (decoder->mCMSMode != eCMSMode_Off) PNGDoGammaCorrection(png_ptr, info_ptr); if (decoder->mCMSMode == eCMSMode_All) { if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) decoder->mTransform = gfxPlatform::GetCMSRGBATransform(); else decoder->mTransform = gfxPlatform::GetCMSRGBTransform(); } } /* let libpng expand interlaced images */ if (interlace_type == PNG_INTERLACE_ADAM7) { /* number_passes = */ png_set_interlace_handling(png_ptr); } /* now all of those things we set above are used to update various struct * members and whatnot, after which we can get channels, rowbytes, etc. */ png_read_update_info(png_ptr, info_ptr); decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr); /*---------------------------------------------------------------*/ /* copy PNG info into imagelib structs (formerly png_set_dims()) */ /*---------------------------------------------------------------*/ PRInt32 alpha_bits = 1; if (channels == 2 || channels == 4) { /* check if alpha is coming from a tRNS chunk and is binary */ if (num_trans) { /* if it's not an indexed color image, tRNS means binary */ if (color_type == PNG_COLOR_TYPE_PALETTE) { for (int i=0; i<num_trans; i++) { if ((trans[i] != 0) && (trans[i] != 255)) { alpha_bits = 8; break; } } } } else { alpha_bits = 8; } } if (channels == 1 || channels == 3) decoder->format = gfxASurface::ImageFormatRGB24; else if (channels == 2 || channels == 4) decoder->format = gfxASurface::ImageFormatARGB32; #ifdef PNG_APNG_SUPPORTED if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, NULL); if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { decoder->mFrameIsHidden = PR_TRUE; } else { #endif decoder->CreateFrame(0, 0, width, height, decoder->format); #ifdef PNG_APNG_SUPPORTED } #endif if (decoder->mTransform && (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) { PRUint32 bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (PRUint8 *)moz_malloc(bpp[channels] * width); if (!decoder->mCMSLine) { longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY } } if (interlace_type == PNG_INTERLACE_ADAM7) { if (height < PR_INT32_MAX / (width * channels)) decoder->interlacebuf = (PRUint8 *)moz_malloc(channels * width * height); if (!decoder->interlacebuf) { longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY } } /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593). * It would be better to show the default frame (if one has already been * successfully decoded) before bailing, but it's simpler to just bail * out with an error message. */ png_set_crc_action(png_ptr, PNG_CRC_NO_CHANGE, PNG_CRC_ERROR_QUIT); return; }
void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) { // int number_passes; NOT USED png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; unsigned int channels; png_bytep trans = nullptr; int num_trans = 0; nsPNGDecoder* decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); // Always decode to 24-bit RGB or 32-bit RGBA png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); // Are we too big? if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) { png_longjmp(decoder->mPNG, 1); } // Post our size to the superclass decoder->PostSize(width, height); if (decoder->HasError()) { // Setting the size led to an error. png_longjmp(decoder->mPNG, 1); } if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); // libpng doesn't reject a tRNS chunk with out-of-range samples // so we check it here to avoid setting up a useless opacity // channel or producing unexpected transparent pixels (bug #428045) if (bit_depth < 16) { png_uint_16 sample_max = (1 << bit_depth) - 1; if ((color_type == PNG_COLOR_TYPE_GRAY && trans_values->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && (trans_values->red > sample_max || trans_values->green > sample_max || trans_values->blue > sample_max))) { // clear the tRNS valid flag and release tRNS memory png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); num_trans = 0; } } if (num_trans != 0) { png_set_expand(png_ptr); } } if (bit_depth == 16) { png_set_scale_16(png_ptr); } qcms_data_type inType = QCMS_DATA_RGBA_8; uint32_t intent = -1; uint32_t pIntent; if (decoder->mCMSMode != eCMSMode_Off) { intent = gfxPlatform::GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent); // If we're not mandating an intent, use the one from the image. if (intent == uint32_t(-1)) { intent = pIntent; } } if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { qcms_data_type outType; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) { outType = QCMS_DATA_RGBA_8; } else { outType = QCMS_DATA_RGB_8; } decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(), outType, (qcms_intent)intent); } else { png_set_gray_to_rgb(png_ptr); // only do gamma correction if CMS isn't entirely disabled if (decoder->mCMSMode != eCMSMode_Off) { PNGDoGammaCorrection(png_ptr, info_ptr); } if (decoder->mCMSMode == eCMSMode_All) { if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) { decoder->mTransform = gfxPlatform::GetCMSRGBATransform(); } else { decoder->mTransform = gfxPlatform::GetCMSRGBTransform(); } } } // let libpng expand interlaced images if (interlace_type == PNG_INTERLACE_ADAM7) { // number_passes = png_set_interlace_handling(png_ptr); } // now all of those things we set above are used to update various struct // members and whatnot, after which we can get channels, rowbytes, etc. png_read_update_info(png_ptr, info_ptr); decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr); //---------------------------------------------------------------// // copy PNG info into imagelib structs (formerly png_set_dims()) // //---------------------------------------------------------------// if (channels == 1 || channels == 3) { decoder->format = gfx::SurfaceFormat::B8G8R8X8; } else if (channels == 2 || channels == 4) { decoder->format = gfx::SurfaceFormat::B8G8R8A8; } else { png_longjmp(decoder->mPNG, 1); // invalid number of channels } #ifdef PNG_APNG_SUPPORTED if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) { png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, nullptr); } if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { decoder->mFrameIsHidden = true; } else { #endif decoder->CreateFrame(0, 0, width, height, decoder->format); #ifdef PNG_APNG_SUPPORTED } #endif if (decoder->mTransform && (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) { uint32_t bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (uint8_t*)malloc(bpp[channels] * width); if (!decoder->mCMSLine) { png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY } } if (interlace_type == PNG_INTERLACE_ADAM7) { if (height < INT32_MAX / (width * channels)) { decoder->interlacebuf = (uint8_t*)malloc(channels * width * height); } if (!decoder->interlacebuf) { png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY } } if (decoder->NeedsNewFrame()) { // We know that we need a new frame, so pause input so the decoder // infrastructure can give it to us. png_process_data_pause(png_ptr, /* save = */ 1); } }
LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(const char* aData, size_t aLength) { mSegment = reinterpret_cast<const JOCTET*>(aData); mSegmentLen = aLength; // Return here if there is a fatal error within libjpeg. nsresult error_code; // This cast to nsresult makes sense because setjmp() returns whatever we // passed to longjmp(), which was actually an nsresult. if ((error_code = static_cast<nsresult>(setjmp(mErr.setjmp_buffer))) != NS_OK) { if (error_code == NS_ERROR_FAILURE) { // Error due to corrupt data. Make sure that we don't feed any more data // to libjpeg-turbo. mState = JPEG_SINK_NON_JPEG_TRAILER; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (setjmp returned NS_ERROR_FAILURE)")); } else { // Error for another reason. (Possibly OOM.) mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (setjmp returned an error)")); } return Transition::TerminateFailure(); } MOZ_LOG(sJPEGLog, LogLevel::Debug, ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); switch (mState) { case JPEG_HEADER: { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering JPEG_HEADER" " case"); // Step 3: read file parameters with jpeg_read_header() if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (JPEG_SUSPENDED)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // Post our size to the superclass PostSize(mInfo.image_width, mInfo.image_height, ReadOrientationFromEXIF()); if (HasError()) { // Setting the size led to an error. mState = JPEG_ERROR; return Transition::TerminateFailure(); } // If we're doing a metadata decode, we're done. if (IsMetadataDecode()) { return Transition::TerminateSuccess(); } // We're doing a full decode. if (mCMSMode != eCMSMode_Off && (mInProfile = GetICCProfile(mInfo)) != nullptr) { uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); bool mismatch = false; #ifdef DEBUG_tor fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace); #endif switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: if (profileSpace == icSigRgbData) { mInfo.out_color_space = JCS_RGB; } else if (profileSpace != icSigGrayData) { mismatch = true; } break; case JCS_RGB: if (profileSpace != icSigRgbData) { mismatch = true; } break; case JCS_YCbCr: if (profileSpace == icSigRgbData) { mInfo.out_color_space = JCS_RGB; } else { // qcms doesn't support ycbcr mismatch = true; } break; case JCS_CMYK: case JCS_YCCK: // qcms doesn't support cmyk mismatch = true; break; default: mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (unknown colorpsace (1))")); return Transition::TerminateFailure(); } if (!mismatch) { qcms_data_type type; switch (mInfo.out_color_space) { case JCS_GRAYSCALE: type = QCMS_DATA_GRAY_8; break; case JCS_RGB: type = QCMS_DATA_RGB_8; break; default: mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (unknown colorpsace (2))")); return Transition::TerminateFailure(); } #if 0 // We don't currently support CMYK profiles. The following // code dealt with lcms types. Add something like this // back when we gain support for CMYK. // Adobe Photoshop writes YCCK/CMYK files with inverted data if (mInfo.out_color_space == JCS_CMYK) { type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); } #endif if (gfxPlatform::GetCMSOutputProfile()) { // Calculate rendering intent. int intent = gfxPlatform::GetRenderingIntent(); if (intent == -1) { intent = qcms_profile_get_rendering_intent(mInProfile); } // Create the color management transform. mTransform = qcms_transform_create(mInProfile, type, gfxPlatform::GetCMSOutputProfile(), QCMS_DATA_RGB_8, (qcms_intent)intent); } } else { #ifdef DEBUG_tor fprintf(stderr, "ICM profile colorspace mismatch\n"); #endif } } if (!mTransform) { switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: // if we're not color managing we can decode directly to // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB if (mCMSMode != eCMSMode_All) { mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB; mInfo.out_color_components = 4; } else { mInfo.out_color_space = JCS_RGB; } break; case JCS_CMYK: case JCS_YCCK: // libjpeg can convert from YCCK to CMYK, but not to RGB mInfo.out_color_space = JCS_CMYK; break; default: mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (unknown colorpsace (3))")); return Transition::TerminateFailure(); } } // Don't allocate a giant and superfluous memory buffer // when not doing a progressive decode. mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo); /* Used to set up image size so arrays can be allocated */ jpeg_calc_output_dimensions(&mInfo); MOZ_ASSERT(!mImageData, "Already have a buffer allocated?"); nsresult rv = AllocateFrame(/* aFrameNum = */ 0, OutputSize(), FullOutputFrame(), SurfaceFormat::B8G8R8X8); if (NS_FAILED(rv)) { mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (could not initialize image frame)")); return Transition::TerminateFailure(); } MOZ_ASSERT(mImageData, "Should have a buffer now"); if (mDownscaler) { nsresult rv = mDownscaler->BeginFrame(Size(), Nothing(), mImageData, /* aHasAlpha = */ false); if (NS_FAILED(rv)) { mState = JPEG_ERROR; return Transition::TerminateFailure(); } } MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, (" JPEGDecoderAccounting: nsJPEGDecoder::" "Write -- created image frame with %ux%u pixels", mInfo.image_width, mInfo.image_height)); mState = JPEG_START_DECOMPRESS; MOZ_FALLTHROUGH; // to start decompressing. } case JPEG_START_DECOMPRESS: { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering" " JPEG_START_DECOMPRESS case"); // Step 4: set parameters for decompression // FIXME -- Should reset dct_method and dither mode // for final pass of progressive JPEG mInfo.dct_method = JDCT_ISLOW; mInfo.dither_mode = JDITHER_FS; mInfo.do_fancy_upsampling = TRUE; mInfo.enable_2pass_quant = FALSE; mInfo.do_block_smoothing = TRUE; // Step 5: Start decompressor if (jpeg_start_decompress(&mInfo) == FALSE) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_start_decompress())")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // If this is a progressive JPEG ... mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; MOZ_FALLTHROUGH; // to decompress sequential JPEG. } case JPEG_DECOMPRESS_SEQUENTIAL: { if (mState == JPEG_DECOMPRESS_SEQUENTIAL) { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- " "JPEG_DECOMPRESS_SEQUENTIAL case"); bool suspend; OutputScanlines(&suspend); if (suspend) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // If we've completed image output ... NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!"); mState = JPEG_DONE; } MOZ_FALLTHROUGH; // to decompress progressive JPEG. } case JPEG_DECOMPRESS_PROGRESSIVE: { if (mState == JPEG_DECOMPRESS_PROGRESSIVE) { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case"); int status; do { status = jpeg_consume_input(&mInfo); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (mInfo.output_scanline == 0) { int scan = mInfo.input_scan_number; // if we haven't displayed anything yet (output_scan_number==0) // and we have enough data for a complete scan, force output // of the last full scan if ((mInfo.output_scan_number == 0) && (scan > 1) && (status != JPEG_REACHED_EOI)) scan--; if (!jpeg_start_output(&mInfo, scan)) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_start_output() -" " PROGRESSIVE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } } if (mInfo.output_scanline == 0xffffff) { mInfo.output_scanline = 0; } bool suspend; OutputScanlines(&suspend); if (suspend) { if (mInfo.output_scanline == 0) { // didn't manage to read any lines - flag so we don't call // jpeg_start_output() multiple times for the same scan mInfo.output_scanline = 0xffffff; } MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } if (mInfo.output_scanline == mInfo.output_height) { if (!jpeg_finish_output(&mInfo)) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_finish_output() -" " PROGRESSIVE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } if (jpeg_input_complete(&mInfo) && (mInfo.input_scan_number == mInfo.output_scan_number)) break; mInfo.output_scanline = 0; if (mDownscaler) { mDownscaler->ResetForNextProgressivePass(); } } } mState = JPEG_DONE; } MOZ_FALLTHROUGH; // to finish decompressing. } case JPEG_DONE: { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::ProcessData -- entering" " JPEG_DONE case"); // Step 7: Finish decompression if (jpeg_finish_decompress(&mInfo) == FALSE) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_finish_decompress() - DONE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // Make sure we don't feed any more data to libjpeg-turbo. mState = JPEG_SINK_NON_JPEG_TRAILER; // We're done. return Transition::TerminateSuccess(); } case JPEG_SINK_NON_JPEG_TRAILER: MOZ_LOG(sJPEGLog, LogLevel::Debug, ("[this=%p] nsJPEGDecoder::ProcessData -- entering" " JPEG_SINK_NON_JPEG_TRAILER case\n", this)); MOZ_ASSERT_UNREACHABLE("Should stop getting data after entering state " "JPEG_SINK_NON_JPEG_TRAILER"); return Transition::TerminateSuccess(); case JPEG_ERROR: MOZ_ASSERT_UNREACHABLE("Should stop getting data after entering state " "JPEG_ERROR"); return Transition::TerminateFailure(); } MOZ_ASSERT_UNREACHABLE("Escaped the JPEG decoder state machine"); return Transition::TerminateFailure(); }