Add the tile-based decoding capability to the PNG decoder.

Change-Id: Ie506dbf914cca9ad8a55747657abbe2a245ef217
diff --git a/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h
index 767a09a..3bb287d 100644
--- a/include/images/SkImageDecoder.h
+++ b/include/images/SkImageDecoder.h
@@ -216,7 +216,6 @@
     virtual bool decodeRegion(SkBitmap* bitmap, SkIRect rect,
                               SkBitmap::Config pref);
 
-
     /** Given a stream, this will try to find an appropriate decoder object.
         If none is found, the method returns NULL.
     */
@@ -318,6 +317,26 @@
         return false;
     }
 
+    /*
+     * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dest are
+     * both sampled by sampleSize from an original Bitmap.
+     *
+     * @param dest the destination Bitmap.
+     * @param src the source Bitmap that is sampled by sampleSize from the original
+     *            Bitmap.
+     * @param sampleSize the sample size that src is sampled from the original Bitmap.
+     * @param (srcX, srcY) the upper-left point of the src Btimap in terms of
+     *                     the coordinate in the original Bitmap.
+     * @param (width, height) the width and height of the unsampled dest.
+     * @param (destX, destY) the upper-left point of the dest Bitmap in terms of
+     *                       the coordinate in the original Bitmap.
+     */
+    virtual void cropBitmap(SkBitmap *dest, SkBitmap *src, int sampleSize,
+                            int destX, int destY, int width, int height,
+                            int srcX, int srcY);
+
+
+
     /** Can be queried from within onDecode, to see if the user (possibly in
         a different thread) has requested the decode to cancel. If this returns
         true, your onDecode() should stop and return false.
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 053caea..7713e86 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -196,6 +196,26 @@
     return this->onBuildTileIndex(stream, width, height);
 }
 
+void SkImageDecoder::cropBitmap(SkBitmap *dest, SkBitmap *src,
+                                    int sampleSize, int destX, int destY,
+                                    int width, int height, int srcX, int srcY) {
+    int w = width / sampleSize;
+    int h = height / sampleSize;
+    if (w == src->width() && h == src->height() &&
+          (srcX - destX) / sampleSize == 0 && (srcY - destY) / sampleSize == 0) {
+        // The output rect is the same as the decode result
+        dest->swap(*src);
+        return;
+    }
+    dest->setConfig(src->getConfig(), w, h);
+    dest->setIsOpaque(src->isOpaque());
+    this->allocPixelRef(dest, NULL);
+
+    SkCanvas canvas(*dest);
+    canvas.drawBitmap(*src, (srcX - destX) / sampleSize,
+                             (srcY - destY) / sampleSize);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
@@ -241,4 +261,3 @@
     }
     return success;
 }
-
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 0565a3b..e3aefea 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -77,15 +77,11 @@
     virtual Format getFormat() const {
         return kJPEG_Format;
     }
-
 protected:
     virtual bool onBuildTileIndex(SkStream *stream,
                                 int *width, int *height);
     virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
-    virtual void cropBitmap(SkBitmap *dest, SkBitmap *src, int sampleSize,
-                            int srcX, int srcY, int width, int height,
-                            int destX, int destY);
 private:
     SkJPEGImageIndex *index;
 };
@@ -511,41 +507,6 @@
     return true;
 }
 
-/*
- * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dest are
- * both sampled by sampleSize from an original Bitmap.
- *
- * @param dest the destination Bitmap.
- * @param src the source Bitmap that is sampled by sampleSize from the original
- *            Bitmap.
- * @param sampleSize the sample size that src is sampled from the original Bitmap.
- * @param (srcX, srcY) the upper-left point of the src Btimap in terms of
- *                     the coordinate in the original Bitmap.
- * @param (width, height) the width and height of the unsampled dest.
- * @param (destX, destY) the upper-left point of the dest Bitmap in terms of
- *                       the coordinate in the original Bitmap.
- */
-void SkJPEGImageDecoder::cropBitmap(SkBitmap *dest, SkBitmap *src,
-                                    int sampleSize, int srcX, int srcY,
-                                    int width, int height, int destX, int destY)
-{
-    int w = width / sampleSize;
-    int h = height / sampleSize;
-    if (w == src->width() && h == src->height() &&
-          (destX - srcX) / sampleSize == 0 && (destY - srcY) / sampleSize == 0) {
-        // The output rect is the same as the decode result
-        dest->swap( *src );
-        return;
-    }
-    dest->setConfig(src->getConfig(), w, h);
-    dest->setIsOpaque(true);
-    this->allocPixelRef(dest, NULL);
-
-    SkCanvas canvas(*dest);
-    canvas.drawBitmap(*src, (destX - srcX) / sampleSize,
-                             (destY - srcY) / sampleSize);
-}
-
 bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
     if (index == NULL) {
         return false;
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index 3548fc8..7021a07 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -30,14 +30,54 @@
 #include "png.h"
 }
 
