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
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index c54fb5d..a38cafa 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -462,10 +462,15 @@
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 16411d5..a4def5f 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -855,7 +855,7 @@
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 8375cc7..c0259af 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -119,6 +119,18 @@
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 046c4d9..b02d482 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -177,6 +177,11 @@
*/
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 eaa812f..55109bf 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -62,8 +62,10 @@
// See also SkTScopedPtr.
template <typename T> 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 @@
private:
T* fObj;
+ bool fDeleteWhenDone;
};
template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h
index 9c9896f..800f12e 100644
--- a/include/images/SkImageRef.h
+++ b/include/images/SkImageRef.h
@@ -57,6 +57,12 @@
*/
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 cc9d246..e9dd977 100644
--- a/include/images/SkJpegUtility.h
+++ b/include/images/SkJpegUtility.h
@@ -41,11 +41,13 @@
/* 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 a0ab52d..b302f5b 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -1186,7 +1186,7 @@
#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 @@
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 @@
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 f1dcb30..f500778 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 @@
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 @@
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 @@
src += 1;
dst += 1;
} while (--count > 0);
+#endif
}
}
@@ -56,11 +91,27 @@
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 f2d73e3..f74fbe3 100644
--- a/src/core/SkBlitter_A8.cpp
+++ b/src/core/SkBlitter_A8.cpp
@@ -249,7 +249,7 @@
}
SkA8_Shader_Blitter::~SkA8_Shader_Blitter() {
- SkSafeUnref(fXfermode);
+ if (fXfermode) SkSafeUnref(fXfermode);
sk_free(fBuffer);
}
@@ -342,7 +342,9 @@
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 55f16af..0be0d8f 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -1233,10 +1233,14 @@
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 @@
}
return NULL;
}
-
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index e096d63..c376412 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -128,3 +128,10 @@
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 cbe3eb4..21ee05e 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -272,6 +272,18 @@
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 8d0f15a..39e1a12 100644
--- a/src/images/SkFlipPixelRef.cpp
+++ b/src/images/SkFlipPixelRef.cpp
@@ -74,8 +74,8 @@
return SkNEW_ARGS(SkFlipPixelRef, (buffer));
}
-static SkPixelRef::Registrar::Registrar reg("SkFlipPixelRef",
- SkFlipPixelRef::Create);
+static SkPixelRef::Registrar reg("SkFlipPixelRef",
+ SkFlipPixelRef::Create);
///////////////////////////////////////////////////////////////////////////////
@@ -125,4 +125,3 @@
iter.next();
}
}
-
diff --git a/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp
index cb5ff21..e5ff395 100644
--- a/src/images/SkImageDecoder_Factory.cpp
+++ b/src/images/SkImageDecoder_Factory.cpp
@@ -71,4 +71,3 @@
}
return NULL;
}
-
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index d2470cc..75a9ee0 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -117,6 +117,11 @@
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 ed523bb..aac0b4b 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -223,13 +223,9 @@
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 @@
#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 16c2820..7d2d416 100644
--- a/src/images/SkImageRef.cpp
+++ b/src/images/SkImageRef.cpp
@@ -57,6 +57,16 @@
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 1f0bc43..1f44a84 100644
--- a/src/images/SkImageRef_GlobalPool.cpp
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -50,8 +50,8 @@
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 @@
SkAutoMutexAcquire ac(gImageRefMutex);
gGlobalImageRefPool.dump();
}
-
diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp
index e207592..a2fe9a8 100644
--- a/src/images/SkJpegUtility.cpp
+++ b/src/images/SkJpegUtility.cpp
@@ -21,6 +21,24 @@
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 @@
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 @@
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 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 @@
///////////////////////////////////////////////////////////////////////////////
-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 1138044..0a85c2d 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 @@
private:
GifFileType* fGIF;
- SavedImage* fCurrSavedImage;
+ int fCurrIndex;
+ int fLastDrawIndex;
+ SkBitmap fBackup;
};
static int Decode(GifFileType* fileType, GifByteType* out, int size) {
@@ -54,7 +57,8 @@
DGifCloseFile(fGIF);
fGIF = NULL;
}
- fCurrSavedImage = NULL;
+ fCurrIndex = -1;
+ fLastDrawIndex = -1;
}
SkGIFMovie::~SkGIFMovie()
@@ -105,29 +109,242 @@
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 @@
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 3ba38f7..32b78ef 100644
--- a/src/images/SkScaledBitmapSampler.cpp
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -93,6 +93,18 @@
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 @@
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 @@
fSrcPixelSize = 1;
index += 6;
break;
+ case SkScaledBitmapSampler::kRGB_565:
+ fSrcPixelSize = 2;
+ index += 8;
+ break;
default:
return false;
}
@@ -388,13 +408,13 @@
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 84a75ba..43f16694 100644
--- a/src/images/SkScaledBitmapSampler.h
+++ b/src/images/SkScaledBitmapSampler.h
@@ -21,7 +21,8 @@
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 a904bae..539d768 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 <sys/mman.h>
@@ -36,7 +37,7 @@
}
SkImageRef_ashmem::~SkImageRef_ashmem() {
- fCT->safeUnref();
+ SkSafeUnref(fCT);
this->closeFD();
}
@@ -201,3 +202,36 @@
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 193a01d..2c485e3 100644
--- a/src/ports/SkImageRef_ashmem.h
+++ b/src/ports/SkImageRef_ashmem.h
@@ -15,6 +15,13 @@
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 @@
virtual void onUnlockPixels();
private:
+ SkImageRef_ashmem(SkFlattenableReadBuffer&);
void closeFD();
SkColorTable* fCT;
diff --git a/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
index 3d85edc..0b601eb 100644
--- a/src/utils/SkNinePatch.cpp
+++ b/src/utils/SkNinePatch.cpp
@@ -235,15 +235,27 @@
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 @@
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);