From 943e1fde4c6ecf0eb8998cd86012caa341a02ccf Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Tue, 15 May 2012 15:00:12 -0700 Subject: Add support for reusing bitmap in webp format. Also relax the constraint of the requested region. Now, we can get a region that is partially outsides the image. bug:5884845 Change-Id: Ibf455d176653ef1cf36924d685df1920d23cb542 --- src/images/SkImageDecoder.cpp | 11 ----- src/images/SkImageDecoder_libjpeg.cpp | 83 +++++++++++++++++++++-------------- src/images/SkImageDecoder_libpng.cpp | 43 ++++++++++-------- src/images/SkImageDecoder_libwebp.cpp | 78 ++++++++++++++++++++++++++------ 4 files changed, 140 insertions(+), 75 deletions(-) (limited to 'src') diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp index 23dec76..c4eac9c 100644 --- a/src/images/SkImageDecoder.cpp +++ b/src/images/SkImageDecoder.cpp @@ -195,19 +195,8 @@ void SkImageDecoder::cropBitmap(SkBitmap *dest, SkBitmap *src, int width, int height, int srcX, int srcY) { int w = width / sampleSize; int h = height / sampleSize; - if (w == src->width() && h == src->height() && - (srcX - destX) / sampleSize == 0 && - (srcY - destY) / sampleSize == 0 && - dest->isNull() - ) { - // The output rect is the same as the decode result - dest->swap(*src); - return; - } - // if the destination has no pixels then we must allocate them. if (dest->isNull()) { - // The output rect is smaller than the decode result dest->setConfig(src->getConfig(), w, h); dest->setIsOpaque(src->isOpaque()); diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index b191848..fbb6887 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -77,6 +77,8 @@ protected: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); private: SkJPEGImageIndex *index; + int imageWidth; + int imageHeight; }; ////////////////////////////////////////////////////////////////////////// @@ -528,7 +530,8 @@ bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, index->cinfo = cinfo; *height = cinfo->output_height; *width = cinfo->output_width; - + this->imageWidth = *width; + this->imageHeight = *height; this->index = index; return true; } @@ -537,11 +540,14 @@ bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { if (index == NULL) { return false; } - int startX = region.fLeft; - int startY = region.fTop; - int width = region.width(); - int height = region.height(); jpeg_decompress_struct *cinfo = index->cinfo; + + SkIRect rect = SkIRect::MakeWH(this->imageWidth, this->imageHeight); + if (!rect.intersect(region)) { + // If the requested region is entirely outsides the image, just + // returns false + return false; + } SkAutoMalloc srcStorage; skjpeg_error_mgr sk_err; cinfo->err = jpeg_std_error(&sk_err); @@ -579,11 +585,11 @@ bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { } } #endif + int startX = rect.fLeft; + int startY = rect.fTop; + int width = rect.width(); + int height = rect.height(); - int oriStartX = startX; - int oriStartY = startY; - int oriWidth = width; - int oriHeight = height; jpeg_init_read_tile_scanline(cinfo, index->index, &startX, &startY, &width, &height); int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo); @@ -604,22 +610,25 @@ bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { { bitmap->setConfig(config, cinfo->output_width, height); bitmap->setIsOpaque(true); - // Check ahead of time if the swap(dest, src) is possible in crop or - // not. If yes, then we will stick to AllocPixelRef since it's cheaper + + // 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 = oriWidth / actualSampleSize; - int h = oriHeight / actualSampleSize; - if (w == bitmap->width() && h == bitmap->height() && - (startX - oriStartX) / actualSampleSize == 0 && - (startY - oriStartY) / actualSampleSize == 0 && bm->isNull()) { - // Not using a recycled-bitmap and the output rect is same as the - // decoded region. + // + // Not using a recycled-bitmap and the output rect is same as the + // decoded region. + 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, NULL)) { return return_false(*cinfo, *bitmap, "allocPixelRef"); } - } - else { + } else { if (!bitmap->allocPixels()) { return return_false(*cinfo, *bitmap, "allocPixels"); } @@ -644,8 +653,13 @@ bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { row_total_count += row_count; rowptr += bpr; } - cropBitmap(bm, bitmap, actualSampleSize, oriStartX, oriStartY, - oriWidth, oriHeight, startX, startY); + + if (swapOnly) { + bm->swap(*bitmap); + } else { + cropBitmap(bm, bitmap, actualSampleSize, region.x(), region.y(), + region.width(), region.height(), startX, startY); + } return true; } #endif @@ -671,20 +685,21 @@ bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { bitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); bitmap->setIsOpaque(true); - // Check ahead of time if the swap(dest, src) is possible in crop or not. + // 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 = oriWidth / actualSampleSize; - int h = oriHeight / actualSampleSize; - if (w == bitmap->width() && h == bitmap->height() && - (startX - oriStartX) / actualSampleSize == 0 && - (startY - oriStartY) / actualSampleSize == 0 && bm->isNull()) { + 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, NULL)) { return return_false(*cinfo, *bitmap, "allocPixelRef"); } - } - else { + } else { if (!bitmap->allocPixels()) { return return_false(*cinfo, *bitmap, "allocPixels"); } @@ -724,8 +739,12 @@ bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { return return_false(*cinfo, *bitmap, "skip rows"); } } - cropBitmap(bm, bitmap, actualSampleSize, oriStartX, oriStartY, - oriWidth, oriHeight, startX, startY); + if (swapOnly) { + bm->swap(*bitmap); + } else { + cropBitmap(bm, bitmap, actualSampleSize, region.x(), region.y(), + region.width(), region.height(), startX, startY); + } return true; } diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp index bb31806..fa35239 100644 --- a/src/images/SkImageDecoder_libpng.cpp +++ b/src/images/SkImageDecoder_libpng.cpp @@ -58,7 +58,7 @@ public: protected: virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height); - virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect); + virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect region); virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); private: @@ -616,7 +616,7 @@ bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr, return true; } -bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { +bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) { int i; png_structp png_ptr = this->index->png_ptr; png_infop info_ptr = this->index->info_ptr; @@ -624,14 +624,19 @@ bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { return false; } - int requestedHeight = rect.fBottom - rect.fTop; - int requestedWidth = rect.fRight - rect.fLeft; - png_uint_32 origWidth, origHeight; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); + SkIRect rect = SkIRect::MakeWH(origWidth, origHeight); + + if (!rect.intersect(region)) { + // If the requested region is entirely outsides the image, just + // returns false + return false; + } + SkBitmap::Config config; bool hasAlpha = false; bool doDither = this->getDitherImage(); @@ -643,7 +648,7 @@ bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { } const int sampleSize = this->getSampleSize(); - SkScaledBitmapSampler sampler(origWidth, requestedHeight, sampleSize); + SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize); SkBitmap *decodedBitmap = new SkBitmap; SkAutoTDelete adb(decodedBitmap); @@ -669,17 +674,17 @@ bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { // Check ahead of time if the swap(dest, src) is possible in crop 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 = requestedWidth / sampleSize; - int h = requestedHeight / sampleSize; - if (w == decodedBitmap->width() && h == decodedBitmap->height() && - (0 - rect.fLeft / sampleSize) == 0 && (rect.fTop - rect.fTop) / sampleSize == 0 && - bm->isNull()) { + int w = rect.width() / sampleSize; + int h = rect.height() / sampleSize; + bool swapOnly = (rect == region) && (w == decodedBitmap->width()) && + (h == decodedBitmap->height()) && + ((0 - rect.x()) / sampleSize == 0) && bm->isNull(); + if (swapOnly) { if (!this->allocPixelRef(decodedBitmap, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) { return false; } - } - else { + } else { if (!decodedBitmap->allocPixels( NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) { return false; @@ -706,8 +711,6 @@ bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { png_ptr->pass = 0; png_read_update_info(png_ptr, info_ptr); - // SkDebugf("Request size %d %d\n", requestedWidth, requestedHeight); - int actualTop = rect.fTop; if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { @@ -757,7 +760,7 @@ bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); } uint8_t* row = base; - for (png_uint_32 y = 0; y < requestedHeight; y++) { + for (png_uint_32 y = 0; y < rect.height(); y++) { uint8_t* bmRow = row; png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); row += rb; @@ -790,8 +793,12 @@ bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) { } } } - cropBitmap(bm, decodedBitmap, sampleSize, rect.fLeft, rect.fTop, - requestedWidth, requestedHeight, 0, rect.fTop); + if (swapOnly) { + bm->swap(*decodedBitmap); + } else { + cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(), + region.width(), region.height(), 0, rect.y()); + } if (0 != theTranspColor) { reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor); diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp index c2fce45..3e416cc 100644 --- a/src/images/SkImageDecoder_libwebp.cpp +++ b/src/images/SkImageDecoder_libwebp.cpp @@ -21,6 +21,7 @@ #include "SkStream.h" #include "SkTemplates.h" #include "SkUtils.h" +#include "SkTScopedPtr.h" // A WebP decoder only, on top of (subset of) libwebp // For more information on WebP image format, and libwebp library, see: @@ -151,7 +152,7 @@ static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap) { // Incremental WebP image decoding. Reads input buffer of 64K size iteratively // and decodes this block to appropriate color-space as per config object. static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) { - WebPIDecoder* idec = WebPIDecode(NULL, NULL, &config); + WebPIDecoder* idec = WebPIDecode(NULL, 0, &config); if (idec == NULL) { WebPFreeDecBuffer(&config.output); return false; @@ -309,33 +310,82 @@ bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream, return true; } +static bool isConfigCompatiable(SkBitmap* bitmap) { + SkBitmap::Config config = bitmap->config(); + return config == SkBitmap::kARGB_4444_Config || + config == SkBitmap::kRGB_565_Config || + config == SkBitmap::kARGB_8888_Config; +} + bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap, SkIRect region) { - const int width = region.width(); - const int height = region.height(); - - const int sampleSize = this->getSampleSize(); - SkScaledBitmapSampler sampler(width, height, sampleSize); + SkIRect rect = SkIRect::MakeWH(origWidth, origHeight); - if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), - sampler.scaledHeight())) { + if (!rect.intersect(region)) { + // If the requested region is entirely outsides the image, just + // returns false return false; } - if (!this->allocPixelRef(decodedBitmap, NULL)) { - return return_false(*decodedBitmap, "allocPixelRef"); + const int sampleSize = this->getSampleSize(); + SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize); + const int width = sampler.scaledWidth(); + const int height = sampler.scaledHeight(); + + // The image can be decoded directly to decodedBitmap if + // 1. the region is within the image range + // 2. bitmap's config is compatible + // 3. bitmap's size is same as the required region (after sampled) + bool directDecode = (rect == region) && + (decodedBitmap->isNull() || + isConfigCompatiable(decodedBitmap) && + (decodedBitmap->width() == width) && + (decodedBitmap->height() == height)); + SkTScopedPtr adb; + SkBitmap *bitmap = decodedBitmap; + + if (!directDecode) { + // allocates a temp bitmap + bitmap = new SkBitmap; + adb.reset(bitmap); + } + + if (bitmap->isNull()) { + if (!setDecodeConfig(bitmap, width, height)) { + return false; + } + // alloc from native heap if it is a temp bitmap. (prevent GC) + bool allocResult = (bitmap == decodedBitmap) + ? allocPixelRef(bitmap, NULL) + : bitmap->allocPixels(); + if (!allocResult) { + return return_false(*decodedBitmap, "allocPixelRef"); + } + } else { + // This is also called in setDecodeConfig in above block. + // i.e., when bitmap->isNull() is true. + if (!chooseFromOneChoice(bitmap->config(), width, height)) { + return false; + } } - SkAutoLockPixels alp(*decodedBitmap); - + SkAutoLockPixels alp(*bitmap); WebPDecoderConfig config; - if (!webp_get_config_resize_crop(config, decodedBitmap, region)) { + if (!webp_get_config_resize_crop(config, bitmap, rect)) { return false; } // Decode the WebP image data stream using WebP incremental decoding for // the specified cropped image-region. - return webp_idecode(this->inputStream, config); + if (!webp_idecode(this->inputStream, config)) { + return false; + } + + if (!directDecode) { + cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), + region.width(), region.height(), rect.x(), rect.y()); + } + return true; } bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, -- cgit v1.1