+class SkPNGImageIndex {
+public:
+    SkPNGImageIndex() {
+        inputStream = NULL;
+        png_ptr = NULL;
+    }
+    virtual ~SkPNGImageIndex() {
+        if (png_ptr) {
+            png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+        }
+        if (inputStream) {
+            delete inputStream;
+        }
+    }
+    png_structp png_ptr;
+    png_infop info_ptr;
+    SkStream *inputStream;
+};
+
 class SkPNGImageDecoder : public SkImageDecoder {
 public:
+    SkPNGImageDecoder() {
+        index = NULL;
+    }
     virtual Format getFormat() const {
         return kPNG_Format;
     }
-    
+    virtual ~SkPNGImageDecoder() {
+        if (index) {
+            delete index;
+        }
+    }
+    virtual bool buildTileIndex(SkStream *stream,
+             int *width, int *height, bool isShareable);
+
 protected:
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+
+private:
+    bool onDecodeInit(SkStream* stream, png_structp *png_ptrp,
+            png_infop *info_ptrp);
+    bool decodePalette(png_structp png_ptr, png_infop info_ptr,
+        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep);
+    bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
+        SkBitmap::Config *config, bool *hasAlpha, bool *doDither,
+        SkPMColor *theTranspColor);
+    SkPNGImageIndex *index;
 };
 
 #ifndef png_jmpbuf
@@ -65,6 +105,12 @@
     }
 }
 
+static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
+    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
+    sk_stream->rewind();
+    (void)sk_stream->skip(offset);
+}
+
 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
     SkImageDecoder::Peeker* peeker =
                     (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
@@ -135,10 +181,9 @@
     return false;
 }
 
-bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
-                                 Mode mode) {
-//    SkAutoTrace    apr("SkPNGImageDecoder::onDecode");
-
+bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream,
+        png_structp *png_ptrp, png_infop *info_ptrp)
+{
     /* Create and initialize the png_struct with the desired error handler
     * functions.  If you want to use the default stderr and longjump method,
     * you can supply NULL for the last three parameters.  We also supply the
@@ -150,6 +195,7 @@
     if (png_ptr == NULL) {
         return false;
     }
+    *png_ptrp = png_ptr;
 
     /* Allocate/initialize the memory for image information. */
     png_infop info_ptr = png_create_info_struct(png_ptr);
@@ -157,8 +203,7 @@
         png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
         return false;
     }
-
-    PNGAutoClean autoClean(png_ptr, info_ptr);
+    *info_ptrp = info_ptr;
 
     /* Set error handling if you are using the setjmp/longjmp method (this is
     * the normal method of doing things with libpng).  REQUIRED unless you
@@ -172,9 +217,10 @@
     * png_init_io() here you would call:
     */
     png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
+    png_set_seek_fn(png_ptr, sk_seek_fn);
     /* where user_io_ptr is a structure you want available to the callbacks */
     /* If we have already read some of the signature */
-//  png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
+    // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
 
     // hookup our peeker so we can see any user-chunks the caller may be interested in
     png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
@@ -187,8 +233,8 @@
     png_read_info(png_ptr, info_ptr);
     png_uint_32 origWidth, origHeight;
     int bit_depth, color_type, interlace_type;
-    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type,
-        &interlace_type, int_p_NULL, int_p_NULL);
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
 
     /* tell libpng to strip 16 bit/color files down to 8 bits/color */
     if (bit_depth == 16) {
@@ -203,112 +249,45 @@
     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
         png_set_gray_1_2_4_to_8(png_ptr);
     }
