From 57f4969724a1dd88c8d9ae35a863e6cf621181d5 Mon Sep 17 00:00:00 2001 From: "djsollen@google.com" Date: Wed, 23 Feb 2011 20:46:31 +0000 Subject: [PATCH] merge from android tree: - optional parameters added to descriptorProc and allocPixels - clip options to image decoders - check for xfermode in blitter_a8 - UNROLL loops in blitrow reviewed by reed@google.com git-svn-id: http://skia.googlecode.com/svn/trunk@841 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkBitmap.h | 7 +- include/core/SkPaint.h | 2 +- include/core/SkPixelRef.h | 12 + include/core/SkStream.h | 5 + include/core/SkTemplates.h | 7 +- include/images/SkImageRef.h | 6 + include/images/SkJpegUtility.h | 6 +- src/core/SkBitmap.cpp | 6 +- src/core/SkBlitRow_D32.cpp | 51 ++++ src/core/SkBlitter_A8.cpp | 6 +- src/core/SkPaint.cpp | 7 +- src/core/SkPixelRef.cpp | 7 + src/core/SkStream.cpp | 12 + src/images/SkFlipPixelRef.cpp | 5 +- src/images/SkImageDecoder_Factory.cpp | 1 - src/images/SkImageDecoder_libgif.cpp | 5 + src/images/SkImageDecoder_libjpeg.cpp | 14 +- src/images/SkImageRef.cpp | 10 + src/images/SkImageRef_GlobalPool.cpp | 5 +- src/images/SkJpegUtility.cpp | 63 +++-- src/images/SkMovie_gif.cpp | 374 ++++++++++++++++++++------ src/images/SkScaledBitmapSampler.cpp | 26 +- src/images/SkScaledBitmapSampler.h | 3 +- src/ports/SkImageRef_ashmem.cpp | 36 ++- src/ports/SkImageRef_ashmem.h | 8 + src/utils/SkNinePatch.cpp | 29 +- 26 files changed, 580 insertions(+), 133 deletions(-) diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h index c54fb5d22..a38cafa35 100644 --- a/include/core/SkBitmap.h +++ b/include/core/SkBitmap.h @@ -462,10 +462,15 @@ public: int extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy); void extractAlpha(SkBitmap* dst) const { - this->extractAlpha(dst, NULL, NULL); + this->extractAlpha(dst, NULL, NULL, NULL); } void extractAlpha(SkBitmap* dst, const SkPaint* paint, + SkIPoint* offset) const { + this->extractAlpha(dst, paint, NULL, offset); + } + + void extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator, SkIPoint* offset) const; void flatten(SkFlattenableWriteBuffer&) const; diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 16411d588..a4def5f18 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -855,7 +855,7 @@ private: void descriptorProc(const SkMatrix* deviceMatrix, void (*proc)(const SkDescriptor*, void*), - void* context) const; + void* context, bool ignoreGamma = false) const; const SkRect& computeStrokeFastBounds(const SkRect& orig, SkRect* storage) const; diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h index 8375cc7f7..c0259af1d 100644 --- a/include/core/SkPixelRef.h +++ b/include/core/SkPixelRef.h @@ -119,6 +119,18 @@ public: virtual Factory getFactory() const { return NULL; } virtual void flatten(SkFlattenableWriteBuffer&) const; + /** Acquire a "global" ref on this object. + * The default implementation just calls ref(), but subclasses can override + * this method to implement additional behavior. + */ + virtual void globalRef(void* data=NULL); + + /** Release a "global" ref on this object. + * The default implementation just calls unref(), but subclasses can override + * this method to implement additional behavior. + */ + virtual void globalUnref(); + static Factory NameToFactory(const char name[]); static const char* FactoryToName(Factory); static void Register(const char name[], Factory); diff --git a/include/core/SkStream.h b/include/core/SkStream.h index 046c4d963..b02d48272 100644 --- a/include/core/SkStream.h +++ b/include/core/SkStream.h @@ -177,6 +177,11 @@ public: */ virtual void setMemory(const void* data, size_t length, bool copyData = false); + /** Replace any memory buffer with the specified buffer. The caller + must have allocated data with sk_malloc or sk_realloc, since it + will be freed with sk_free. + */ + void setMemoryOwned(const void* data, size_t length); void skipToAlign4(); virtual bool rewind(); virtual size_t read(void* buffer, size_t size); diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h index eaa812fdb..55109bfce 100644 --- a/include/core/SkTemplates.h +++ b/include/core/SkTemplates.h @@ -62,8 +62,10 @@ private: // See also SkTScopedPtr. template class SkAutoTDelete : SkNoncopyable { public: - SkAutoTDelete(T* obj) : fObj(obj) {} - ~SkAutoTDelete() { delete fObj; } + SkAutoTDelete(T* obj, bool deleteWhenDone = true) : fObj(obj) { + fDeleteWhenDone = deleteWhenDone; + } + ~SkAutoTDelete() { if (fDeleteWhenDone) delete fObj; } T* get() const { return fObj; } void free() { delete fObj; fObj = NULL; } @@ -71,6 +73,7 @@ public: private: T* fObj; + bool fDeleteWhenDone; }; template class SkAutoTDeleteArray : SkNoncopyable { diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h index 9c9896f6f..800f12e07 100644 --- a/include/images/SkImageRef.h +++ b/include/images/SkImageRef.h @@ -57,6 +57,12 @@ public: */ bool getInfo(SkBitmap* bm); + /** Return true if the image can be decoded and is opaque. Calling this + method will decode and set the pixels in the specified bitmap and + sets the isOpaque flag. + */ + bool isOpaque(SkBitmap* bm); + SkImageDecoderFactory* getDecoderFactory() const { return fFactory; } // returns the factory parameter SkImageDecoderFactory* setDecoderFactory(SkImageDecoderFactory*); diff --git a/include/images/SkJpegUtility.h b/include/images/SkJpegUtility.h index cc9d2466d..e9dd9778e 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 ownStream); + ~skjpeg_source_mgr(); SkStream* fStream; - const void* fMemoryBase; + void* fMemoryBase; size_t fMemoryBaseSize; + bool fUnrefStream; SkImageDecoder* fDecoder; enum { kBufferSize = 1024 diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index a0ab52d0b..b302f5b8d 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -1186,7 +1186,7 @@ static bool GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[], #include "SkMatrix.h" void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, - SkIPoint* offset) const { + Allocator *allocator, SkIPoint* offset) const { SkDEBUGCODE(this->validate();) SkMatrix identity; @@ -1210,7 +1210,7 @@ void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, NO_FILTER_CASE: dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(), srcM.fRowBytes); - dst->allocPixels(); + dst->allocPixels(allocator, NULL); GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes); if (offset) { offset->set(0, 0); @@ -1229,7 +1229,7 @@ void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(), dstM.fBounds.height(), dstM.fRowBytes); - dst->allocPixels(); + dst->allocPixels(allocator, NULL); memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize()); if (offset) { offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop); diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp index f1dcb309a..f50077839 100644 --- a/src/core/SkBlitRow_D32.cpp +++ b/src/core/SkBlitRow_D32.cpp @@ -2,6 +2,8 @@ #include "SkColorPriv.h" #include "SkUtils.h" +#define UNROLL + static void S32_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst, const SkPMColor* SK_RESTRICT src, int count, U8CPU alpha) { @@ -16,11 +18,28 @@ static void S32_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst, if (count > 0) { unsigned src_scale = SkAlpha255To256(alpha); unsigned dst_scale = 256 - src_scale; + +#ifdef UNROLL + if (count & 1) { + *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale); + dst += 1; + count -= 1; + } + + const SkPMColor* SK_RESTRICT srcEnd = src + count; + while (src != srcEnd) { + *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale); + dst += 1; + *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale); + dst += 1; + } +#else do { *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale); src += 1; dst += 1; } while (--count > 0); +#endif } } @@ -31,6 +50,21 @@ static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst, int count, U8CPU alpha) { SkASSERT(255 == alpha); if (count > 0) { +#ifdef UNROLL + if (count & 1) { + *dst = SkPMSrcOver(*(src++), *dst); + dst += 1; + count -= 1; + } + + const SkPMColor* SK_RESTRICT srcEnd = src + count; + while (src != srcEnd) { + *dst = SkPMSrcOver(*(src++), *dst); + dst += 1; + *dst = SkPMSrcOver(*(src++), *dst); + dst += 1; + } +#else do { #ifdef TEST_SRC_ALPHA SkPMColor sc = *src; @@ -48,6 +82,7 @@ static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst, src += 1; dst += 1; } while (--count > 0); +#endif } } @@ -56,11 +91,27 @@ static void S32A_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst, int count, U8CPU alpha) { SkASSERT(alpha <= 255); if (count > 0) { +#ifdef UNROLL + if (count & 1) { + *dst = SkBlendARGB32(*(src++), *dst, alpha); + dst += 1; + count -= 1; + } + + const SkPMColor* SK_RESTRICT srcEnd = src + count; + while (src != srcEnd) { + *dst = SkBlendARGB32(*(src++), *dst, alpha); + dst += 1; + *dst = SkBlendARGB32(*(src++), *dst, alpha); + dst += 1; + } +#else do { *dst = SkBlendARGB32(*src, *dst, alpha); src += 1; dst += 1; } while (--count > 0); +#endif } } diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp index f2d73e3a6..f74fbe3c5 100644 --- a/src/core/SkBlitter_A8.cpp +++ b/src/core/SkBlitter_A8.cpp @@ -249,7 +249,7 @@ SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& } SkA8_Shader_Blitter::~SkA8_Shader_Blitter() { - SkSafeUnref(fXfermode); + if (fXfermode) SkSafeUnref(fXfermode); sk_free(fBuffer); } @@ -342,7 +342,9 @@ void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { while (--height >= 0) { fShader->shadeSpan(x, y, span, width); - fXfermode->xferA8(device, span, width, alpha); + if (fXfermode) { + fXfermode->xferA8(device, span, width, alpha); + } y += 1; device += fDevice.rowBytes(); diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 55f16af67..0be0d8fbf 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -1233,10 +1233,14 @@ void SkScalerContext::MakeRec(const SkPaint& paint, void SkPaint::descriptorProc(const SkMatrix* deviceMatrix, void (*proc)(const SkDescriptor*, void*), - void* context) const { + void* context, bool ignoreGamma) const { SkScalerContext::Rec rec; SkScalerContext::MakeRec(*this, deviceMatrix, &rec); + if (ignoreGamma) { + rec.fFlags &= ~(SkScalerContext::kGammaForBlack_Flag | + SkScalerContext::kGammaForWhite_Flag); + } size_t descSize = sizeof(rec); int entryCount = 1; @@ -1645,4 +1649,3 @@ const SkPath* SkTextToPathIter::next(SkScalar* xpos) { } return NULL; } - diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp index e096d634b..c376412c6 100644 --- a/src/core/SkPixelRef.cpp +++ b/src/core/SkPixelRef.cpp @@ -128,3 +128,10 @@ const char* SkPixelRef::FactoryToName(Factory fact) { return NULL; } +void SkPixelRef::globalRef(void* data) { + this->ref(); +} + +void SkPixelRef::globalUnref() { + this->unref(); +} diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp index cbe3eb488..21ee05ea8 100644 --- a/src/core/SkStream.cpp +++ b/src/core/SkStream.cpp @@ -272,6 +272,18 @@ SkMemoryStream::~SkMemoryStream() sk_free((void*)fSrc); } +void SkMemoryStream::setMemoryOwned(const void* src, size_t size) +{ + if (fWeOwnTheData) + sk_free((void*)fSrc); + + fSize = size; + fOffset = 0; + fWeOwnTheData = true; + + fSrc = src; +} + void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) { if (fWeOwnTheData) diff --git a/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp index 8d0f15a70..39e1a1205 100644 --- a/src/images/SkFlipPixelRef.cpp +++ b/src/images/SkFlipPixelRef.cpp @@ -74,8 +74,8 @@ SkPixelRef* SkFlipPixelRef::Create(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(SkFlipPixelRef, (buffer)); } -static SkPixelRef::Registrar::Registrar reg("SkFlipPixelRef", - SkFlipPixelRef::Create); +static SkPixelRef::Registrar reg("SkFlipPixelRef", + SkFlipPixelRef::Create); /////////////////////////////////////////////////////////////////////////////// @@ -125,4 +125,3 @@ void SkFlipPixelRef::CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip, iter.next(); } } - diff --git a/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp index cb5ff2143..e5ff39546 100644 --- a/src/images/SkImageDecoder_Factory.cpp +++ b/src/images/SkImageDecoder_Factory.cpp @@ -71,4 +71,3 @@ SkMovie* SkMovie::DecodeStream(SkStream* stream) { } return NULL; } - diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp index d2470ccf1..75a9ee0c8 100644 --- a/src/images/SkImageDecoder_libgif.cpp +++ b/src/images/SkImageDecoder_libgif.cpp @@ -117,6 +117,11 @@ static const ColorMapObject* find_colormap(const GifFileType* gif) { if (NULL == cmap) { cmap = gif->SColorMap; } + + if (NULL == cmap) { + // no colormap found + return NULL; + } // some sanity checks if (cmap && ((unsigned)cmap->ColorCount > 256 || cmap->ColorCount != (1 << cmap->BitsPerPixel))) { diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index ed523bb05..aac0b4b5c 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -223,13 +223,9 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { if (config == SkBitmap::kARGB_8888_Config) { cinfo.out_color_space = JCS_RGBA_8888; } else if (config == SkBitmap::kRGB_565_Config) { - if (sampleSize == 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; - } + cinfo.out_color_space = JCS_RGB_565; + if (this->getDitherImage()) { + cinfo.dither_mode = JDITHER_ORDERED; } } #endif @@ -319,8 +315,8 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { #ifdef ANDROID_RGB } else if (JCS_RGBA_8888 == cinfo.out_color_space) { sc = SkScaledBitmapSampler::kRGBX; - //} else if (JCS_RGB_565 == cinfo.out_color_space) { - // sc = SkScaledBitmapSampler::kRGB_565; + } else if (JCS_RGB_565 == cinfo.out_color_space) { + sc = SkScaledBitmapSampler::kRGB_565; #endif } else if (1 == cinfo.out_color_components && JCS_GRAYSCALE == cinfo.out_color_space) { diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp index 16c2820b9..7d2d41629 100644 --- a/src/images/SkImageRef.cpp +++ b/src/images/SkImageRef.cpp @@ -57,6 +57,16 @@ bool SkImageRef::getInfo(SkBitmap* bitmap) { return true; } +bool SkImageRef::isOpaque(SkBitmap* bitmap) { + if (bitmap && bitmap->pixelRef() == this) { + bitmap->lockPixels(); + bitmap->setIsOpaque(fBitmap.isOpaque()); + bitmap->unlockPixels(); + return true; + } + return false; +} + SkImageDecoderFactory* SkImageRef::setDecoderFactory( SkImageDecoderFactory* fact) { SkRefCnt_SafeAssign(fFactory, fact); diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp index 1f0bc4306..1f44a8457 100644 --- a/src/images/SkImageRef_GlobalPool.cpp +++ b/src/images/SkImageRef_GlobalPool.cpp @@ -50,8 +50,8 @@ SkPixelRef* SkImageRef_GlobalPool::Create(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(SkImageRef_GlobalPool, (buffer)); } -static SkPixelRef::Registrar::Registrar reg("SkImageRef_GlobalPool", - SkImageRef_GlobalPool::Create); +static SkPixelRef::Registrar reg("SkImageRef_GlobalPool", + SkImageRef_GlobalPool::Create); /////////////////////////////////////////////////////////////////////////////// // global imagerefpool wrappers @@ -80,4 +80,3 @@ void SkImageRef_GlobalPool::DumpPool() { SkAutoMutexAcquire ac(gImageRefMutex); gGlobalImageRefPool.dump(); } - diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp index e20759297..a2fe9a8c7 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,31 +130,34 @@ 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 ownStream) : fStream(stream) { fDecoder = decoder; const void* baseAddr = stream->getMemoryBase(); - if (baseAddr && false) { - fMemoryBase = baseAddr; - fMemoryBaseSize = stream->getLength(); + size_t bufferSize = 4096; + size_t len; + fMemoryBase = NULL; + fUnrefStream = ownStream; + fMemoryBaseSize = 0; - 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; - } else { - fMemoryBase = NULL; - fMemoryBaseSize = 0; - - init_source = sk_init_source; - fill_input_buffer = sk_fill_input_buffer; - skip_input_data = sk_skip_input_data; - resync_to_restart = sk_resync_to_restart; - term_source = sk_term_source; - } + init_source = sk_init_source; + fill_input_buffer = sk_fill_input_buffer; + 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/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp index 113804462..0a85c2d60 100644 --- a/src/images/SkMovie_gif.cpp +++ b/src/images/SkMovie_gif.cpp @@ -20,6 +20,7 @@ #include "SkColorPriv.h" #include "SkStream.h" #include "SkTemplates.h" +#include "SkUtils.h" #include "gif_lib.h" @@ -35,7 +36,9 @@ protected: private: GifFileType* fGIF; - SavedImage* fCurrSavedImage; + int fCurrIndex; + int fLastDrawIndex; + SkBitmap fBackup; }; static int Decode(GifFileType* fileType, GifByteType* out, int size) { @@ -54,7 +57,8 @@ SkGIFMovie::SkGIFMovie(SkStream* stream) DGifCloseFile(fGIF); fGIF = NULL; } - fCurrSavedImage = NULL; + fCurrIndex = -1; + fLastDrawIndex = -1; } SkGIFMovie::~SkGIFMovie() @@ -105,29 +109,242 @@ bool SkGIFMovie::onSetTime(SkMSec time) dur += savedimage_duration(&fGIF->SavedImages[i]); if (dur >= time) { - SavedImage* prev = fCurrSavedImage; - fCurrSavedImage = &fGIF->SavedImages[i]; - return prev != fCurrSavedImage; + fCurrIndex = i; + return fLastDrawIndex != fCurrIndex; } } - fCurrSavedImage = &fGIF->SavedImages[fGIF->ImageCount - 1]; + fCurrIndex = fGIF->ImageCount - 1; return true; } +static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap, + int transparent, int width) +{ + for (; width > 0; width--, src++, dst++) { + if (*src != transparent) { + const GifColorType& col = cmap->Colors[*src]; + *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue); + } + } +} + +static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src, + const ColorMapObject* cmap, int transparent, int copyWidth, + int copyHeight, const GifImageDesc& imageDesc, int rowStep, + int startRow) +{ + int row; + // every 'rowStep'th row, starting with row 'startRow' + for (row = startRow; row < copyHeight; row += rowStep) { + uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row); + copyLine(dst, src, cmap, transparent, copyWidth); + src += imageDesc.Width; + } + + // pad for rest height + src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep); +} + +static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap, + int transparent) +{ + int width = bm->width(); + int height = bm->height(); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + // deinterlace + const unsigned char* src = (unsigned char*)frame->RasterBits; + + // group 1 - every 8th row, starting with row 0 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0); + + // group 2 - every 8th row, starting with row 4 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4); + + // group 3 - every 4th row, starting with row 2 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2); + + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1); +} + +static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap, + int transparent) +{ + int width = bm->width(); + int height = bm->height(); + const unsigned char* src = (unsigned char*)frame->RasterBits; + uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + int srcPad, dstPad; + dstPad = width - copyWidth; + srcPad = frame->ImageDesc.Width - copyWidth; + for (; copyHeight > 0; copyHeight--) { + copyLine(dst, src, cmap, transparent, copyWidth); + src += frame->ImageDesc.Width; + dst += width; + } +} + +static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height, + uint32_t col) +{ + int bmWidth = bm->width(); + int bmHeight = bm->height(); + uint32_t* dst = bm->getAddr32(left, top); + GifWord copyWidth = width; + if (left + copyWidth > bmWidth) { + copyWidth = bmWidth - left; + } + + GifWord copyHeight = height; + if (top + copyHeight > bmHeight) { + copyHeight = bmHeight - top; + } + + for (; copyHeight > 0; copyHeight--) { + sk_memset32(dst, col, copyWidth); + dst += bmWidth; + } +} + +static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap) +{ + int transparent = -1; + + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + bool has_transparency = ((eb->Bytes[0] & 1) == 1); + if (has_transparency) { + transparent = (unsigned char)eb->Bytes[3]; + } + } + } + + if (frame->ImageDesc.ColorMap != NULL) { + // use local color table + cmap = frame->ImageDesc.ColorMap; + } + + if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + SkASSERT(!"bad colortable setup"); + return; + } + + if (frame->ImageDesc.Interlace) { + blitInterlace(bm, frame, cmap, transparent); + } else { + blitNormal(bm, frame, cmap, transparent); + } +} + +static bool checkIfWillBeCleared(const SavedImage* frame) +{ + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + // check disposal method + int disposal = ((eb->Bytes[0] >> 2) & 7); + if (disposal == 2 || disposal == 3) { + return true; + } + } + } + return false; +} + +static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal) +{ + *trans = false; + *disposal = 0; + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + *trans = ((eb->Bytes[0] & 1) == 1); + *disposal = ((eb->Bytes[0] >> 2) & 7); + } + } +} + +// return true if area of 'target' is completely covers area of 'covered' +static bool checkIfCover(const SavedImage* target, const SavedImage* covered) +{ + if (target->ImageDesc.Left <= covered->ImageDesc.Left + && covered->ImageDesc.Left + covered->ImageDesc.Width <= + target->ImageDesc.Left + target->ImageDesc.Width + && target->ImageDesc.Top <= covered->ImageDesc.Top + && covered->ImageDesc.Top + covered->ImageDesc.Height <= + target->ImageDesc.Top + target->ImageDesc.Height) { + return true; + } + return false; +} + +static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next, + SkBitmap* backup, SkColor color) +{ + // We can skip disposal process if next frame is not transparent + // and completely covers current area + bool curTrans; + int curDisposal; + getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal); + bool nextTrans; + int nextDisposal; + getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal); + if ((curDisposal == 2 || curDisposal == 3) + && (nextTrans || !checkIfCover(next, cur))) { + switch (curDisposal) { + // restore to background color + // -> 'background' means background under this image. + case 2: + fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top, + cur->ImageDesc.Width, cur->ImageDesc.Height, + color); + break; + + // restore to previous + case 3: + bm->swap(*backup); + break; + } + } + + // Save current image if next frame's disposal method == 3 + if (nextDisposal == 3) { + const uint32_t* src = bm->getAddr32(0, 0); + uint32_t* dst = backup->getAddr32(0, 0); + int cnt = bm->width() * bm->height(); + memcpy(dst, src, cnt*sizeof(uint32_t)); + } +} + bool SkGIFMovie::onGetBitmap(SkBitmap* bm) { - GifFileType* gif = fGIF; + const GifFileType* gif = fGIF; if (NULL == gif) return false; - // should we check for the Image cmap or the global (SColorMap) first? - ColorMapObject* cmap = gif->SColorMap; - if (cmap == NULL) - cmap = gif->Image.ColorMap; - - if (cmap == NULL || gif->ImageCount < 1 || cmap->ColorCount != (1 << cmap->BitsPerPixel)) - { - SkASSERT(!"bad colortable setup"); + if (gif->ImageCount < 1) { return false; } @@ -137,76 +354,79 @@ bool SkGIFMovie::onGetBitmap(SkBitmap* bm) return false; } - SavedImage* gif_image = fCurrSavedImage; - SkBitmap::Config config = SkBitmap::kIndex8_Config; - - SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount)); - SkAutoUnref aur(colorTable); - - bm->setConfig(config, width, height, 0); - if (!bm->allocPixels(colorTable)) { - return false; + // no need to draw + if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) { + return true; } - int transparent = -1; - for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) { - ExtensionBlock* eb = gif_image->ExtensionBlocks + i; - if (eb->Function == 0xF9 && - eb->ByteCount == 4) { - bool has_transparency = ((eb->Bytes[0] & 1) == 1); - if (has_transparency) { - transparent = (unsigned char)eb->Bytes[3]; + int startIndex = fLastDrawIndex + 1; + if (fLastDrawIndex < 0 || !bm->readyToDraw()) { + // first time + + startIndex = 0; + + // create bitmap + bm->setConfig(SkBitmap::kARGB_8888_Config, width, height, 0); + if (!bm->allocPixels(NULL)) { + return false; } - } + // create bitmap for backup + fBackup.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0); + if (!fBackup.allocPixels(NULL)) { + return false; + } + } else if (startIndex > fCurrIndex) { + // rewind to 1st frame for repeat + startIndex = 0; } - SkPMColor* colorPtr = colorTable->lockColors(); - - if (transparent >= 0) - memset(colorPtr, 0, cmap->ColorCount * 4); - else - colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); - - for (int index = 0; index < cmap->ColorCount; index++) - { - if (transparent != index) - colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red, - cmap->Colors[index].Green, cmap->Colors[index].Blue); + int lastIndex = fCurrIndex; + if (lastIndex < 0) { + // first time + lastIndex = 0; + } else if (lastIndex > fGIF->ImageCount - 1) { + // this block must not be reached. + lastIndex = fGIF->ImageCount - 1; } - colorTable->unlockColors(true); - unsigned char* in = (unsigned char*)gif_image->RasterBits; - unsigned char* out = bm->getAddr8(0, 0); - if (gif->Image.Interlace) { - - // deinterlace - int row; - // group 1 - every 8th row, starting with row 0 - for (row = 0; row < height; row += 8) { - memcpy(out + width * row, in, width); - in += width; - } - - // group 2 - every 8th row, starting with row 4 - for (row = 4; row < height; row += 8) { - memcpy(out + width * row, in, width); - in += width; - } - - // group 3 - every 4th row, starting with row 2 - for (row = 2; row < height; row += 4) { - memcpy(out + width * row, in, width); - in += width; - } - - for (row = 1; row < height; row += 2) { - memcpy(out + width * row, in, width); - in += width; - } - - } else { - memcpy(out, in, width * height); + SkColor bgColor = SkPackARGB32(0, 0, 0, 0); + if (gif->SColorMap != NULL) { + const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor]; + bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue); } + + static SkColor paintingColor = SkPackARGB32(0, 0, 0, 0); + // draw each frames - not intelligent way + for (int i = startIndex; i <= lastIndex; i++) { + const SavedImage* cur = &fGIF->SavedImages[i]; + if (i == 0) { + bool trans; + int disposal; + getTransparencyAndDisposalMethod(cur, &trans, &disposal); + if (!trans && gif->SColorMap != NULL) { + paintingColor = bgColor; + } else { + paintingColor = SkColorSetARGB(0, 0, 0, 0); + } + + bm->eraseColor(paintingColor); + fBackup.eraseColor(paintingColor); + } else { + // Dispose previous frame before move to next frame. + const SavedImage* prev = &fGIF->SavedImages[i-1]; + disposeFrameIfNeeded(bm, prev, cur, &fBackup, paintingColor); + } + + // Draw frame + // We can skip this process if this index is not last and disposal + // method == 2 or method == 3 + if (i == lastIndex || !checkIfWillBeCleared(cur)) { + drawFrame(bm, cur, gif->SColorMap); + } + } + + // save index + fLastDrawIndex = lastIndex; return true; } diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp index 3ba38f7b1..32b78efcc 100644 --- a/src/images/SkScaledBitmapSampler.cpp +++ b/src/images/SkScaledBitmapSampler.cpp @@ -93,6 +93,18 @@ static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow, return false; } +static bool Sample_D565_D565(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int, const SkPMColor[]) { + uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; + uint16_t* SK_RESTRICT castedSrc = (uint16_t*) src; + for (int x = 0; x < width; x++) { + dst[x] = castedSrc[0]; + castedSrc += deltaSrc >> 1; + } + return false; +} + static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int y, const SkPMColor[]) { @@ -335,21 +347,25 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_Index_D8888, Sample_Index_D8888, + NULL, NULL, // 565 (no alpha distinction) Sample_Gray_D565, Sample_Gray_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_Index_D565, Sample_Index_D565_D, + Sample_D565_D565, Sample_D565_D565, // 4444 Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBA_D4444, Sample_RGBA_D4444_D, Sample_Index_D4444, Sample_Index_D4444_D, + NULL, NULL, // Index8 NULL, NULL, NULL, NULL, NULL, NULL, Sample_Index_DI, Sample_Index_DI, + NULL, NULL, }; fCTable = ctable; @@ -379,6 +395,10 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither, fSrcPixelSize = 1; index += 6; break; + case SkScaledBitmapSampler::kRGB_565: + fSrcPixelSize = 2; + index += 8; + break; default: return false; } @@ -388,13 +408,13 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither, index += 0; break; case SkBitmap::kRGB_565_Config: - index += 8; + index += 10; break; case SkBitmap::kARGB_4444_Config: - index += 16; + index += 20; break; case SkBitmap::kIndex8_Config: - index += 24; + index += 30; break; default: return false; diff --git a/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h index 84a75ba54..43f166946 100644 --- a/src/images/SkScaledBitmapSampler.h +++ b/src/images/SkScaledBitmapSampler.h @@ -21,7 +21,8 @@ public: kIndex, // 1 byte per pixel kRGB, // 3 bytes per pixel kRGBX, // 4 byes per pixel (ignore 4th) - kRGBA // 4 bytes per pixel + kRGBA, // 4 bytes per pixel + kRGB_565 // 2 bytes per pixel }; // Given a dst bitmap (with pixels already allocated) and a src-config, diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp index a904bae95..539d768b2 100644 --- a/src/ports/SkImageRef_ashmem.cpp +++ b/src/ports/SkImageRef_ashmem.cpp @@ -1,5 +1,6 @@ #include "SkImageRef_ashmem.h" #include "SkImageDecoder.h" +#include "SkFlattenable.h" #include "SkThread.h" #include @@ -36,7 +37,7 @@ SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream, } SkImageRef_ashmem::~SkImageRef_ashmem() { - fCT->safeUnref(); + SkSafeUnref(fCT); this->closeFD(); } @@ -201,3 +202,36 @@ void SkImageRef_ashmem::onUnlockPixels() { fBitmap.setPixels(NULL, NULL); } +void SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + const char* uri = getURI(); + if (uri) { + size_t len = strlen(uri); + buffer.write32(len); + buffer.writePad(uri, len); + } else { + buffer.write32(0); + } +} + +SkImageRef_ashmem::SkImageRef_ashmem(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + fRec.fFD = -1; + fRec.fAddr = NULL; + fRec.fSize = 0; + fRec.fPinned = false; + fCT = NULL; + size_t length = buffer.readU32(); + if (length) { + char* buf = (char*) malloc(length); + buffer.read(buf, length); + setURI(buf, length); + } +} + +SkPixelRef* SkImageRef_ashmem::Create(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkImageRef_ashmem, (buffer)); +} + +static SkPixelRef::Registrar reg("SkImageRef_ashmem", + SkImageRef_ashmem::Create); diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h index 193a01d35..2c485e3ae 100644 --- a/src/ports/SkImageRef_ashmem.h +++ b/src/ports/SkImageRef_ashmem.h @@ -15,6 +15,13 @@ public: SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1); virtual ~SkImageRef_ashmem(); + // overrides + virtual void flatten(SkFlattenableWriteBuffer&) const; + virtual Factory getFactory() const { + return Create; + } + static SkPixelRef* Create(SkFlattenableReadBuffer&); + protected: virtual bool onDecode(SkImageDecoder* codec, SkStream* stream, SkBitmap* bitmap, SkBitmap::Config config, @@ -24,6 +31,7 @@ protected: virtual void onUnlockPixels(); private: + SkImageRef_ashmem(SkFlattenableReadBuffer&); void closeFD(); SkColorTable* fCT; diff --git a/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp index 3d85edcf9..0b601eb66 100644 --- a/src/utils/SkNinePatch.cpp +++ b/src/utils/SkNinePatch.cpp @@ -235,15 +235,27 @@ static void drawNineViaRects(SkCanvas* canvas, const SkRect& dst, const int32_t srcY[4] = { 0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height() }; - const SkScalar dstX[4] = { + SkScalar dstX[4] = { dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft), dst.fRight - SkIntToScalar(margins.fRight), dst.fRight }; - const SkScalar dstY[4] = { + SkScalar dstY[4] = { dst.fTop, dst.fTop + SkIntToScalar(margins.fTop), dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom }; - + + if (dstX[1] > dstX[2]) { + dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * SkIntToScalar(margins.fLeft) / + (SkIntToScalar(margins.fLeft) + SkIntToScalar(margins.fRight)); + dstX[2] = dstX[1]; + } + + if (dstY[1] > dstY[2]) { + dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * SkIntToScalar(margins.fTop) / + (SkIntToScalar(margins.fTop) + SkIntToScalar(margins.fBottom)); + dstY[2] = dstY[1]; + } + SkIRect s; SkRect d; for (int y = 0; y < 3; y++) { @@ -278,6 +290,17 @@ void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds, xDivs[1] = bitmap.width() - margins.fRight; yDivs[0] = margins.fTop; yDivs[1] = bitmap.height() - margins.fBottom; + + if (xDivs[0] > xDivs[1]) { + xDivs[0] = bitmap.width() * margins.fLeft / + (margins.fLeft + margins.fRight); + xDivs[1] = xDivs[0]; + } + if (yDivs[0] > yDivs[1]) { + yDivs[0] = bitmap.height() * margins.fTop / + (margins.fTop + margins.fBottom); + yDivs[1] = yDivs[0]; + } SkNinePatch::DrawMesh(canvas, bounds, bitmap, xDivs, 2, yDivs, 2, paint);