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 |