-    
+
     /* Make a grayscale image into RGB. */
     if (color_type == PNG_COLOR_TYPE_GRAY ||
         color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
         png_set_gray_to_rgb(png_ptr);
     }
-        
+    return true;
+}
+
+bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
+                                 Mode mode) {
+    png_structp png_ptr;
+    png_infop info_ptr;
+
+    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
+        return false;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    PNGAutoClean autoClean(png_ptr, info_ptr);
+
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
     SkBitmap::Config    config;
     bool                hasAlpha = false;
     bool                doDither = this->getDitherImage();
     SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
-    
-    // check for sBIT chunk data, in case we should disable dithering because
-    // our data is not truely 8bits per component
-    if (doDither) {
-#if 0
-        SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
-                 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
-                 info_ptr->sig_bit.alpha);
-#endif
-        // 0 seems to indicate no information available
-        if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
-                pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
-                pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
-            doDither = false;
-        }
-    }
-    
-    if (color_type == PNG_COLOR_TYPE_PALETTE) {
-        bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
-        config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
-        // now see if we can upscale to their requested config
-        if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) {
-            config = SkBitmap::kIndex8_Config;
-        }
-    } else {
-        png_color_16p   transpColor = NULL;
-        int             numTransp = 0;
-        
-        png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
-        
-        bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
-        
-        if (valid && numTransp == 1 && transpColor != NULL) {
-            /*  Compute our transparent color, which we'll match against later.
-                We don't really handle 16bit components properly here, since we
-                do our compare *after* the values have been knocked down to 8bit
-                which means we will find more matches than we should. The real
-                fix seems to be to see the actual 16bit components, do the
-                compare, and then knock it down to 8bits ourselves.
-            */
-            if (color_type & PNG_COLOR_MASK_COLOR) {
-                if (16 == bit_depth) {
-                    theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
-                              transpColor->green >> 8, transpColor->blue >> 8);
-                } else {
-                    theTranspColor = SkPackARGB32(0xFF, transpColor->red,
-                                      transpColor->green, transpColor->blue);
-                }
-            } else {    // gray
-                if (16 == bit_depth) {
-                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
-                              transpColor->gray >> 8, transpColor->gray >> 8);
-                } else {
-                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
-                                          transpColor->gray, transpColor->gray);
-                }
-            }
-        }
 
-        if (valid ||
-                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
-                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
-            hasAlpha = true;
-        }
-        config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
-        // now match the request against our capabilities
-        if (hasAlpha) {
-            if (config != SkBitmap::kARGB_4444_Config) {
-                config = SkBitmap::kARGB_8888_Config;
-            }
-        } else {
-            if (config != SkBitmap::kRGB_565_Config &&
-                config != SkBitmap::kARGB_4444_Config) {
-                config = SkBitmap::kARGB_8888_Config;
-            }
-        }
-    }
-
-    // sanity check for size
-    {
-        Sk64 size;
-        size.setMul(origWidth, origHeight);
-        if (size.isNeg() || !size.is32()) {
-            return false;
-        }
-        // now check that if we are 4-bytes per pixel, we also don't overflow
-        if (size.get32() > (0x7FFFFFFF >> 2)) {
-            return false;
-        }
-    }
-
-    if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
+    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
+                &doDither, &theTranspColor) == false) {
         return false;
     }
-    
+
     const int sampleSize = this->getSampleSize();
     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
 
@@ -317,7 +296,7 @@
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         return true;
     }
-    
+
     // from here down we are concerned with colortables and pixels
 
     // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
