diff --git a/Android.mk b/Android.mk index a63a0f1c5f39b888a3252d3f8864cbe59d1e2dec..6f8162cd506103ad72c7877189e394ec84ca9a45 100644 --- a/Android.mk +++ b/Android.mk @@ -78,6 +78,7 @@ LOCAL_SRC_FILES:= \ src/images/SkImageRef_GlobalPool.cpp \ src/images/SkImageRefPool.cpp \ src/images/SkJpegUtility.cpp \ + src/images/SkLargeBitmap.cpp \ src/images/SkMovie.cpp \ src/images/SkMovie_gif.cpp \ src/images/SkPageFlipper.cpp \ diff --git a/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h index f8a941fdf6b1a4f65d826d079ce9fffa6a07c730..be1046b6fd02da5885a11f12fd472310f7320f3d 100644 --- a/include/images/SkImageDecoder.h +++ b/include/images/SkImageDecoder.h @@ -18,10 +18,17 @@ #define SkImageDecoder_DEFINED #include "SkBitmap.h" +#include "SkRect.h" #include "SkRefCnt.h" class SkStream; +class SkVMMemoryReporter : public SkRefCnt { +public: + virtual ~SkVMMemoryReporter() {} + virtual bool reportMemory(size_t memorySize); +}; + /** \class SkImageDecoder Base class for decoding compressed images into a SkBitmap @@ -30,6 +37,7 @@ class SkImageDecoder { public: virtual ~SkImageDecoder(); + // Should be consistent with kFormatName enum Format { kUnknown_Format, kBMP_Format, @@ -42,10 +50,21 @@ public: kLastKnownFormat = kWBMP_Format }; + /** Contains the image format name. + * This should be consistent with Format. + * + * The format name gives a more meaningful error message than enum. + */ + static const char *kFormatName[7]; + /** Return the compressed data's format (see Format enum) */ virtual Format getFormat() const; + /** Return the compressed data's format name. + */ + const char* getFormatName() const { return kFormatName[getFormat()]; } + /** Returns true if the decoder should try to dither the resulting image. The default setting is true. */ @@ -118,6 +137,7 @@ public: SkBitmap::Allocator* getAllocator() const { return fAllocator; } SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*); + SkVMMemoryReporter* setReporter(SkVMMemoryReporter*); // sample-size, if set to > 1, tells the decoder to return a smaller than // original bitmap, sampling 1 pixel for every size pixels. e.g. if sample @@ -176,6 +196,29 @@ public: return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode); } + /** + * Given a stream, build an index for doing tile-based decode. + * The built index will be saved in the decoder, and the image size will + * be returned in width and height. + * + * Return true for success or false on failure. + */ + virtual bool buildTileIndex(SkStream*, + int *width, int *height, bool isShareable) { + return false; + } + + /** + * Decode a rectangle region in the image specified by rect. + * The method can only be called after buildTileIndex(). + * + * Return true for success. + * Return false if the index is never built or failing in decoding. + */ + virtual bool decodeRegion(SkBitmap* bitmap, SkIRect rect, + SkBitmap::Config pref); + + /** Given a stream, this will try to find an appropriate decoder object. If none is found, the method returns NULL. */ @@ -264,6 +307,11 @@ protected: // must be overridden in subclasses. This guy is called by decode(...) virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0; + // must be overridden in subclasses. This guy is called by decodeRegion(...) + virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect) { + return false; + } + /** Can be queried from within onDecode, to see if the user (possibly in a different thread) has requested the decode to cancel. If this returns true, your onDecode() should stop and return false. @@ -304,6 +352,8 @@ protected: */ SkBitmap::Config getPrefConfig(SrcDepth, bool hasAlpha) const; + SkVMMemoryReporter* fReporter; + private: Peeker* fPeeker; Chooser* fChooser; diff --git a/include/images/SkJpegUtility.h b/include/images/SkJpegUtility.h index cc9d2466df28daaccffa8c8521ac6b104848a2f6..5dd789c9f02c94d6740e87de0f5e1812803644b9 100644 --- a/include/images/SkJpegUtility.h +++ b/include/images/SkJpegUtility.h @@ -41,11 +41,13 @@ void skjpeg_error_exit(j_common_ptr cinfo); /* Our source struct for directing jpeg to our stream object. */ struct skjpeg_source_mgr : jpeg_source_mgr { - skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder); + skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder, bool copyStream, bool ownStream); + ~skjpeg_source_mgr(); SkStream* fStream; - const void* fMemoryBase; + void* fMemoryBase; size_t fMemoryBaseSize; + bool fUnrefStream; SkImageDecoder* fDecoder; enum { kBufferSize = 1024 diff --git a/include/images/SkLargeBitmap.h b/include/images/SkLargeBitmap.h new file mode 100644 index 0000000000000000000000000000000000000000..0d19f32cf1ee35398a381649c8b26cebd97aa434 --- /dev/null +++ b/include/images/SkLargeBitmap.h @@ -0,0 +1,33 @@ +#ifndef SkLargeBitmap_DEFINED +#define SkLargeBitmap_DEFINED + +#include "SkBitmap.h" +#include "SkRect.h" +#include "SkImageDecoder.h" + +class SkLargeBitmap { +public: + SkLargeBitmap(SkImageDecoder *decoder, int width, int height) { + fDecoder = decoder; + fWidth = width; + fHeight = height; + } + virtual ~SkLargeBitmap() { + delete fDecoder; + } + + virtual bool decodeRegion(SkBitmap* bitmap, SkIRect rect, + SkBitmap::Config pref, int sampleSize); + + virtual int getWidth() { return fWidth; } + virtual int getHeight() { return fHeight; } + + virtual SkImageDecoder* getDecoder() { return fDecoder; } + +private: + SkImageDecoder *fDecoder; + int fWidth; + int fHeight; +}; + +#endif diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp index 768d671d6a99d7c2637e05846ae373acf970307b..51911175ca3621eeed2d83ade14d430be67769e9 100644 --- a/src/images/SkImageDecoder.cpp +++ b/src/images/SkImageDecoder.cpp @@ -20,6 +20,17 @@ #include "SkPixelRef.h" #include "SkStream.h" #include "SkTemplates.h" +#include "SkCanvas.h" + +const char *SkImageDecoder::kFormatName[] = { + "Unknown Format", + "BMP", + "GIF", + "ICO", + "JPEG", + "PNG", + "WBMP", +}; static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; @@ -36,8 +47,8 @@ void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config) /////////////////////////////////////////////////////////////////////////////// SkImageDecoder::SkImageDecoder() - : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1), - fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true), + : fReporter(NULL), fPeeker(NULL), fChooser(NULL), fAllocator(NULL), + fSampleSize(1), fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true), fUsePrefTable(false) { } @@ -45,6 +56,7 @@ SkImageDecoder::~SkImageDecoder() { fPeeker->safeUnref(); fChooser->safeUnref(); fAllocator->safeUnref(); + fReporter->safeUnref(); } SkImageDecoder::Format SkImageDecoder::getFormat() const { @@ -66,6 +78,11 @@ SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) { return alloc; } +SkVMMemoryReporter* SkImageDecoder::setReporter(SkVMMemoryReporter* reporter) { + SkRefCnt_SafeAssign(fReporter, reporter); + return reporter; +} + void SkImageDecoder::setSampleSize(int size) { if (size < 1) { size = 1; @@ -150,6 +167,24 @@ bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, return true; } +bool SkImageDecoder::decodeRegion(SkBitmap* bm, SkIRect rect, + SkBitmap::Config pref) { + // pass a temporary bitmap, so that if we return false, we are assured of + // leaving the caller's bitmap untouched. + SkBitmap tmp; + + // we reset this to false before calling onDecodeRegion + fShouldCancelDecode = false; + // assign this, for use by getPrefConfig(), in case fUsePrefTable is false + fDefaultPref = pref; + + if (!this->onDecodeRegion(&tmp, rect)) { + return false; + } + bm->swap(tmp); + return true; +} + /////////////////////////////////////////////////////////////////////////////// bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index ed523bb05f404afac6d5ba1f44031dac332178f7..2318a3798a8130d0a6901a0a21b2b8379a88d8f3 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -23,6 +23,8 @@ #include "SkStream.h" #include "SkTemplates.h" #include "SkUtils.h" +#include "SkRect.h" +#include "SkCanvas.h" #include <stdio.h> extern "C" { @@ -48,14 +50,44 @@ static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap"; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +class SkJPEGImageIndex { +public: + SkJPEGImageIndex() {} + virtual ~SkJPEGImageIndex() { + jpeg_destroy_huffman_index(index); + delete cinfo->src; + jpeg_finish_decompress(cinfo); + jpeg_destroy_decompress(cinfo); + free(cinfo); + } + jpeg_decompress_struct *cinfo; + huffman_index *index; +}; + + class SkJPEGImageDecoder : public SkImageDecoder { public: + SkJPEGImageDecoder() { + index = NULL; + } + ~SkJPEGImageDecoder() { + if (index) + delete index; + } virtual Format getFormat() const { return kJPEG_Format; } + virtual bool buildTileIndex(SkStream *stream, + int *width, int *height, bool isShareable); protected: + virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect); virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); + virtual void cropBitmap(SkBitmap *dest, SkBitmap *src, int sampleSize, + int srcX, int srcY, int width, int height, + int destX, int destY); +private: + SkJPEGImageIndex *index; }; ////////////////////////////////////////////////////////////////////////// @@ -141,6 +173,21 @@ static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, return true; } +static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo, + huffman_index *index, void* buffer, + int startX, int startY, int width, int height, + int count) { + for (int i = 0; i < count; i++) { + JSAMPLE* rowptr = (JSAMPLE*)buffer; + int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr, + startX, startY, width, height); + if (row_count != 1) { + return false; + } + } + return true; +} + // This guy exists just to aid in debugging, as it allows debuggers to just // set a break-point in one place to see all error exists. static bool return_false(const jpeg_decompress_struct& cinfo, @@ -163,7 +210,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { jpeg_decompress_struct cinfo; skjpeg_error_mgr sk_err; - skjpeg_source_mgr sk_stream(stream, this); + skjpeg_source_mgr sk_stream(stream, this, false, false); cinfo.err = jpeg_std_error(&sk_err); sk_err.error_exit = skjpeg_error_exit; @@ -388,6 +435,278 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { return true; } +bool SkJPEGImageDecoder::buildTileIndex(SkStream* stream, + int *width, int *height, + bool isShareable) { + SkAutoMalloc srcStorage; + SkJPEGImageIndex *index = new SkJPEGImageIndex; + + jpeg_decompress_struct *cinfo = (jpeg_decompress_struct*) + malloc(sizeof(jpeg_decompress_struct)); + skjpeg_error_mgr sk_err; + skjpeg_source_mgr *sk_stream = + new skjpeg_source_mgr(stream, this, !isShareable, true); + if (cinfo == NULL || sk_stream == NULL) { + return false; + } + + cinfo->err = jpeg_std_error(&sk_err); + sk_err.error_exit = skjpeg_error_exit; + + // 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; + } + + jpeg_create_decompress(cinfo); + cinfo->do_fancy_upsampling = 0; + cinfo->do_block_smoothing = 0; + +#ifdef ANDROID + overwrite_mem_buffer_size(cinfo); +#endif + + cinfo->src = sk_stream; + int status = jpeg_read_header(cinfo, true); + if (status != JPEG_HEADER_OK) { + return false; + } + index->index = (huffman_index*)malloc(sizeof(huffman_index)); + jpeg_create_huffman_index(cinfo, index->index); + + cinfo->dct_method = JDCT_IFAST; + cinfo->scale_num = 1; + cinfo->scale_denom = 1; + if (!jpeg_build_huffman_index(cinfo, index->index)) { + return false; + } + if (fReporter) + fReporter->reportMemory(index->index->mem_used); + jpeg_destroy_decompress(cinfo); + + + // Init decoder to image decode mode + jpeg_create_decompress(cinfo); + +#ifdef ANDROID + overwrite_mem_buffer_size(cinfo); +#endif + + cinfo->src = sk_stream; + status = jpeg_read_header(cinfo,true); + if (status != JPEG_HEADER_OK) { + return false; + } + cinfo->out_color_space = JCS_RGBA_8888; + cinfo->do_fancy_upsampling = 0; + cinfo->do_block_smoothing = 0; + //jpeg_start_decompress(cinfo); + jpeg_start_tile_decompress(cinfo); + + cinfo->dct_method = JDCT_IFAST; + cinfo->scale_num = 1; + index->cinfo = cinfo; + *height = cinfo->output_height; + *width = cinfo->output_width; + + this->index = index; + return true; +} + +/* + * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dest are + * both sampled by sampleSize from an original Bitmap. + * + * @param dest the destination Bitmap. + * @param src the source Bitmap that is sampled by sampleSize from the original + * Bitmap. + * @param sampleSize the sample size that src is sampled from the original Bitmap. + * @param (srcX, srcY) the upper-left point of the src Btimap in terms of + * the coordinate in the original Bitmap. + * @param (width, height) the width and height of the unsampled dest. + * @param (destX, destY) the upper-left point of the dest Bitmap in terms of + * the coordinate in the original Bitmap. + */ +void SkJPEGImageDecoder::cropBitmap(SkBitmap *dest, SkBitmap *src, + int sampleSize, int srcX, int srcY, + int width, int height, int destX, int destY) +{ + int w = width / sampleSize; + int h = height / sampleSize; + if (w == src->width() && h == src->height() && + (destX - srcX) / sampleSize == 0 && (destY - srcY) / sampleSize == 0) { + // The output rect is the same as the decode result + dest->swap( *src ); + return; + } + dest->setConfig(src->getConfig(), w, h); + dest->setIsOpaque(true); + this->allocPixelRef(dest, NULL); + + SkCanvas canvas(*dest); + canvas.drawBitmap(*src, (destX - srcX) / sampleSize, + (destY - srcY) / sampleSize); +} + +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; + SkAutoMalloc srcStorage; + skjpeg_error_mgr sk_err; + cinfo->err = jpeg_std_error(&sk_err); + sk_err.error_exit = skjpeg_error_exit; + if (setjmp(sk_err.fJmpBuf)) { + return false; + } + int requestedSampleSize = this->getSampleSize(); + cinfo->scale_denom = requestedSampleSize; + + SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false); + if (config != SkBitmap::kARGB_8888_Config && + config != SkBitmap::kARGB_4444_Config && + config != SkBitmap::kRGB_565_Config) { + config = SkBitmap::kARGB_8888_Config; + } +#ifdef ANDROID_RGB + cinfo->dither_mode = JDITHER_NONE; + if (config == SkBitmap::kARGB_8888_Config) { + cinfo->out_color_space = JCS_RGBA_8888; + } else if (config == SkBitmap::kRGB_565_Config) { + if (requestedSampleSize == 1) { + // SkScaledBitmapSampler can't handle RGB_565 yet, + // so don't even try. + cinfo->out_color_space = JCS_RGB_565; + if (this->getDitherImage()) { + cinfo->dither_mode = JDITHER_ORDERED; + } + } + } +#endif + + 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); + SkBitmap *bitmap = new SkBitmap; + SkAutoTDelete<SkBitmap> adb(bitmap); + + int actualSampleSize = skiaSampleSize * cinfo->image_width / cinfo->output_width; + +#ifdef ANDROID_RGB + /* short-circuit the SkScaledBitmapSampler when possible, as this gives + a significant performance boost. + */ + if (skiaSampleSize == 1 && + ((config == SkBitmap::kARGB_8888_Config && + cinfo->out_color_space == JCS_RGBA_8888) || + (config == SkBitmap::kRGB_565_Config && + cinfo->out_color_space == JCS_RGB_565))) + { + bitmap->setConfig(config, cinfo->output_width, height); + bitmap->setIsOpaque(true); + if (!this->allocPixelRef(bitmap, NULL)) { + return return_false(*cinfo, *bitmap, "allocPixelRef"); + } + SkAutoLockPixels alp(*bitmap); + JSAMPLE* rowptr = (JSAMPLE*)bitmap->getPixels(); + INT32 const bpr = bitmap->rowBytes(); + int row_total_count = 0; + + while (row_total_count < height) { + int row_count = jpeg_read_tile_scanline(cinfo, + index->index, &rowptr, startX, startY, width, height); + // if row_count == 0, then we didn't get a scanline, so abort. + // if we supported partial images, we might return true in this case + if (0 == row_count) { + return return_false(*cinfo, *bitmap, "read_scanlines"); + } + if (this->shouldCancelDecode()) { + return return_false(*cinfo, *bitmap, "shouldCancelDecode"); + } + row_total_count += row_count; + rowptr += bpr; + } + cropBitmap(bm, bitmap, actualSampleSize, oriStartX, oriStartY, + oriWidth, oriHeight, startX, startY); + return true; + } +#endif + // check for supported formats + SkScaledBitmapSampler::SrcConfig sc; + if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) { + sc = SkScaledBitmapSampler::kRGB; +#ifdef ANDROID_RGB + } else if (JCS_RGBA_8888 == cinfo->out_color_space) { + sc = SkScaledBitmapSampler::kRGBX; +#endif + } else if (1 == cinfo->out_color_components && + JCS_GRAYSCALE == cinfo->out_color_space) { + sc = SkScaledBitmapSampler::kGray; + } else { + return return_false(*cinfo, *bm, "jpeg colorspace"); + } + + SkScaledBitmapSampler sampler(width, height, skiaSampleSize); + + bitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); + bitmap->setIsOpaque(true); + + if (!this->allocPixelRef(bitmap, NULL)) { + return return_false(*cinfo, *bitmap, "allocPixelRef"); + } + + SkAutoLockPixels alp(*bitmap); + if (!sampler.begin(bitmap, sc, this->getDitherImage())) { + return return_false(*cinfo, *bitmap, "sampler.begin"); + } + + uint8_t* srcRow = (uint8_t*)srcStorage.alloc(width * 4); + + // Possibly skip initial rows [sampler.srcY0] + if (!skip_src_rows_tile(cinfo, index->index, srcRow, + startX, startY, width, height, 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, index->index, &rowptr, + startX, startY, width, height); + if (0 == row_count) { + return return_false(*cinfo, *bitmap, "read_scanlines"); + } + if (this->shouldCancelDecode()) { + return return_false(*cinfo, *bitmap, "shouldCancelDecode"); + } + + sampler.next(srcRow); + if (bitmap->height() - 1 == y) { + // we're done + break; + } + + if (!skip_src_rows_tile(cinfo, index->index, srcRow, + startX, startY, width, height, + sampler.srcDY() - 1)) { + return return_false(*cinfo, *bitmap, "skip rows"); + } + } + cropBitmap(bm, bitmap, actualSampleSize, oriStartX, oriStartY, + oriWidth, oriHeight, startX, startY); + return true; +} + /////////////////////////////////////////////////////////////////////////////// #include "SkColorPriv.h" diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp index e20759297434063a2e6e14106dfb76d59ef1e18a..aaf87db688078468f1cbd6061cffd9b52cc9efd4 100644 --- a/src/images/SkJpegUtility.cpp +++ b/src/images/SkJpegUtility.cpp @@ -21,6 +21,24 @@ static void sk_init_source(j_decompress_ptr cinfo) { skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; src->next_input_byte = (const JOCTET*)src->fBuffer; src->bytes_in_buffer = 0; + src->current_offset = 0; + src->fStream->rewind(); +} + +static boolean sk_seek_input_data(j_decompress_ptr cinfo, long byte_offset) { + skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; + + if (byte_offset > src->current_offset) { + (void)src->fStream->skip(byte_offset - src->current_offset); + } else { + src->fStream->rewind(); + (void)src->fStream->skip(byte_offset); + } + + src->current_offset = byte_offset; + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = 0; + return TRUE; } static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) { @@ -35,6 +53,7 @@ static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) { return FALSE; } + src->current_offset += bytes; src->next_input_byte = (const JOCTET*)src->fBuffer; src->bytes_in_buffer = bytes; return TRUE; @@ -52,6 +71,7 @@ static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { cinfo->err->error_exit((j_common_ptr)cinfo); return; } + src->current_offset += bytes; bytesToSkip -= bytes; } src->next_input_byte = (const JOCTET*)src->fBuffer; @@ -83,7 +103,9 @@ static void sk_term_source(j_decompress_ptr /*cinfo*/) {} static void skmem_init_source(j_decompress_ptr cinfo) { skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; src->next_input_byte = (const JOCTET*)src->fMemoryBase; + src->start_input_byte = (const JOCTET*)src->fMemoryBase; src->bytes_in_buffer = src->fMemoryBaseSize; + src->current_offset = src->fMemoryBaseSize; } static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) { @@ -108,18 +130,23 @@ static void skmem_term_source(j_decompress_ptr /*cinfo*/) {} /////////////////////////////////////////////////////////////////////////////// -skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) { +skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder, + bool copyStream, bool ownStream) : fStream(stream) { fDecoder = decoder; const void* baseAddr = stream->getMemoryBase(); - if (baseAddr && false) { - fMemoryBase = baseAddr; + fMemoryBase = NULL; + fUnrefStream = ownStream; + if (copyStream) { fMemoryBaseSize = stream->getLength(); + fMemoryBase = sk_malloc_throw(fMemoryBaseSize); + stream->read(fMemoryBase, fMemoryBaseSize); init_source = skmem_init_source; fill_input_buffer = skmem_fill_input_buffer; skip_input_data = skmem_skip_input_data; resync_to_restart = skmem_resync_to_restart; term_source = skmem_term_source; + seek_input_data = NULL; } else { fMemoryBase = NULL; fMemoryBaseSize = 0; @@ -129,10 +156,20 @@ skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder) skip_input_data = sk_skip_input_data; resync_to_restart = sk_resync_to_restart; term_source = sk_term_source; + seek_input_data = sk_seek_input_data; } // SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize); } +skjpeg_source_mgr::~skjpeg_source_mgr() { + if (fMemoryBase) { + sk_free(fMemoryBase); + } + if (fUnrefStream) { + fStream->unref(); + } +} + /////////////////////////////////////////////////////////////////////////////// static void sk_init_destination(j_compress_ptr cinfo) { diff --git a/src/images/SkLargeBitmap.cpp b/src/images/SkLargeBitmap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18fb36fe4cf5be3d1850e9a37bea2e6e1d09ae39 --- /dev/null +++ b/src/images/SkLargeBitmap.cpp @@ -0,0 +1,7 @@ +#include "SkLargeBitmap.h" + +bool SkLargeBitmap::decodeRegion(SkBitmap* bitmap, SkIRect rect, + SkBitmap::Config pref, int sampleSize) { + fDecoder->setSampleSize(sampleSize); + return fDecoder->decodeRegion(bitmap, rect, pref); +}