| Index: src/images/SkImageDecoder_libjpeg.cpp
|
| diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
|
| index 914bdea5c8b8a96f9e4b0117900831d3c797eec0..c80cd9b64f04a4a629bf3e82e346c16cac4d9d3d 100644
|
| --- a/src/images/SkImageDecoder_libjpeg.cpp
|
| +++ b/src/images/SkImageDecoder_libjpeg.cpp
|
| @@ -85,154 +85,20 @@ static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* sr
|
| }
|
| }
|
|
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| -class SkJPEGImageIndex {
|
| -public:
|
| - // Takes ownership of stream.
|
| - SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
|
| - : fSrcMgr(stream, decoder)
|
| - , fStream(stream)
|
| - , fInfoInitialized(false)
|
| - , fHuffmanCreated(false)
|
| - , fDecompressStarted(false)
|
| - {
|
| - SkDEBUGCODE(fReadHeaderSucceeded = false;)
|
| - }
|
| -
|
| - ~SkJPEGImageIndex() {
|
| - if (fHuffmanCreated) {
|
| - // Set to false before calling the libjpeg function, in case
|
| - // the libjpeg function calls longjmp. Our setjmp handler may
|
| - // attempt to delete this SkJPEGImageIndex, thus entering this
|
| - // destructor again. Setting fHuffmanCreated to false first
|
| - // prevents an infinite loop.
|
| - fHuffmanCreated = false;
|
| - jpeg_destroy_huffman_index(&fHuffmanIndex);
|
| - }
|
| - if (fDecompressStarted) {
|
| - // Like fHuffmanCreated, set to false before calling libjpeg
|
| - // function to prevent potential infinite loop.
|
| - fDecompressStarted = false;
|
| - jpeg_finish_decompress(&fCInfo);
|
| - }
|
| - if (fInfoInitialized) {
|
| - this->destroyInfo();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Destroy the cinfo struct.
|
| - * After this call, if a huffman index was already built, it
|
| - * can be used after calling initializeInfoAndReadHeader
|
| - * again. Must not be called after startTileDecompress except
|
| - * in the destructor.
|
| - */
|
| - void destroyInfo() {
|
| - SkASSERT(fInfoInitialized);
|
| - SkASSERT(!fDecompressStarted);
|
| - // Like fHuffmanCreated, set to false before calling libjpeg
|
| - // function to prevent potential infinite loop.
|
| - fInfoInitialized = false;
|
| - jpeg_destroy_decompress(&fCInfo);
|
| - SkDEBUGCODE(fReadHeaderSucceeded = false;)
|
| - }
|
| -
|
| - /**
|
| - * Initialize the cinfo struct.
|
| - * Calls jpeg_create_decompress, makes customizations, and
|
| - * finally calls jpeg_read_header. Returns true if jpeg_read_header
|
| - * returns JPEG_HEADER_OK.
|
| - * If cinfo was already initialized, destroyInfo must be called to
|
| - * destroy the old one. Must not be called after startTileDecompress.
|
| - */
|
| - bool initializeInfoAndReadHeader() {
|
| - SkASSERT(!fInfoInitialized && !fDecompressStarted);
|
| - initialize_info(&fCInfo, &fSrcMgr);
|
| - fInfoInitialized = true;
|
| - const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
|
| - SkDEBUGCODE(fReadHeaderSucceeded = success;)
|
| - return success;
|
| - }
|
| -
|
| - jpeg_decompress_struct* cinfo() { return &fCInfo; }
|
| -
|
| - huffman_index* huffmanIndex() { return &fHuffmanIndex; }
|
| -
|
| - /**
|
| - * Build the index to be used for tile based decoding.
|
| - * Must only be called after a successful call to
|
| - * initializeInfoAndReadHeader and must not be called more
|
| - * than once.
|
| - */
|
| - bool buildHuffmanIndex() {
|
| - SkASSERT(fReadHeaderSucceeded);
|
| - SkASSERT(!fHuffmanCreated);
|
| - jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
|
| - SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
|
| - fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
|
| - return fHuffmanCreated;
|
| - }
|
| -
|
| - /**
|
| - * Start tile based decoding. Must only be called after a
|
| - * successful call to buildHuffmanIndex, and must only be
|
| - * called once.
|
| - */
|
| - bool startTileDecompress() {
|
| - SkASSERT(fHuffmanCreated);
|
| - SkASSERT(fReadHeaderSucceeded);
|
| - SkASSERT(!fDecompressStarted);
|
| - if (jpeg_start_tile_decompress(&fCInfo)) {
|
| - fDecompressStarted = true;
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| -private:
|
| - skjpeg_source_mgr fSrcMgr;
|
| - SkAutoTDelete<SkStream> fStream;
|
| - jpeg_decompress_struct fCInfo;
|
| - huffman_index fHuffmanIndex;
|
| - bool fInfoInitialized;
|
| - bool fHuffmanCreated;
|
| - bool fDecompressStarted;
|
| - SkDEBUGCODE(bool fReadHeaderSucceeded;)
|
| -};
|
| -#endif
|
| -
|
| class SkJPEGImageDecoder : public SkImageDecoder {
|
| public:
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| - SkJPEGImageDecoder() {
|
| - fImageIndex = nullptr;
|
| - fImageWidth = 0;
|
| - fImageHeight = 0;
|
| - }
|
| -
|
| - virtual ~SkJPEGImageDecoder() { delete fImageIndex; }
|
| -#endif
|
|
|
| Format getFormat() const override {
|
| return kJPEG_Format;
|
| }
|
|
|
| protected:
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| - bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
|
| - bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
|
| -#endif
|
| Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
|
| bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
|
| void* planes[3], size_t rowBytes[3],
|
| SkYUVColorSpace* colorSpace) override;
|
|
|
| private:
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| - SkJPEGImageIndex* fImageIndex;
|
| - int fImageWidth;
|
| - int fImageHeight;
|
| -#endif
|
|
|
| /**
|
| * Determine the appropriate bitmap colortype and out_color_space based on
|
| @@ -295,20 +161,6 @@ static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count
|
| return true;
|
| }
|
|
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| -static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
|
| - huffman_index *index, void* buffer, int count) {
|
| - for (int i = 0; i < count; i++) {
|
| - JSAMPLE* rowptr = (JSAMPLE*)buffer;
|
| - int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
|
| - if (1 != row_count) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -#endif
|
| -
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| // This guy exists just to aid in debugging, as it allows debuggers to just
|
| @@ -329,14 +181,6 @@ static bool return_false(const jpeg_decompress_struct& cinfo,
|
| return false;
|
| }
|
|
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| -static bool return_false(const jpeg_decompress_struct& cinfo,
|
| - const SkBitmap& bm, const char caller[]) {
|
| - print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
|
| - return false;
|
| -}
|
| -#endif
|
| -
|
| static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
|
| const SkBitmap& bm, const char caller[]) {
|
| print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
|
| @@ -939,240 +783,6 @@ bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentS
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| -#ifdef SK_JPEG_INDEX_SUPPORTED
|
| -bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
|
| - SkAutoTDelete<SkJPEGImageIndex> imageIndex(new SkJPEGImageIndex(stream, this));
|
| -
|
| - skjpeg_error_mgr sk_err;
|
| - set_error_mgr(imageIndex->cinfo(), &sk_err);
|
| -
|
| - // All objects need to be instantiated before this setjmp call so that
|
| - // they will be cleaned up properly if an error occurs.
|
| - if (setjmp(sk_err.fJmpBuf)) {
|
| - return false;
|
| - }
|
| -
|
| - // create the cinfo used to create/build the huffmanIndex
|
| - if (!imageIndex->initializeInfoAndReadHeader()) {
|
| - return false;
|
| - }
|
| -
|
| - if (!imageIndex->buildHuffmanIndex()) {
|
| - return false;
|
| - }
|
| -
|
| - // destroy the cinfo used to create/build the huffman index
|
| - imageIndex->destroyInfo();
|
| -
|
| - // Init decoder to image decode mode
|
| - if (!imageIndex->initializeInfoAndReadHeader()) {
|
| - return false;
|
| - }
|
| -
|
| - jpeg_decompress_struct* cinfo = imageIndex->cinfo();
|
| - // We have a new cinfo, so set the error mgr again.
|
| - set_error_mgr(cinfo, &sk_err);
|
| -
|
| - // FIXME: This sets cinfo->out_color_space, which we may change later
|
| - // based on the config in onDecodeSubset. This should be fine, since
|
| - // jpeg_init_read_tile_scanline will check out_color_space again after
|
| - // that change (when it calls jinit_color_deconverter).
|
| - (void) this->getBitmapColorType(cinfo);
|
| -
|
| - turn_off_visual_optimizations(cinfo);
|
| -
|
| - // instead of jpeg_start_decompress() we start a tiled decompress
|
| - if (!imageIndex->startTileDecompress()) {
|
| - return false;
|
| - }
|
| -
|
| - SkASSERT(1 == cinfo->scale_num);
|
| - fImageWidth = cinfo->output_width;
|
| - fImageHeight = cinfo->output_height;
|
| -
|
| - if (width) {
|
| - *width = fImageWidth;
|
| - }
|
| - if (height) {
|
| - *height = fImageHeight;
|
| - }
|
| -
|
| - delete fImageIndex;
|
| - fImageIndex = imageIndex.detach();
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
|
| - if (nullptr == fImageIndex) {
|
| - return false;
|
| - }
|
| - jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
|
| -
|
| - SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
|
| - if (!rect.intersect(region)) {
|
| - // If the requested region is entirely outside the image return false
|
| - return false;
|
| - }
|
| -
|
| -
|
| - skjpeg_error_mgr errorManager;
|
| - set_error_mgr(cinfo, &errorManager);
|
| -
|
| - if (setjmp(errorManager.fJmpBuf)) {
|
| - return false;
|
| - }
|
| -
|
| - int requestedSampleSize = this->getSampleSize();
|
| - cinfo->scale_denom = requestedSampleSize;
|
| -
|
| - set_dct_method(*this, cinfo);
|
| -
|
| - const SkColorType colorType = this->getBitmapColorType(cinfo);
|
| - adjust_out_color_space_and_dither(cinfo, colorType, *this);
|
| -
|
| - int startX = rect.fLeft;
|
| - int startY = rect.fTop;
|
| - int width = rect.width();
|
| - int height = rect.height();
|
| -
|
| - jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
|
| - &startX, &startY, &width, &height);
|
| - int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
|
| - int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
|
| -
|
| - SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
|
| -
|
| - SkBitmap bitmap;
|
| - // Assume an A8 bitmap is not opaque to avoid the check of each
|
| - // individual pixel. It is very unlikely to be opaque, since
|
| - // an opaque A8 bitmap would not be very interesting.
|
| - // Otherwise, a jpeg image is opaque.
|
| - bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
|
| - kAlpha_8_SkColorType == colorType ?
|
| - kPremul_SkAlphaType : kOpaque_SkAlphaType));
|
| -
|
| - // Check ahead of time if the swap(dest, src) is possible or not.
|
| - // If yes, then we will stick to AllocPixelRef since it's cheaper with the
|
| - // swap happening. If no, then we will use alloc to allocate pixels to
|
| - // prevent garbage collection.
|
| - int w = rect.width() / actualSampleSize;
|
| - int h = rect.height() / actualSampleSize;
|
| - bool swapOnly = (rect == region) && bm->isNull() &&
|
| - (w == bitmap.width()) && (h == bitmap.height()) &&
|
| - ((startX - rect.x()) / actualSampleSize == 0) &&
|
| - ((startY - rect.y()) / actualSampleSize == 0);
|
| - if (swapOnly) {
|
| - if (!this->allocPixelRef(&bitmap, nullptr)) {
|
| - return return_false(*cinfo, bitmap, "allocPixelRef");
|
| - }
|
| - } else {
|
| - if (!bitmap.tryAllocPixels()) {
|
| - return return_false(*cinfo, bitmap, "allocPixels");
|
| - }
|
| - }
|
| -
|
| - SkAutoLockPixels alp(bitmap);
|
| -
|
| -#ifdef ANDROID_RGB
|
| - /* short-circuit the SkScaledBitmapSampler when possible, as this gives
|
| - a significant performance boost.
|
| - */
|
| - if (skiaSampleSize == 1 &&
|
| - ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
|
| - (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
|
| - {
|
| - JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
|
| - INT32 const bpr = bitmap.rowBytes();
|
| - int rowTotalCount = 0;
|
| -
|
| - while (rowTotalCount < height) {
|
| - int rowCount = jpeg_read_tile_scanline(cinfo,
|
| - fImageIndex->huffmanIndex(),
|
| - &rowptr);
|
| - // if rowCount == 0, then we didn't get a scanline, so abort.
|
| - // onDecodeSubset() relies on onBuildTileIndex(), which
|
| - // needs a complete image to succeed.
|
| - if (0 == rowCount) {
|
| - return return_false(*cinfo, bitmap, "read_scanlines");
|
| - }
|
| - if (this->shouldCancelDecode()) {
|
| - return return_false(*cinfo, bitmap, "shouldCancelDecode");
|
| - }
|
| - rowTotalCount += rowCount;
|
| - rowptr += bpr;
|
| - }
|
| -
|
| - if (swapOnly) {
|
| - bm->swap(bitmap);
|
| - return true;
|
| - }
|
| -
|
| - return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
|
| - region.width(), region.height(), startX, startY);
|
| - }
|
| -#endif
|
| -
|
| - // check for supported formats
|
| - SkScaledBitmapSampler::SrcConfig sc;
|
| - int srcBytesPerPixel;
|
| -
|
| - if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
|
| - return return_false(*cinfo, *bm, "jpeg colorspace");
|
| - }
|
| -
|
| - if (!sampler.begin(&bitmap, sc, *this)) {
|
| - return return_false(*cinfo, bitmap, "sampler.begin");
|
| - }
|
| -
|
| - SkAutoMalloc srcStorage(width * srcBytesPerPixel);
|
| - uint8_t* srcRow = (uint8_t*)srcStorage.get();
|
| -
|
| - // Possibly skip initial rows [sampler.srcY0]
|
| - if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
|
| - return return_false(*cinfo, bitmap, "skip rows");
|
| - }
|
| -
|
| - // now loop through scanlines until y == bitmap->height() - 1
|
| - for (int y = 0;; y++) {
|
| - JSAMPLE* rowptr = (JSAMPLE*)srcRow;
|
| - int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
|
| - // if row_count == 0, then we didn't get a scanline, so abort.
|
| - // onDecodeSubset() relies on onBuildTileIndex(), which
|
| - // needs a complete image to succeed.
|
| - if (0 == row_count) {
|
| - return return_false(*cinfo, bitmap, "read_scanlines");
|
| - }
|
| - if (this->shouldCancelDecode()) {
|
| - return return_false(*cinfo, bitmap, "shouldCancelDecode");
|
| - }
|
| -
|
| - if (JCS_CMYK == cinfo->out_color_space) {
|
| - convert_CMYK_to_RGB(srcRow, width);
|
| - }
|
| -
|
| - sampler.next(srcRow);
|
| - if (bitmap.height() - 1 == y) {
|
| - // we're done
|
| - break;
|
| - }
|
| -
|
| - if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
|
| - sampler.srcDY() - 1)) {
|
| - return return_false(*cinfo, bitmap, "skip rows");
|
| - }
|
| - }
|
| - if (swapOnly) {
|
| - bm->swap(bitmap);
|
| - return true;
|
| - }
|
| - return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
|
| - region.width(), region.height(), startX, startY);
|
| -}
|
| -#endif
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| #include "SkColorPriv.h"
|
|
|
| // taken from jcolor.c in libjpeg
|
|
|