@@ -327,58 +306,10 @@
     SkColorTable* colorTable = NULL;
 
     if (color_type == PNG_COLOR_TYPE_PALETTE) {
-        int num_palette;
-        png_colorp palette;
-        png_bytep trans;
-        int num_trans;
-
-        png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
-        
-        /*  BUGGY IMAGE WORKAROUND
-            
-            We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
-            which is a problem since we use the byte as an index. To work around this we grow
-            the colortable by 1 (if its < 256) and duplicate the last color into that slot.
-        */
-        int colorCount = num_palette + (num_palette < 256);
-
-        colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
-
-        SkPMColor* colorPtr = colorTable->lockColors();
-        if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
-            png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
-            hasAlpha = (num_trans > 0);
-        } else {
-            num_trans = 0;
-            colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
-        }        
-        // check for bad images that might make us crash
-        if (num_trans > num_palette) {
-            num_trans = num_palette;
-        }
-
-        int index = 0;
-        int transLessThanFF = 0;
-
-        for (; index < num_trans; index++) {
-            transLessThanFF |= (int)*trans - 0xFF;
-            *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
-            palette++;
-        }
-        reallyHasAlpha |= (transLessThanFF < 0);
-
-        for (; index < num_palette; index++) {
-            *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
-            palette++;
-        }
-
-        // see BUGGY IMAGE WORKAROUND comment above
-        if (num_palette < 256) {
-            *colorPtr = colorPtr[-1];
-        }
-        colorTable->unlockColors(true);
+        decodePalette(png_ptr, info_ptr, &hasAlpha,
+                &reallyHasAlpha, &colorTable);
     }
-    
+
     SkAutoUnref aur(colorTable);
 
     if (!this->allocPixelRef(decodedBitmap,
@@ -386,16 +317,9 @@
                                 colorTable : NULL)) {
         return false;
     }
-    
+
     SkAutoLockPixels alp(*decodedBitmap);
 
-    /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
-//  if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-//      ; // png_set_swap_alpha(png_ptr);
-
-    /* swap bytes of 16 bit files to least significant byte first */
-    //   png_set_swap(png_ptr);
-
     /* Add filler (or alpha) byte (before/after each RGB triplet) */
     if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
         png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
@@ -405,7 +329,7 @@
     * png_read_image().  To see how to handle interlacing passes,
     * see the png_read_row() method below:
     */
