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 ImageLib 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 ImageLib 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; }
bool decode(const SharedBuffer& data, bool onlySize) { m_decodingSizeOnly = onlySize; unsigned newByteCount = data.size() - m_bufferLength; unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer += newByteCount; m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset; // If we still have bytes to skip, try to skip those now. if (m_bytesToSkip) skipBytes(m_bytesToSkip); m_bufferLength = data.size(); // We need to do the setjmp here. Otherwise bad things will happen if (setjmp(m_err.setjmp_buffer)) return m_decoder->setFailed(); switch (m_state) { case JPEG_HEADER: // Read file parameters with jpeg_read_header(). if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) return false; // I/O suspension. switch (m_info.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: // libjpeg can convert GRAYSCALE and YCbCr image pixels to RGB. m_info.out_color_space = rgbOutputColorSpace(); break; case JCS_CMYK: case JCS_YCCK: // libjpeg can convert YCCK to CMYK, but neither to RGB, so we // manually convert CMKY to RGB. m_info.out_color_space = JCS_CMYK; break; default: return m_decoder->setFailed(); } // Don't allocate a giant and superfluous memory buffer when the // image is a sequential JPEG. m_info.buffered_image = jpeg_has_multiple_scans(&m_info); // Used to set up image size so arrays can be allocated. jpeg_calc_output_dimensions(&m_info); // Make a one-row-high sample array that will go away when done with // image. Always make it big enough to hold an RGB row. Since this // uses the IJG memory manager, it must be allocated before the call // to jpeg_start_compress(). m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, JPOOL_IMAGE, m_info.output_width * 4, 1); m_state = JPEG_START_DECOMPRESS; // We can fill in the size now that the header is available. if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) return false; // Allow color management of the decoded RGBA pixels if possible. if (!m_decoder->ignoresGammaAndColorProfile()) { ColorProfile rgbInputDeviceColorProfile = readColorProfile(info()); if (!rgbInputDeviceColorProfile.isEmpty()) m_decoder->setColorProfile(rgbInputDeviceColorProfile); } if (m_decodingSizeOnly) { // We can stop here. Reduce our buffer length and available // data. m_bufferLength -= m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer = 0; return true; } // FALL THROUGH case JPEG_START_DECOMPRESS: // Set parameters for decompression. // FIXME -- Should reset dct_method and dither mode for final pass // of progressive JPEG. m_info.dct_method = JDCT_ISLOW; m_info.dither_mode = JDITHER_FS; m_info.do_fancy_upsampling = true; m_info.enable_2pass_quant = false; m_info.do_block_smoothing = true; // Start decompressor. if (!jpeg_start_decompress(&m_info)) return false; // I/O suspension. // If this is a progressive JPEG ... m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; // FALL THROUGH case JPEG_DECOMPRESS_SEQUENTIAL: if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { if (!m_decoder->outputScanlines()) return false; // I/O suspension. // If we've completed image output... ASSERT(m_info.output_scanline == m_info.output_height); m_state = JPEG_DONE; } // FALL THROUGH case JPEG_DECOMPRESS_PROGRESSIVE: if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { int status; do { status = jpeg_consume_input(&m_info); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (!m_info.output_scanline) { int scan = m_info.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 (!m_info.output_scan_number && (scan > 1) && (status != JPEG_REACHED_EOI)) --scan; if (!jpeg_start_output(&m_info, scan)) return false; // I/O suspension. } if (m_info.output_scanline == 0xffffff) m_info.output_scanline = 0; if (!m_decoder->outputScanlines()) { if (!m_info.output_scanline) // Didn't manage to read any lines - flag so we // don't call jpeg_start_output() multiple times for // the same scan. m_info.output_scanline = 0xffffff; return false; // I/O suspension. } if (m_info.output_scanline == m_info.output_height) { if (!jpeg_finish_output(&m_info)) return false; // I/O suspension. if (jpeg_input_complete(&m_info) && (m_info.input_scan_number == m_info.output_scan_number)) break; m_info.output_scanline = 0; } } m_state = JPEG_DONE; } // FALL THROUGH case JPEG_DONE: // Finish decompression. return jpeg_finish_decompress(&m_info); case JPEG_ERROR: // We can get here if the constructor failed. return m_decoder->setFailed(); } return true; }
bool decode(const SharedBuffer& data, bool onlySize) { unsigned newByteCount = data.size() - m_bufferLength; unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer += newByteCount; m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset; // If we still have bytes to skip, try to skip those now. if (m_bytesToSkip) skipBytes(m_bytesToSkip); m_bufferLength = data.size(); // We need to do the setjmp here. Otherwise bad things will happen if (setjmp(m_err.setjmp_buffer)) return m_decoder->setFailed(); switch (m_state) { case JPEG_HEADER: // Read file parameters with jpeg_read_header(). if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) return false; // I/O suspension. switch (m_info.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: // libjpeg can convert GRAYSCALE and YCbCr image pixels to RGB. m_info.out_color_space = rgbOutputColorSpace(); #if defined(TURBO_JPEG_RGB_SWIZZLE) if (m_info.saw_JFIF_marker) break; // FIXME: Swizzle decoding does not support Adobe transform=0 // images (yet), so revert to using JSC_RGB in that case. if (m_info.saw_Adobe_marker && !m_info.Adobe_transform) m_info.out_color_space = JCS_RGB; #endif break; case JCS_CMYK: case JCS_YCCK: // libjpeg can convert YCCK to CMYK, but neither to RGB, so we // manually convert CMKY to RGB. m_info.out_color_space = JCS_CMYK; break; default: return m_decoder->setFailed(); } m_state = JPEG_START_DECOMPRESS; // We can fill in the size now that the header is available. if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) return false; // Calculate and set decoded size. m_info.scale_num = m_decoder->desiredScaleNumerator(); m_info.scale_denom = scaleDenominator; jpeg_calc_output_dimensions(&m_info); m_decoder->setDecodedSize(m_info.output_width, m_info.output_height); m_decoder->setOrientation(readImageOrientation(info())); #if USE(QCMSLIB) // Allow color management of the decoded RGBA pixels if possible. if (!m_decoder->ignoresGammaAndColorProfile()) { ColorProfile colorProfile; readColorProfile(info(), colorProfile); createColorTransform(colorProfile, colorSpaceHasAlpha(m_info.out_color_space)); #if defined(TURBO_JPEG_RGB_SWIZZLE) // Input RGBA data to qcms. Note: restored to BGRA on output. if (m_transform && m_info.out_color_space == JCS_EXT_BGRA) m_info.out_color_space = JCS_EXT_RGBA; #endif } #endif // Don't allocate a giant and superfluous memory buffer when the // image is a sequential JPEG. m_info.buffered_image = jpeg_has_multiple_scans(&m_info); if (onlySize) { // We can stop here. Reduce our buffer length and available data. m_bufferLength -= m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer = 0; return true; } // FALL THROUGH case JPEG_START_DECOMPRESS: // Set parameters for decompression. // FIXME -- Should reset dct_method and dither mode for final pass // of progressive JPEG. m_info.dct_method = dctMethod(); m_info.dither_mode = ditherMode(); m_info.do_fancy_upsampling = doFancyUpsampling(); m_info.enable_2pass_quant = false; m_info.do_block_smoothing = true; // Make a one-row-high sample array that will go away when done with // image. Always make it big enough to hold an RGB row. Since this // uses the IJG memory manager, it must be allocated before the call // to jpeg_start_compress(). // FIXME: note that some output color spaces do not need the samples // buffer. Remove this allocation for those color spaces. m_samples = (*m_info.mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(&m_info), JPOOL_IMAGE, m_info.output_width * 4, 1); // Start decompressor. if (!jpeg_start_decompress(&m_info)) return false; // I/O suspension. // If this is a progressive JPEG ... m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; // FALL THROUGH case JPEG_DECOMPRESS_SEQUENTIAL: if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { if (!m_decoder->outputScanlines()) return false; // I/O suspension. // If we've completed image output... ASSERT(m_info.output_scanline == m_info.output_height); m_state = JPEG_DONE; } // FALL THROUGH case JPEG_DECOMPRESS_PROGRESSIVE: if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { int status; do { status = jpeg_consume_input(&m_info); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (!m_info.output_scanline) { int scan = m_info.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 (!m_info.output_scan_number && (scan > 1) && (status != JPEG_REACHED_EOI)) --scan; if (!jpeg_start_output(&m_info, scan)) return false; // I/O suspension. } if (m_info.output_scanline == 0xffffff) m_info.output_scanline = 0; // If outputScanlines() fails, it deletes |this|. Therefore, // copy the decoder pointer and use it to check for failure // to avoid member access in the failure case. JPEGImageDecoder* decoder = m_decoder; if (!decoder->outputScanlines()) { if (decoder->failed()) // Careful; |this| is deleted. return false; if (!m_info.output_scanline) // Didn't manage to read any lines - flag so we // don't call jpeg_start_output() multiple times for // the same scan. m_info.output_scanline = 0xffffff; return false; // I/O suspension. } if (m_info.output_scanline == m_info.output_height) { if (!jpeg_finish_output(&m_info)) return false; // I/O suspension. if (jpeg_input_complete(&m_info) && (m_info.input_scan_number == m_info.output_scan_number)) break; m_info.output_scanline = 0; } } m_state = JPEG_DONE; } // FALL THROUGH case JPEG_DONE: // Finish decompression. return jpeg_finish_decompress(&m_info); case JPEG_ERROR: // We can get here if the constructor failed. return m_decoder->setFailed(); } return true; }
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.) PostDecoderError(error_code); 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 } // If we have a sample size specified for -moz-sample-size, use it. if (mSampleSize > 0) { mInfo.scale_num = 1; mInfo.scale_denom = mSampleSize; } // 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 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; PostDataError(); 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; PostDataError(); 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; PostDataError(); 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); MOZ_ASSERT(!mImageData, "Already have a buffer allocated?"); nsIntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize(); nsresult rv = AllocateFrame(0, targetSize, nsIntRect(nsIntPoint(), targetSize), gfx::SurfaceFormat::B8G8R8A8); 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(GetSize(), 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.output_width, mInfo.output_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(); }
/* * Receive and process new chunks of JPEG image data */ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize) { DilloImgType type; uchar_t *linebuf; JSAMPLE *array[1]; int num_read; _MSG("Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg, (long) BufSize, (ulong_t)jpeg->Start_Ofs); /* See if we are supposed to skip ahead. */ if (BufSize <= jpeg->Start_Ofs) return; /* Concatenate with the partial input, if any. */ jpeg->cinfo.src->next_input_byte = (uchar_t *)Buf + jpeg->Start_Ofs; jpeg->cinfo.src->bytes_in_buffer = BufSize - jpeg->Start_Ofs; jpeg->NewStart = BufSize; jpeg->Data = Buf; if (setjmp(jpeg->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ jpeg->state = DILLO_JPEG_ERROR; } /* Process the bytes in the input buffer. */ if (jpeg->state == DILLO_JPEG_INIT) { /* decompression step 3 (see libjpeg.doc) */ if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) { type = DILLO_IMG_TYPE_GRAY; if (jpeg->cinfo.num_components == 1) { type = DILLO_IMG_TYPE_GRAY; } else if (jpeg->cinfo.num_components == 3) { type = DILLO_IMG_TYPE_RGB; } else { MSG("4-component JPEG!\n"); if (jpeg->cinfo.jpeg_color_space == JCS_YCCK) MSG("YCCK. Are the colors wrong?\n"); if (!jpeg->cinfo.saw_Adobe_marker) MSG("No adobe marker! Is the image shown in reverse video?\n"); type = DILLO_IMG_TYPE_CMYK_INV; } /* * If a multiple-scan image is not completely in cache, * use progressive display, updating as it arrives. */ if (jpeg_has_multiple_scans(&jpeg->cinfo) && !(a_Capi_get_flags(jpeg->url) & CAPI_Completed)) jpeg->cinfo.buffered_image = TRUE; /* check max image size */ if (jpeg->cinfo.image_width <= 0 || jpeg->cinfo.image_height <= 0 || jpeg->cinfo.image_width > IMAGE_MAX_AREA / jpeg->cinfo.image_height) { MSG("Jpeg_write: suspicious image size request %u x %u\n", (uint_t)jpeg->cinfo.image_width, (uint_t)jpeg->cinfo.image_height); jpeg->state = DILLO_JPEG_ERROR; return; } a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image, (uint_t)jpeg->cinfo.image_width, (uint_t)jpeg->cinfo.image_height, type); /* decompression step 4 (see libjpeg.doc) */ jpeg->state = DILLO_JPEG_STARTING; } } if (jpeg->state == DILLO_JPEG_STARTING) { /* decompression step 5 (see libjpeg.doc) */ if (jpeg_start_decompress(&(jpeg->cinfo))) { jpeg->y = 0; jpeg->state = jpeg->cinfo.buffered_image ? DILLO_JPEG_READ_BEGIN_SCAN : DILLO_JPEG_READ_IN_SCAN; } } /* * A progressive jpeg contains multiple scans that can be used to display * an increasingly sharp image as it is being received. The reading of each * scan must be surrounded by jpeg_start_output()/jpeg_finish_output(). */ if (jpeg->state == DILLO_JPEG_READ_END_SCAN) { if (jpeg_finish_output(&jpeg->cinfo)) { if (jpeg_input_complete(&jpeg->cinfo)) { jpeg->state = DILLO_JPEG_DONE; } else { jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN; } } } if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) { if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) { a_Dicache_new_scan(jpeg->url, jpeg->version); jpeg->state = DILLO_JPEG_READ_IN_SCAN; } } if (jpeg->state == DILLO_JPEG_READ_IN_SCAN) { linebuf = dMalloc(jpeg->cinfo.image_width * jpeg->cinfo.num_components); array[0] = linebuf; while (1) { num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1); if (num_read == 0) { /* out of input */ break; } a_Dicache_write(jpeg->url, jpeg->version, linebuf, jpeg->y); jpeg->y++; if (jpeg->y == jpeg->cinfo.image_height) { /* end of scan */ if (!jpeg->cinfo.buffered_image) { /* single scan */ jpeg->state = DILLO_JPEG_DONE; break; } else { jpeg->y = 0; if (jpeg_input_complete(&jpeg->cinfo)) { if (jpeg->cinfo.input_scan_number == jpeg->cinfo.output_scan_number) { jpeg->state = DILLO_JPEG_DONE; break; } else { /* one final loop through the scanlines */ jpeg_finish_output(&jpeg->cinfo); jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number); continue; } } jpeg->state = DILLO_JPEG_READ_END_SCAN; if (!jpeg_finish_output(&jpeg->cinfo)) { /* out of input */ break; } else { if (jpeg_input_complete(&jpeg->cinfo)) { jpeg->state = DILLO_JPEG_DONE; break; } else { jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN; } } if (!jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) { /* out of input */ break; } a_Dicache_new_scan(jpeg->url, jpeg->version); jpeg->state = DILLO_JPEG_READ_IN_SCAN; } } } dFree(linebuf); } }
bool ScImgDataLoader_JPEG::loadPicture(const QString& fn, int /*page*/, int res, bool thumbnail) { bool isCMYK = false; bool fromPS = false; float xres = 72.0, yres = 72.0; if (!QFile::exists(fn)) return false; ExifData ExifInf; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; infile = NULL; initialize(); m_imageInfoRecord.type = ImageTypeJPG; m_imageInfoRecord.exifInfo.thumbnail = QImage(); if (setjmp (jerr.setjmp_buffer)) { jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); return false; } jpeg_create_decompress (&cinfo); if ((infile = fopen (fn.toLocal8Bit(), "rb")) == NULL) return false; jpeg_stdio_src(&cinfo, infile); jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF); jpeg_save_markers(&cinfo, PHOTOSHOP_MARKER, 0xFFFF); jpeg_read_header(&cinfo, true); jpeg_start_decompress(&cinfo); bool exi = ExifInf.scan(fn); if ((exi) && (ExifInf.exifDataValid)) { if (cinfo.output_components == 4) m_imageInfoRecord.colorspace = ColorSpaceCMYK; else if (cinfo.output_components == 3) m_imageInfoRecord.colorspace = ColorSpaceRGB; else if (cinfo.output_components == 1) m_imageInfoRecord.colorspace = ColorSpaceGray; if ((!ExifInf.Thumbnail.isNull()) && thumbnail) { m_image = ExifInf.getThumbnail(); m_imageInfoRecord.exifInfo.thumbnail = ExifInf.getThumbnail(); if (cinfo.output_components == 4) { QRgb *s; unsigned char cc, cm, cy, ck; for( int yit=0; yit < m_image.height(); ++yit ) { s = (QRgb*)(m_image.scanLine( yit )); for(int xit=0; xit < m_image.width(); ++xit ) { cc = 255 - qRed(*s); cm = 255 - qGreen(*s); cy = 255 - qBlue(*s); ck = qMin(qMin(cc, cm), cy); *s = qRgba(cc-ck,cm-ck,cy-ck,ck); s++; } } } } else m_imageInfoRecord.exifInfo.thumbnail = QImage(); m_imageInfoRecord.exifInfo.cameraName = ExifInf.getCameraModel(); m_imageInfoRecord.exifInfo.cameraVendor = ExifInf.getCameraMake(); m_imageInfoRecord.exifInfo.comment = ExifInf.getComment(); m_imageInfoRecord.exifInfo.width = ExifInf.getWidth(); m_imageInfoRecord.exifInfo.height = ExifInf.getHeight(); m_imageInfoRecord.exifInfo.userComment = ExifInf.getUserComment(); m_imageInfoRecord.exifInfo.dateTime = ExifInf.getDateTime(); m_imageInfoRecord.exifInfo.ApertureFNumber = ExifInf.getApertureFNumber(); m_imageInfoRecord.exifInfo.ExposureTime = ExifInf.getExposureTime(); m_imageInfoRecord.exifInfo.ISOequivalent = ExifInf.getISOequivalent(); m_imageInfoRecord.exifDataValid = true; if (cinfo.density_unit == 0) { xres = 72; yres = 72; } else if ( cinfo.density_unit == 1 ) { xres = cinfo.X_density; yres = cinfo.Y_density; } else if ( cinfo.density_unit == 2 ) { xres = cinfo.X_density * 2.54; yres = cinfo.Y_density * 2.54; } if( xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0 ) { xres = yres = 72.0; QFileInfo qfi(fn); m_message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName()); m_msgType = warningMsg; } m_imageInfoRecord.xres = qRound(xres); m_imageInfoRecord.yres = qRound(yres); m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo); if ((!ExifInf.Thumbnail.isNull()) && thumbnail) { jpeg_destroy_decompress(&cinfo); fclose(infile); return true; } } else m_imageInfoRecord.exifDataValid = false; m_imageInfoRecord.exifInfo.thumbnail = QImage(); unsigned int EmbedLen = 0; unsigned char* EmbedBuffer; if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen)) { QByteArray profArray = QByteArray((const char*) EmbedBuffer, EmbedLen); ScColorProfile prof = ScColorMgmtEngine::openProfileFromMem(profArray); m_embeddedProfile = profArray; m_imageInfoRecord.profileName = prof.productDescription(); m_imageInfoRecord.isEmbedded = true; free(EmbedBuffer); } else { m_imageInfoRecord.isEmbedded = false; m_imageInfoRecord.profileName = ""; } unsigned int PhotoshopLen = 0; unsigned char * PhotoshopBuffer; if (cinfo.density_unit == 0) { xres = 72; yres = 72; m_image.setDotsPerMeterX(2834); m_image.setDotsPerMeterY(2834); } else if ( cinfo.density_unit == 1 ) { xres = cinfo.X_density; yres = cinfo.Y_density; m_image.setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) ); m_image.setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) ); } else if ( cinfo.density_unit == 2 ) { xres = cinfo.X_density * 2.54; yres = cinfo.Y_density * 2.54; m_image.setDotsPerMeterX( int(100. * cinfo.X_density) ); m_image.setDotsPerMeterY( int(100. * cinfo.Y_density) ); } if( xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0 ) { xres = yres = 72.0; m_image.setDotsPerMeterX(2834); m_image.setDotsPerMeterY(2834); QFileInfo qfi(fn); m_message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName()); m_msgType = warningMsg; } m_imageInfoRecord.xres = qRound(xres); m_imageInfoRecord.yres = qRound(yres); if (cinfo.output_components == 4) { isCMYK = true; m_imageInfoRecord.colorspace = ColorSpaceCMYK; } else if (cinfo.output_components == 3) m_imageInfoRecord.colorspace = ColorSpaceRGB; else if (cinfo.output_components == 1) m_imageInfoRecord.colorspace = ColorSpaceGray; m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo); if (read_jpeg_marker(PHOTOSHOP_MARKER,&cinfo, &PhotoshopBuffer, &PhotoshopLen) ) { if (PhotoshopLen != 0) { bool savEx = m_imageInfoRecord.exifDataValid; QByteArray arrayPhot(PhotoshopLen, ' '); arrayPhot = QByteArray::fromRawData((const char*)PhotoshopBuffer,PhotoshopLen); QDataStream strPhot(&arrayPhot,QIODevice::ReadOnly); strPhot.setByteOrder( QDataStream::BigEndian ); PSDHeader fakeHeader; fakeHeader.width = cinfo.output_width; fakeHeader.height = cinfo.output_height; if (cinfo.output_components == 4) m_imageInfoRecord.colorspace = ColorSpaceCMYK; else if (cinfo.output_components == 3) m_imageInfoRecord.colorspace = ColorSpaceRGB; else if (cinfo.output_components == 1) m_imageInfoRecord.colorspace = ColorSpaceGray; m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo); parseRessourceData(strPhot, fakeHeader, PhotoshopLen); // Photoshop resolution is more accurate than jpeg header resolution xres = m_imageInfoRecord.xres; yres = m_imageInfoRecord.yres; m_image.setDotsPerMeterX( int(100. * m_imageInfoRecord.xres / 2.54) ); m_image.setDotsPerMeterY( int(100. * m_imageInfoRecord.yres / 2.54) ); if( xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0 ) { xres = yres = 72.0; m_imageInfoRecord.xres = qRound(xres); m_imageInfoRecord.yres = qRound(yres); m_image.setDotsPerMeterX(2834); m_image.setDotsPerMeterY(2834); QFileInfo qfi(fn); m_message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName()); m_msgType = warningMsg; } if (m_imageInfoRecord.exifDataValid && !m_imageInfoRecord.exifInfo.thumbnail.isNull() && thumbnail) { m_image = QImage(m_imageInfoRecord.exifInfo.width, m_imageInfoRecord.exifInfo.height, QImage::Format_ARGB32 ); m_imageInfoRecord.exifInfo.width = cinfo.output_width; m_imageInfoRecord.exifInfo.height = cinfo.output_height; if (cinfo.output_components == 4) { QRgb *d; QRgb *s; unsigned char cc, cm, cy, ck; for( int yit=0; yit < m_image.height(); ++yit ) { d = (QRgb*)(m_image.scanLine( yit )); s = (QRgb*)(m_imageInfoRecord.exifInfo.thumbnail.scanLine( yit )); for(int xit=0; xit < m_image.width(); ++xit ) { cc = 255 - qRed(*s); cm = 255 - qGreen(*s); cy = 255 - qBlue(*s); ck = qMin(qMin(cc, cm), cy); *d = qRgba(cc-ck,cm-ck,cy-ck,ck); s++; d++; } } } else m_image = m_imageInfoRecord.exifInfo.thumbnail.copy(); } m_imageInfoRecord.valid = (m_imageInfoRecord.PDSpathData.size())>0?true:false; // The only interest is vectormask arrayPhot.clear(); free( PhotoshopBuffer ); if (m_imageInfoRecord.exifDataValid && !m_imageInfoRecord.exifInfo.thumbnail.isNull() && thumbnail) { jpeg_destroy_decompress(&cinfo); fclose(infile); return true; } m_imageInfoRecord.exifInfo.thumbnail = QImage(); m_imageInfoRecord.exifDataValid = savEx; fromPS = true; } } if ( cinfo.output_components == 3 || cinfo.output_components == 4) m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 ); else if ( cinfo.output_components == 1 ) { m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8 ); m_image.setNumColors(256); for (int i=0; i<256; i++) m_image.setColor(i, qRgb(i,i,i)); } if (!m_image.isNull()) { uchar* data = m_image.bits(); int bpl = m_image.bytesPerLine(); while (cinfo.output_scanline < cinfo.output_height) { uchar *d = data + cinfo.output_scanline * bpl; (void) jpeg_read_scanlines(&cinfo, &d, 1); } if ( cinfo.output_components == 3 ) { uchar *in; QRgb *out; for (uint j=0; j<cinfo.output_height; j++) { in = m_image.scanLine(j) + cinfo.output_width * 3; out = (QRgb*) m_image.scanLine(j); for (uint i=cinfo.output_width; i--; ) { in -= 3; out[i] = qRgb(in[0], in[1], in[2]); } } } if ( cinfo.output_components == 4 ) { int method = 0; if (cinfo.jpeg_color_space == JCS_YCCK) method = 1; else if (fromPS) { if ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker) && (cinfo.Adobe_transform == 0)) method = 2; } else if ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker)) method = 1; QRgb *ptr; unsigned char c, m, y ,k; unsigned char *p; for (int i = 0; i < m_image.height(); i++) { ptr = (QRgb*) m_image.scanLine(i); if (method == 1) { for (int j = 0; j < m_image.width(); j++) { p = (unsigned char *) ptr; c = p[0]; m = p[1]; y = p[2]; k = p[3]; *ptr = qRgba(255 - c, 255 - m, 255 - y, 255 - k); ptr++; } } else if (method == 2) { for (int j = 0; j < m_image.width(); j++) { p = (unsigned char *) ptr; c = p[0]; m = p[1]; y = p[2]; k = p[3]; *ptr = qRgba(255 - c, 255 - m, 255 - y, k); ptr++; } } else { for (int j = 0; j < m_image.width(); j++) { p = (unsigned char *) ptr; c = p[0]; m = p[1]; y = p[2]; k = p[3]; *ptr = qRgba(y, m, c, k); ptr++; } } } isCMYK = true; } else isCMYK = false; if ( cinfo.output_components == 1 ) { QImage tmpImg = m_image.convertToFormat(QImage::Format_ARGB32); m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 ); QRgb *s; QRgb *d; for( int yi=0; yi < tmpImg.height(); ++yi ) { s = (QRgb*)(tmpImg.scanLine( yi )); d = (QRgb*)(m_image.scanLine( yi )); for(int xi=0; xi < tmpImg.width(); ++xi ) { (*d) = (*s); s++; d++; } } } } (void) jpeg_finish_decompress(&cinfo); fclose (infile); jpeg_destroy_decompress (&cinfo); m_imageInfoRecord.layerInfo.clear(); m_imageInfoRecord.BBoxX = 0; m_imageInfoRecord.BBoxH = m_image.height(); return (!m_image.isNull()); }
bool decode(const Vector<char>& data, bool sizeOnly) { m_decodingSizeOnly = sizeOnly; unsigned newByteCount = data.size() - m_bufferLength; unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer += newByteCount; m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset; // If we still have bytes to skip, try to skip those now. if (m_bytesToSkip) skipBytes(m_bytesToSkip); m_bufferLength = data.size(); // We need to do the setjmp here. Otherwise bad things will happen if (setjmp(m_err.setjmp_buffer)) { m_state = JPEG_SINK_NON_JPEG_TRAILER; close(); return false; } switch (m_state) { case JPEG_HEADER: { /* Read file parameters with jpeg_read_header() */ if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) return true; /* I/O suspension */ /* let libjpeg take care of gray->RGB and YCbCr->RGB conversions */ switch (m_info.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: m_info.out_color_space = JCS_RGB; break; case JCS_CMYK: case JCS_YCCK: default: m_state = JPEG_ERROR; return false; } /* * Don't allocate a giant and superfluous memory buffer * when the image is a sequential JPEG. */ m_info.buffered_image = jpeg_has_multiple_scans(&m_info); /* Used to set up image size so arrays can be allocated */ jpeg_calc_output_dimensions(&m_info); /* * Make a one-row-high sample array that will go away * when done with image. Always make it big enough to * hold an RGB row. Since this uses the IJG memory * manager, it must be allocated before the call to * jpeg_start_compress(). */ int row_stride = m_info.output_width * 4; // RGBA buffer m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, JPOOL_IMAGE, row_stride, 1); m_state = JPEG_START_DECOMPRESS; // We can fill in the size now that the header is available. if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) { m_state = JPEG_ERROR; return false; } if (m_decodingSizeOnly) { // We can stop here. // Reduce our buffer length and available data. m_bufferLength -= m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer = 0; return true; } } case JPEG_START_DECOMPRESS: { /* Set parameters for decompression */ /* FIXME -- Should reset dct_method and dither mode * for final pass of progressive JPEG */ m_info.dct_method = JDCT_ISLOW; m_info.dither_mode = JDITHER_FS; m_info.do_fancy_upsampling = true; m_info.enable_2pass_quant = false; m_info.do_block_smoothing = true; /* Start decompressor */ if (!jpeg_start_decompress(&m_info)) return true; /* I/O suspension */ /* If this is a progressive JPEG ... */ m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; } case JPEG_DECOMPRESS_SEQUENTIAL: { if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { if (!m_decoder->outputScanlines()) return true; /* I/O suspension */ /* If we've completed image output ... */ assert(m_info.output_scanline == m_info.output_height); m_state = JPEG_DONE; } } case JPEG_DECOMPRESS_PROGRESSIVE: { if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { int status; do { status = jpeg_consume_input(&m_info); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (m_info.output_scanline == 0) { int scan = m_info.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 ((m_info.output_scan_number == 0) && (scan > 1) && (status != JPEG_REACHED_EOI)) scan--; if (!jpeg_start_output(&m_info, scan)) return true; /* I/O suspension */ } if (m_info.output_scanline == 0xffffff) m_info.output_scanline = 0; if (!m_decoder->outputScanlines()) { if (m_info.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 */ m_info.output_scanline = 0xffffff; return true; /* I/O suspension */ } if (m_info.output_scanline == m_info.output_height) { if (!jpeg_finish_output(&m_info)) return true; /* I/O suspension */ if (jpeg_input_complete(&m_info) && (m_info.input_scan_number == m_info.output_scan_number)) break; m_info.output_scanline = 0; } } m_state = JPEG_DONE; } } case JPEG_DONE: { /* Finish decompression */ if (!jpeg_finish_decompress(&m_info)) return true; /* I/O suspension */ m_state = JPEG_SINK_NON_JPEG_TRAILER; /* we're done */ break; } case JPEG_SINK_NON_JPEG_TRAILER: break; case JPEG_ERROR: break; } return true; }
mng_retcode mngjpeg_decompressdata (mng_datap pData, mng_uint32 iRawsize, mng_uint8p pRawdata) { mng_retcode iRetcode; mng_uint32 iRemain; mng_uint8p pWork; #ifdef MNG_SUPPORT_TRACE MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_START) #endif #if defined (MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) iRetcode = setjmp (pData->sErrorbuf);/* initialize local JPEG error-recovery */ if (iRetcode != 0) /* got here from longjmp ? */ MNG_ERRORJ (pData, iRetcode) /* then IJG-lib issued an error */ #endif pWork = pRawdata; iRemain = iRawsize; if (pData->iJPEGtoskip) /* JPEG-lib told us to skip some more data ? */ { if (iRemain > pData->iJPEGtoskip) /* enough data in this buffer ? */ { iRemain -= pData->iJPEGtoskip; /* skip enough to access the next byte */ pWork += pData->iJPEGtoskip; pData->iJPEGtoskip = 0; /* no more to skip then */ } else { pData->iJPEGtoskip -= iRemain; /* skip all data in the buffer */ iRemain = 0; /* and indicate this accordingly */ } /* the skip set current-pointer to NULL ! */ pData->pJPEGcurrent = pData->pJPEGbuf; } while (iRemain) /* repeat until no more input-bytes */ { /* need to shift anything ? */ if ((pData->pJPEGcurrent > pData->pJPEGbuf) && (pData->pJPEGcurrent - pData->pJPEGbuf + pData->iJPEGbufremain + iRemain > pData->iJPEGbufmax)) { if (pData->iJPEGbufremain > 0) /* then do so */ MNG_COPY (pData->pJPEGbuf, pData->pJPEGcurrent, pData->iJPEGbufremain) pData->pJPEGcurrent = pData->pJPEGbuf; } /* does the remaining input fit into the buffer ? */ if (pData->iJPEGbufremain + iRemain <= pData->iJPEGbufmax) { /* move the lot */ MNG_COPY ((pData->pJPEGcurrent + pData->iJPEGbufremain), pWork, iRemain) pData->iJPEGbufremain += iRemain;/* adjust remaining_bytes counter */ iRemain = 0; /* and indicate there's no input left */ } else { /* calculate what does fit */ mng_uint32 iFits = pData->iJPEGbufmax - pData->iJPEGbufremain; if (iFits <= 0) /* no space is just bugger 'm all */ MNG_ERROR (pData, MNG_JPEGBUFTOOSMALL) /* move that */ MNG_COPY ((pData->pJPEGcurrent + pData->iJPEGbufremain), pWork, iFits) pData->iJPEGbufremain += iFits; /* adjust remain_bytes counter */ iRemain -= iFits; /* and the input-parms */ pWork += iFits; } #ifdef MNG_INCLUDE_IJG6B pData->pJPEGdinfo->src->next_input_byte = pData->pJPEGcurrent; pData->pJPEGdinfo->src->bytes_in_buffer = pData->iJPEGbufremain; if (!pData->bJPEGhasheader) /* haven't got the header yet ? */ { void *temp; /* call jpeg_read_header() to obtain image info */ #ifdef MNG_SUPPORT_TRACE MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_HEADER) #endif if (jpeg_read_header (pData->pJPEGdinfo, TRUE) != JPEG_SUSPENDED) { /* indicate the header's oke */ pData->bJPEGhasheader = MNG_TRUE; /* let's do some sanity checks ! */ if ((pData->pJPEGdinfo->image_width != pData->iDatawidth ) || (pData->pJPEGdinfo->image_height != pData->iDataheight) ) MNG_ERROR (pData, MNG_JPEGPARMSERR) if ( ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAY ) || (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA) ) && (pData->pJPEGdinfo->jpeg_color_space != JCS_GRAYSCALE ) ) MNG_ERROR (pData, MNG_JPEGPARMSERR) if ( ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLOR ) || (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) && (pData->pJPEGdinfo->jpeg_color_space != JCS_YCbCr ) ) MNG_ERROR (pData, MNG_JPEGPARMSERR) /* indicate whether or not it's progressive */ pData->bJPEGprogressive = (mng_bool)jpeg_has_multiple_scans (pData->pJPEGdinfo); /* progressive+alpha can't display "on-the-fly"!! */ if ((pData->bJPEGprogressive) && ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) )) pData->fDisplayrow = MNG_NULL; /* allocate a row of JPEG-samples */ if (pData->pJPEGdinfo->jpeg_color_space == JCS_YCbCr) pData->iJPEGrowlen = pData->pJPEGdinfo->image_width * 3; else pData->iJPEGrowlen = pData->pJPEGdinfo->image_width; MNG_ALLOC (pData, temp, pData->iJPEGrowlen) pData->pJPEGrow = (mng_uint8p)temp; pData->iJPEGrgbrow = 0; /* quite empty up to now */ } pData->pJPEGcurrent = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte; pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer; }
void nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount) { 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(gJPEGDecoderAccountingLog, 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(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("} (setjmp returned an error)")); return; } } PR_LOG(gJPEGlog, PR_LOG_DEBUG, ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); switch (mState) { case JPEG_HEADER: { LOG_SCOPE(gJPEGlog, "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(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("} (JPEG_SUSPENDED)")); return; /* I/O suspension */ } // Post our size to the superclass PostSize(mInfo.image_width, mInfo.image_height); 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(gJPEGDecoderAccountingLog, 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(gJPEGDecoderAccountingLog, 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: 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(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("} (unknown colorpsace (3))")); return; break; } } /* * Don't allocate a giant and superfluous memory buffer * when the image is a sequential JPEG. */ mInfo.buffered_image = jpeg_has_multiple_scans(&mInfo); /* Used to set up image size so arrays can be allocated */ jpeg_calc_output_dimensions(&mInfo); uint32_t imagelength; if (NS_FAILED(mImage.EnsureFrame(0, 0, 0, mInfo.image_width, mInfo.image_height, gfxASurface::ImageFormatRGB24, &mImageData, &imagelength))) { mState = JPEG_ERROR; PostDecoderError(NS_ERROR_OUT_OF_MEMORY); PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("} (could not initialize image frame)")); return; } PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, (" JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels", mInfo.image_width, mInfo.image_height)); // Tell the superclass we're starting a frame PostFrameStart(); mState = JPEG_START_DECOMPRESS; } case JPEG_START_DECOMPRESS: { LOG_SCOPE(gJPEGlog, "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(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("} (I/O suspension after jpeg_start_decompress())")); return; /* I/O suspension */ } /* Force to use our YCbCr to Packed RGB converter when possible */ if (!mTransform && (mCMSMode != eCMSMode_All) && mInfo.jpeg_color_space == JCS_YCbCr && mInfo.out_color_space == JCS_RGB) { /* Special case for the most common case: transform from YCbCr direct into packed ARGB */ mInfo.out_color_components = 4; /* Packed ARGB pixels are always 4 bytes...*/ mInfo.cconvert->color_convert = ycc_rgb_convert_argb; } /* 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(gJPEGlog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case"); bool suspend; OutputScanlines(&suspend); if (suspend) { PR_LOG(gJPEGDecoderAccountingLog, 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(gJPEGlog, "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(gJPEGDecoderAccountingLog, 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(gJPEGDecoderAccountingLog, 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(gJPEGDecoderAccountingLog, 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(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case"); /* Step 7: Finish decompression */ if (jpeg_finish_decompress(&mInfo) == FALSE) { PR_LOG(gJPEGDecoderAccountingLog, 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(gJPEGlog, 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(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("} (end of function)")); return; }
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to * do 255-pel. */ static int read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) { struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; jpeg_saved_marker_ptr p; VipsInterpretation interpretation; double xres, yres; /* Capture app2 sections here for assembly. */ void *app2_data[MAX_APP2_SECTIONS] = { 0 }; size_t app2_data_length[MAX_APP2_SECTIONS] = { 0 }; size_t data_length; int i; /* Read JPEG header. libjpeg will set out_color_space sanely for us * for YUV YCCK etc. */ jpeg_read_header( cinfo, TRUE ); cinfo->scale_denom = jpeg->shrink; cinfo->scale_num = 1; jpeg_calc_output_dimensions( cinfo ); jpeg->invert_pels = FALSE; switch( cinfo->out_color_space ) { case JCS_GRAYSCALE: interpretation = VIPS_INTERPRETATION_B_W; break; case JCS_CMYK: interpretation = VIPS_INTERPRETATION_CMYK; /* Photoshop writes CMYK JPEG inverted :-( Maybe this is a * way to spot photoshop CMYK JPGs. */ if( cinfo->saw_Adobe_marker ) jpeg->invert_pels = TRUE; break; case JCS_RGB: default: interpretation = VIPS_INTERPRETATION_sRGB; break; } /* Get the jfif resolution. exif may overwrite this later. */ xres = 1.0; yres = 1.0; if( cinfo->saw_JFIF_marker && cinfo->X_density != 1U && cinfo->Y_density != 1U ) { #ifdef DEBUG printf( "read_jpeg_header: seen jfif _density %d, %d\n", cinfo->X_density, cinfo->Y_density ); #endif /*DEBUG*/ switch( cinfo->density_unit ) { case 1: /* Pixels per inch. */ xres = cinfo->X_density / 25.4; yres = cinfo->Y_density / 25.4; break; case 2: /* Pixels per cm. */ xres = cinfo->X_density / 10.0; yres = cinfo->Y_density / 10.0; break; default: vips_warn( "VipsJpeg", "%s", _( "unknown JFIF resolution unit" ) ); break; } #ifdef DEBUG printf( "read_jpeg_header: seen jfif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ } /* Set VIPS header. */ vips_image_init_fields( out, cinfo->output_width, cinfo->output_height, cinfo->output_components, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, interpretation, xres, yres ); vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. */ (void) vips_image_set_int( out, "jpeg-multiscan", jpeg_has_multiple_scans( cinfo ) ); /* Look for EXIF and ICC profile. */ for( p = cinfo->marker_list; p; p = p->next ) { #ifdef DEBUG { printf( "read_jpeg_header: seen %d bytes of APP%d\n", p->data_length, p->marker - JPEG_APP0 ); for( i = 0; i < 10; i++ ) printf( "\t%d) '%c' (%d)\n", i, p->data[i], p->data[i] ); } #endif /*DEBUG*/ switch( p->marker ) { case JPEG_APP0 + 1: /* Possible EXIF or XMP data. */ if( p->data_length > 4 && vips_isprefix( "Exif", (char *) p->data ) ) { if( parse_exif( out, p->data, p->data_length ) || attach_blob( out, VIPS_META_EXIF_NAME, p->data, p->data_length ) ) return( -1 ); } if( p->data_length > 4 && vips_isprefix( "http", (char *) p->data ) && attach_blob( out, VIPS_META_XMP_NAME, p->data, p->data_length ) ) return( -1 ); break; case JPEG_APP0 + 2: /* Possible ICC profile. */ if( p->data_length > 14 && vips_isprefix( "ICC_PROFILE", (char *) p->data ) ) { /* cur_marker numbers from 1, according to * spec. */ int cur_marker = p->data[12] - 1; if( cur_marker >= 0 && cur_marker < MAX_APP2_SECTIONS ) { app2_data[cur_marker] = p->data + 14; app2_data_length[cur_marker] = p->data_length - 14; } } break; case JPEG_APP0 + 13: /* Possible IPCT data block. */ if( p->data_length > 5 && vips_isprefix( "Photo", (char *) p->data ) && attach_blob( out, VIPS_META_IPCT_NAME, p->data, p->data_length ) ) return( -1 ); break; default: break; } } /* Assemble ICC sections. */ data_length = 0; for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) data_length += app2_data_length[i]; if( data_length ) { unsigned char *data; int p; #ifdef DEBUG printf( "read_jpeg_header: assembled %zd byte ICC profile\n", data_length ); #endif /*DEBUG*/ if( !(data = vips_malloc( NULL, data_length )) ) return( -1 ); p = 0; for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) { memcpy( data + p, app2_data[i], app2_data_length[i] ); p += app2_data_length[i]; } vips_image_set_blob( out, VIPS_META_ICC_NAME, (VipsCallbackFn) vips_free, data, data_length ); } return( 0 ); }
CPLErr JPEG_Codec::DecompressJPEG(buf_mgr &dst, buf_mgr &isrc) #endif { int nbands = img.pagesize.c; // Locals, clean up after themselves jpeg_decompress_struct cinfo; MRFJPEGStruct sJPEGStruct; struct jpeg_error_mgr sJErr; BitMask mask(img.pagesize.x, img.pagesize.y); RLEC3Packer packer; mask.set_packer(&packer); memset(&cinfo, 0, sizeof(cinfo)); // Pass the mask address to the decompressor sJPEGStruct.mask = &mask; struct jpeg_source_mgr src; cinfo.err = jpeg_std_error( &sJErr ); sJErr.error_exit = errorExit; sJErr.emit_message = emitMessage; cinfo.client_data = (void *) &(sJPEGStruct); src.next_input_byte = (JOCTET *)isrc.buffer; src.bytes_in_buffer = isrc.size; src.term_source = stub_source_dec; src.init_source = stub_source_dec; src.skip_input_data = skip_input_data_dec; src.fill_input_buffer = fill_input_buffer_dec; src.resync_to_restart = jpeg_resync_to_restart; jpeg_create_decompress(&cinfo); if (setjmp(sJPEGStruct.setjmpBuffer)) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: Error reading JPEG page"); jpeg_destroy_decompress(&cinfo); return CE_Failure; } cinfo.src = &src; jpeg_set_marker_processor(&cinfo, JPEG_APP0 + 3, MaskProcessor); jpeg_read_header(&cinfo, TRUE); /* In some cases, libjpeg needs to allocate a lot of memory */ /* http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf */ if( jpeg_has_multiple_scans(&(cinfo)) ) { /* In this case libjpeg will need to allocate memory or backing */ /* store for all coefficients */ /* See call to jinit_d_coef_controller() from master_selection() */ /* in libjpeg */ vsi_l_offset nRequiredMemory = static_cast<vsi_l_offset>(cinfo.image_width) * cinfo.image_height * cinfo.num_components * ((cinfo.data_precision+7)/8); /* BLOCK_SMOOTHING_SUPPORTED is generally defined, so we need */ /* to replicate the logic of jinit_d_coef_controller() */ if( cinfo.progressive_mode ) nRequiredMemory *= 3; #ifndef GDAL_LIBJPEG_LARGEST_MEM_ALLOC #define GDAL_LIBJPEG_LARGEST_MEM_ALLOC (100 * 1024 * 1024) #endif if( nRequiredMemory > GDAL_LIBJPEG_LARGEST_MEM_ALLOC && CPLGetConfigOption("GDAL_ALLOW_LARGE_LIBJPEG_MEM_ALLOC", nullptr) == nullptr ) { CPLError(CE_Failure, CPLE_NotSupported, "Reading this image would require libjpeg to allocate " "at least " CPL_FRMT_GUIB " bytes. " "This is disabled since above the " CPL_FRMT_GUIB " threshold. " "You may override this restriction by defining the " "GDAL_ALLOW_LARGE_LIBJPEG_MEM_ALLOC environment variable, " "or recompile GDAL by defining the " "GDAL_LIBJPEG_LARGEST_MEM_ALLOC macro to a value greater " "than " CPL_FRMT_GUIB, static_cast<GUIntBig>(nRequiredMemory), static_cast<GUIntBig>(GDAL_LIBJPEG_LARGEST_MEM_ALLOC), static_cast<GUIntBig>(GDAL_LIBJPEG_LARGEST_MEM_ALLOC)); jpeg_destroy_decompress(&cinfo); return CE_Failure; } } // Use float, it is actually faster than the ISLOW method by a tiny bit cinfo.dct_method = JDCT_FLOAT; // // Tolerate different input if we can do the conversion // Gray and RGB for example // This also means that a RGB MRF can be read as grayscale and vice versa // If libJPEG can't convert it will throw an error // if (nbands == 3 && cinfo.num_components != nbands) cinfo.out_color_space = JCS_RGB; if (nbands == 1 && cinfo.num_components != nbands) cinfo.out_color_space = JCS_GRAYSCALE; const int datasize = ((cinfo.data_precision == 8) ? 1 : 2); if( cinfo.image_width > static_cast<unsigned>(INT_MAX / (nbands * datasize)) ) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG decompress buffer overflow"); jpeg_destroy_decompress(&cinfo); return CE_Failure; } int linesize = cinfo.image_width * nbands * datasize; // We have a mismatch between the real and the declared data format // warn and fail if output buffer is too small if (linesize > static_cast<int>(INT_MAX / cinfo.image_height)) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG decompress buffer overflow"); jpeg_destroy_decompress(&cinfo); return CE_Failure; } if (linesize*cinfo.image_height != dst.size) { CPLError(CE_Warning, CPLE_AppDefined, "MRF: read JPEG size is wrong"); if (linesize*cinfo.image_height > dst.size) { CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG decompress buffer overflow"); jpeg_destroy_decompress(&cinfo); return CE_Failure; } } struct jpeg_progress_mgr sJProgress; sJProgress.progress_monitor = ProgressMonitor; cinfo.progress = &sJProgress; jpeg_start_decompress(&cinfo); // Decompress, two lines at a time is what libjpeg does while (cinfo.output_scanline < cinfo.image_height) { char *rp[2]; rp[0] = (char *)dst.buffer + linesize*cinfo.output_scanline; rp[1] = rp[0] + linesize; // if this fails, it calls the error handler // which will report an error if( jpeg_read_scanlines(&cinfo, JSAMPARRAY(rp), 2) == 0 ) { jpeg_destroy_decompress(&cinfo); return CE_Failure; } } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); // Apply the mask if (datasize == 1) apply_mask(sJPEGStruct, reinterpret_cast<char *>(dst.buffer), img.pagesize.c); else apply_mask(sJPEGStruct, reinterpret_cast<GUInt16 *>(dst.buffer), img.pagesize.c); return CE_None; }
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to * do 255-pel. */ static int read_jpeg_header( struct jpeg_decompress_struct *cinfo, IMAGE *out, gboolean *invert_pels, int shrink ) { jpeg_saved_marker_ptr p; int type; /* Capture app2 sections here for assembly. */ void *app2_data[MAX_APP2_SECTIONS] = { 0 }; int app2_data_length[MAX_APP2_SECTIONS] = { 0 }; int data_length; int i; /* Read JPEG header. libjpeg will set out_color_space sanely for us * for YUV YCCK etc. */ jpeg_read_header( cinfo, TRUE ); cinfo->scale_denom = shrink; cinfo->scale_num = 1; jpeg_calc_output_dimensions( cinfo ); *invert_pels = FALSE; switch( cinfo->out_color_space ) { case JCS_GRAYSCALE: type = IM_TYPE_B_W; break; case JCS_CMYK: type = IM_TYPE_CMYK; /* Photoshop writes CMYK JPEG inverted :-( Maybe this is a * way to spot photoshop CMYK JPGs. */ if( cinfo->saw_Adobe_marker ) *invert_pels = TRUE; break; case JCS_RGB: default: type = IM_TYPE_sRGB; break; } /* Set VIPS header. */ im_initdesc( out, cinfo->output_width, cinfo->output_height, cinfo->output_components, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, type, 1.0, 1.0, 0, 0 ); /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. */ (void) im_meta_set_int( out, "jpeg-multiscan", jpeg_has_multiple_scans( cinfo ) ); /* Look for EXIF and ICC profile. */ for( p = cinfo->marker_list; p; p = p->next ) { switch( p->marker ) { case JPEG_APP0 + 1: /* EXIF data. */ #ifdef DEBUG printf( "read_jpeg_header: seen %d bytes of APP1\n", p->data_length ); #endif /*DEBUG*/ if( read_exif( out, p->data, p->data_length ) ) return( -1 ); break; case JPEG_APP0 + 2: /* ICC profile. */ #ifdef DEBUG printf( "read_jpeg_header: seen %d bytes of APP2\n", p->data_length ); #endif /*DEBUG*/ if( p->data_length > 14 && im_isprefix( "ICC_PROFILE", (char *) p->data ) ) { /* cur_marker numbers from 1, according to * spec. */ int cur_marker = p->data[12] - 1; if( cur_marker >= 0 && cur_marker < MAX_APP2_SECTIONS ) { app2_data[cur_marker] = p->data + 14; app2_data_length[cur_marker] = p->data_length - 14; } } break; default: #ifdef DEBUG printf( "read_jpeg_header: seen %d bytes of data\n", p->data_length ); #endif /*DEBUG*/ break; } } /* Assemble ICC sections. */ data_length = 0; for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) data_length += app2_data_length[i]; if( data_length ) { unsigned char *data; int p; #ifdef DEBUG printf( "read_jpeg_header: assembled %d byte ICC profile\n", data_length ); #endif /*DEBUG*/ if( !(data = im_malloc( NULL, data_length )) ) return( -1 ); p = 0; for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) { memcpy( data + p, app2_data[i], app2_data_length[i] ); p += app2_data_length[i]; } if( im_meta_set_blob( out, IM_META_ICC_NAME, (im_callback_fn) im_free, data, data_length ) ) { im_free( data ); return( -1 ); } } return( 0 ); }