示例#1
0
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;
}
示例#2
0
    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;
    }
示例#3
0
    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;
    }
示例#4
0
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();
}
示例#5
0
文件: jpeg.c 项目: fredollinger/dillo
/*
 * 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;
    }
示例#8
0
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;
    }
示例#9
0
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;
}
示例#10
0
文件: jpeg2vips.c 项目: jieah/libvips
/* 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 );
}
示例#11
0
文件: JPEG_band.cpp 项目: hdfeos/gdal
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;
}
示例#12
0
/* 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 );
}