-    const int number_passes = interlace_type != PNG_INTERLACE_NONE ? 
+    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
                         png_set_interlace_handling(png_ptr) : 1;
 
     /* Optional call to gamma correct and add the background to the palette
@@ -424,7 +348,7 @@
     } else {
         SkScaledBitmapSampler::SrcConfig sc;
         int srcBytesPerPixel = 4;
-        
+
         if (colorTable != NULL) {
             sc = SkScaledBitmapSampler::kIndex;
             srcBytesPerPixel = 1;
@@ -495,6 +419,385 @@
     return true;
 }
 
+bool SkPNGImageDecoder::buildTileIndex(SkStream* sk_stream,
+                int *width, int *height, bool isShareable) {
+    png_structp png_ptr;
+    png_infop   info_ptr;
+
+    this->index = new SkPNGImageIndex();
+
+    if (!isShareable) {
+        size_t len, inputLen = 0;
+        size_t bufferSize = 4096;
+        void *tmp = sk_malloc_throw(bufferSize);
+
+        while ((len = sk_stream->read(tmp + inputLen,
+                        bufferSize - inputLen)) != 0) {
+            inputLen += len;
+            if (inputLen == bufferSize) {
+                bufferSize *= 2;
+                tmp = sk_realloc_throw(tmp, bufferSize);
+            }
+        }
+        tmp = sk_realloc_throw(tmp, inputLen);
+
+        SkMemoryStream *mem_stream = new SkMemoryStream(tmp, inputLen, true);
+        this->index->inputStream = mem_stream;
+        sk_stream = mem_stream;
+    }
+
+    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
+        return false;
+    }
+
+    int bit_depth, color_type, interlace_type;
+    png_uint_32 origWidth, origHeight;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    *width = origWidth;
+    *height = origHeight;
+
+    png_build_index(png_ptr);
+    this->index->png_ptr = png_ptr;
+    this->index->info_ptr = info_ptr;
+    return true;
+}
+
+bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
+        SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp,
+        SkPMColor *theTranspColorp) {
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    // check for sBIT chunk data, in case we should disable dithering because
+    // our data is not truely 8bits per component
+    if (*doDitherp) {
+#if 0
+        SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
+                 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
+                 info_ptr->sig_bit.alpha);
+#endif
+        // 0 seems to indicate no information available
+        if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
+                pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
+                pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
+            *doDitherp = false;
+        }
+    }
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
+        *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
+        // now see if we can upscale to their requested config
+        if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
+            *configp = SkBitmap::kIndex8_Config;
+        }
+    } else {
+        png_color_16p   transpColor = NULL;
+        int             numTransp = 0;
+
+        png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
+
+        bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
+
+        if (valid && numTransp == 1 && transpColor != NULL) {
+            /*  Compute our transparent color, which we'll match against later.
+                We don't really handle 16bit components properly here, since we
+                do our compare *after* the values have been knocked down to 8bit
+                which means we will find more matches than we should. The real
+                fix seems to be to see the actual 16bit components, do the
+                compare, and then knock it down to 8bits ourselves.
+            */
+            if (color_type & PNG_COLOR_MASK_COLOR) {
+                if (16 == bit_depth) {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
+                              transpColor->green >> 8, transpColor->blue >> 8);
+                } else {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
+                                      transpColor->green, transpColor->blue);
+                }
+            } else {    // gray
+                if (16 == bit_depth) {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
+                              transpColor->gray >> 8, transpColor->gray >> 8);
+                } else {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
+                                          transpColor->gray, transpColor->gray);
+                }
+            }
+        }
+
+        if (valid ||
+                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
+                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
+            *hasAlphap = true;
+        }
+        *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
+        // now match the request against our capabilities
+        if (*hasAlphap) {
+            if (*configp != SkBitmap::kARGB_4444_Config) {
+                *configp = SkBitmap::kARGB_8888_Config;
+            }
+        } else {
+            if (*configp != SkBitmap::kRGB_565_Config &&
+                *configp != SkBitmap::kARGB_4444_Config) {
+                *configp = SkBitmap::kARGB_8888_Config;
+            }
+        }
+    }
+
+    // sanity check for size
+    {
+        Sk64 size;
+        size.setMul(origWidth, origHeight);
+        if (size.isNeg() || !size.is32()) {
+            return false;
+        }
+        // now check that if we are 4-bytes per pixel, we also don't overflow
+        if (size.get32() > (0x7FFFFFFF >> 2)) {
+            return false;
+        }
+    }
+
+    if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
+        return false;
+    }
+    return true;
+}
+
+bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
+        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep) {
+    int num_palette;
+    png_colorp palette;
+    png_bytep trans;
+    int num_trans;
+    bool reallyHasAlpha = false;
+    SkColorTable* colorTable = NULL;
+
+    png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+
+    /*  BUGGY IMAGE WORKAROUND
+
+        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
+        which is a problem since we use the byte as an index. To work around this we grow
+        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
+        */
+    int colorCount = num_palette + (num_palette < 256);
+
+    colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
+
+    SkPMColor* colorPtr = colorTable->lockColors();
+    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+        *hasAlphap = (num_trans > 0);
+    } else {
+        num_trans = 0;
+        colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+    }
+    // check for bad images that might make us crash
+    if (num_trans > num_palette) {
+        num_trans = num_palette;
+    }
+
+    int index = 0;
+    int transLessThanFF = 0;
+
+    for (; index < num_trans; index++) {
+        transLessThanFF |= (int)*trans - 0xFF;
+        *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
+        palette++;
+    }
+    reallyHasAlpha |= (transLessThanFF < 0);
+
+    for (; index < num_palette; index++) {
+        *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+        palette++;
+    }
+
+    // see BUGGY IMAGE WORKAROUND comment above
+    if (num_palette < 256) {
+        *colorPtr = colorPtr[-1];
+    }
+    colorTable->unlockColors(true);
+    *colorTablep = colorTable;
+    *reallyHasAlphap = reallyHasAlpha;
+    return true;
+}
+
+bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) {
+    int i;
+    png_structp png_ptr = this->index->png_ptr;
+    png_infop info_ptr = this->index->info_ptr;
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    int requestedHeight = rect.fBottom - rect.fTop;
+    int requestedWidth = rect.fRight - rect.fLeft;
+
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    SkBitmap::Config    config;
+    bool                hasAlpha = false;
+    bool                doDither = this->getDitherImage();
+    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
+
+    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
+                &doDither, &theTranspColor) == false) {
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, requestedHeight, sampleSize);
+
+    SkBitmap *decodedBitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap> adb(decodedBitmap);
+
+    decodedBitmap->setConfig(config, sampler.scaledWidth(),
+                             sampler.scaledHeight(), 0);
+
+    // from here down we are concerned with colortables and pixels
+
+    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
+    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
+    // draw lots faster if we can flag the bitmap has being opaque
+    bool reallyHasAlpha = false;
+    SkColorTable* colorTable = NULL;
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        decodePalette(png_ptr, info_ptr, &hasAlpha,
+                &reallyHasAlpha, &colorTable);
+    }
+
+    SkAutoUnref aur(colorTable);
+
+    if (!this->allocPixelRef(decodedBitmap,
+                             SkBitmap::kIndex8_Config == config ?
+                                colorTable : NULL)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    /* Add filler (or alpha) byte (before/after each RGB triplet) */
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
+        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+    }
+
+    /* Turn on interlace handling.  REQUIRED if you are not using
+    * png_read_image().  To see how to handle interlacing passes,
+    * see the png_read_row() method below:
+    */
+    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
+                        png_set_interlace_handling(png_ptr) : 1;
+
+    /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (ie you selected such a transform above).
+    */
+    png_ptr->pass = 0;
+    png_read_update_info(png_ptr, info_ptr);
+
+    SkDebugf("Request size %d %d\n", requestedWidth, requestedHeight);
+
+    int actualTop = rect.fTop;
+
+    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+        for (int i = 0; i < number_passes; i++) {
+            png_configure_decoder(png_ptr, &actualTop, i);
+            for (int j = 0; j < rect.fTop - actualTop; j++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+            for (png_uint_32 y = 0; y < origHeight; y++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+        }
+    } else {
+        SkScaledBitmapSampler::SrcConfig sc;
+        int srcBytesPerPixel = 4;
+
+        if (colorTable != NULL) {
+            sc = SkScaledBitmapSampler::kIndex;
+            srcBytesPerPixel = 1;
+        } else if (hasAlpha) {
+            sc = SkScaledBitmapSampler::kRGBA;
+        } else {
+            sc = SkScaledBitmapSampler::kRGBX;
+        }
+
+        /*  We have to pass the colortable explicitly, since we may have one
+            even if our decodedBitmap doesn't, due to the request that we
+            upscale png's palette to a direct model
+         */
+        SkAutoLockColors ctLock(colorTable);
+        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
+            return false;
+        }
+        const int height = decodedBitmap->height();
+
+        if (number_passes > 1) {
+            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
+            uint8_t* base = (uint8_t*)storage.get();
+            size_t rb = origWidth * srcBytesPerPixel;
+
+            for (int i = 0; i < number_passes; i++) {
+                png_configure_decoder(png_ptr, &actualTop, i);
+                for (int j = 0; j < rect.fTop - actualTop; j++) {
+                    uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                }
+                uint8_t* row = base;
+                for (png_uint_32 y = 0; y < requestedHeight; y++) {
+                    uint8_t* bmRow = row;
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                    row += rb;
+                }
+            }
+            // now sample it
+            base += sampler.srcY0() * rb;
+            for (int y = 0; y < height; y++) {
+                reallyHasAlpha |= sampler.next(base);
+                base += sampler.srcDY() * rb;
+            }
+        } else {
+            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
+            uint8_t* srcRow = (uint8_t*)storage.get();
+
+            png_configure_decoder(png_ptr, &actualTop, 0);
+            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+
+            for (int i = 0; i < rect.fTop - actualTop; i++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+            for (int y = 0; y < height; y++) {
+                uint8_t* tmp = srcRow;
+                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+                reallyHasAlpha |= sampler.next(srcRow);
+                if (y < height - 1) {
+                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
+                }
+            }
+        }
+    }
+    cropBitmap(bm, decodedBitmap, sampleSize, rect.fLeft, rect.fTop,
+                requestedWidth, requestedHeight, 0, rect.fTop);
+
+    if (0 != theTranspColor) {
+        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+    }
+    decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkColorPriv.h"