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);