Upstream Android modifications to the image encoders/decoders.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 14 Mar 2013 14:42:18 +0000 (14:42 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 14 Mar 2013 14:42:18 +0000 (14:42 +0000)
This CL does not update the libjpeg as that change is large enough
to warrant its own CL.

Author: djsollen@google.com

Reviewed By: reed@google.com,robertphillips@google.com,scroggo@google.com

Review URL: https://chromiumcodereview.appspot.com/12604006

git-svn-id: http://skia.googlecode.com/svn/trunk@8155 2bbb7eff-a529-9590-31e7-b0007b416f81

14 files changed:
DEPS
gyp/images.gyp
gyp/libwebp.gyp [new file with mode: 0644]
include/images/SkBitmapRegionDecoder.h [new file with mode: 0644]
include/images/SkImageDecoder.h
include/images/SkImageEncoder.h
src/images/SkBitmapRegionDecoder.cpp [new file with mode: 0644]
src/images/SkImageDecoder.cpp
src/images/SkImageDecoder_libbmp.cpp
src/images/SkImageDecoder_libgif.cpp
src/images/SkImageDecoder_libico.cpp
src/images/SkImageDecoder_libpng.cpp
src/images/SkImageDecoder_libwebp.cpp [new file with mode: 0644]
src/images/SkImageDecoder_wbmp.cpp

diff --git a/DEPS b/DEPS
index 408aab1a665bf8b0b4084049638337da8f6c2a63..6ade579e7934438b61677ae49f56a05094fc089b 100644 (file)
--- a/DEPS
+++ b/DEPS
@@ -15,6 +15,7 @@ deps = {
   "third_party/externals/libjpeg" : "http://src.chromium.org/svn/trunk/src/third_party/libjpeg@125399",
   "third_party/externals/jsoncpp" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp@248",
   "third_party/externals/jsoncpp-chromium" : "http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@125399",
+  "third_party/externals/libwebp" : "http://src.chromium.org/svn/trunk/src/third_party/libwebp@186718",
 }
 
 #hooks = [
index b5447a27a554ec42171b7197aab3d086354d8a0e..32c7459d80b7e919cb2496d0d15103042e05df76 100644 (file)
@@ -7,6 +7,7 @@
       'standalone_static_library': 1,
       'dependencies': [
         'libjpeg.gyp:*',
+        'libwebp.gyp:libwebp',
         'utils.gyp:utils',
       ],
       'export_dependent_settings': [
 
         '../src/images/bmpdecoderhelper.cpp',
         '../src/images/bmpdecoderhelper.h',
+
+        '../src/images/SkBitmapRegionDecoder.cpp',
+
         '../src/images/SkImageDecoder.cpp',
         '../src/images/SkImageDecoder_Factory.cpp',
-        '../src/images/SkImageDecoder_libjpeg.cpp',
         '../src/images/SkImageDecoder_libbmp.cpp',
         '../src/images/SkImageDecoder_libgif.cpp',
         '../src/images/SkImageDecoder_libico.cpp',
+        '../src/images/SkImageDecoder_libjpeg.cpp',
         '../src/images/SkImageDecoder_libpng.cpp',
+        '../src/images/SkImageDecoder_libwebp.cpp',
         '../src/images/SkImageDecoder_wbmp.cpp',
         '../src/images/SkImageEncoder.cpp',
         '../src/images/SkImageEncoder_Factory.cpp',
diff --git a/gyp/libwebp.gyp b/gyp/libwebp.gyp
new file mode 100644 (file)
index 0000000..ad3f47a
--- /dev/null
@@ -0,0 +1,174 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'use_system_libwebp%': 0,
+  },
+  'conditions': [
+    ['use_system_libwebp==0', {
+      'targets': [
+        {
+          'target_name': 'libwebp_dec',
+          'type': 'static_library',
+          'include_dirs': [
+              '../third_party/externals/libwebp',
+          ],
+          'sources': [
+            '../third_party/externals/libwebp/dec/alpha.c',
+            '../third_party/externals/libwebp/dec/buffer.c',
+            '../third_party/externals/libwebp/dec/frame.c',
+            '../third_party/externals/libwebp/dec/idec.c',
+            '../third_party/externals/libwebp/dec/io.c',
+            '../third_party/externals/libwebp/dec/layer.c',
+            '../third_party/externals/libwebp/dec/quant.c',
+            '../third_party/externals/libwebp/dec/tree.c',
+            '../third_party/externals/libwebp/dec/vp8.c',
+            '../third_party/externals/libwebp/dec/vp8l.c',
+            '../third_party/externals/libwebp/dec/webp.c',
+          ],
+          'cflags!': [
+            '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+          ],
+        },
+        {
+          'target_name': 'libwebp_dsp',
+          'type': 'static_library',
+          'include_dirs': [
+              '../third_party/externals/libwebp',
+          ],
+          'sources': [
+            '../third_party/externals/libwebp/dsp/cpu.c',
+            '../third_party/externals/libwebp/dsp/dec.c',
+            '../third_party/externals/libwebp/dsp/dec_sse2.c',
+            '../third_party/externals/libwebp/dsp/enc.c',
+            '../third_party/externals/libwebp/dsp/enc_sse2.c',
+            '../third_party/externals/libwebp/dsp/lossless.c',
+            '../third_party/externals/libwebp/dsp/upsampling.c',
+            '../third_party/externals/libwebp/dsp/upsampling_sse2.c',
+            '../third_party/externals/libwebp/dsp/yuv.c',
+          ],
+          'cflags!': [
+            '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+          ],
+          'conditions': [
+            ['skia_os == "android"', {
+              'dependencies' : [
+                'android_deps.gyp:cpu_features',
+              ],
+            }],
+          ],
+        },
+        {
+          'target_name': 'libwebp_dsp_neon',
+          'conditions': [
+            ['armv7 == 1', {
+              'type': 'static_library',
+              'include_dirs': [
+                  '../third_party/externals/libwebp',
+              ],
+              'sources': [
+                '../third_party/externals/libwebp/dsp/dec_neon.c',
+              ],
+              # behavior similar dsp_neon.c.neon in an Android.mk
+              'cflags!': [ 
+                '-mfpu=vfpv3-d16',
+                '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+              ],
+              'cflags': [ '-mfpu=neon' ],
+            },{  # "armv7 != 1"
+              'type': 'none',
+            }],
+          ],
+        },
+        {
+          'target_name': 'libwebp_enc',
+          'type': 'static_library',
+          'include_dirs': [
+              '../third_party/externals/libwebp',
+          ],
+          'sources': [
+            '../third_party/externals/libwebp/enc/alpha.c',
+            '../third_party/externals/libwebp/enc/analysis.c',
+            '../third_party/externals/libwebp/enc/backward_references.c',
+            '../third_party/externals/libwebp/enc/config.c',
+            '../third_party/externals/libwebp/enc/cost.c',
+            '../third_party/externals/libwebp/enc/filter.c',
+            '../third_party/externals/libwebp/enc/frame.c',
+            '../third_party/externals/libwebp/enc/histogram.c',
+            '../third_party/externals/libwebp/enc/iterator.c',
+            '../third_party/externals/libwebp/enc/layer.c',
+            '../third_party/externals/libwebp/enc/picture.c',
+            '../third_party/externals/libwebp/enc/quant.c',
+            '../third_party/externals/libwebp/enc/syntax.c',
+            '../third_party/externals/libwebp/enc/tree.c',
+            '../third_party/externals/libwebp/enc/vp8l.c',
+            '../third_party/externals/libwebp/enc/webpenc.c',
+          ],
+          'cflags!': [
+            '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+          ],
+        },
+        {
+          'target_name': 'libwebp_utils',
+          'type': 'static_library',
+          'include_dirs': [
+              '../third_party/externals/libwebp',
+          ],
+          'sources': [
+            '../third_party/externals/libwebp/utils/bit_reader.c',
+            '../third_party/externals/libwebp/utils/bit_writer.c',
+            '../third_party/externals/libwebp/utils/color_cache.c',
+            '../third_party/externals/libwebp/utils/filters.c',
+            '../third_party/externals/libwebp/utils/huffman.c',
+            '../third_party/externals/libwebp/utils/huffman_encode.c',
+            '../third_party/externals/libwebp/utils/quant_levels.c',
+            '../third_party/externals/libwebp/utils/rescaler.c',
+            '../third_party/externals/libwebp/utils/thread.c',
+            '../third_party/externals/libwebp/utils/utils.c',
+          ],
+          'cflags!': [
+            '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+          ],
+        },
+        {
+          'target_name': 'libwebp',
+          'type': 'none',
+          'dependencies' : [
+            'libwebp_dec',
+            'libwebp_dsp',
+            'libwebp_dsp_neon',
+            'libwebp_enc',
+            'libwebp_utils',
+          ],
+          'direct_dependent_settings': {
+            'include_dirs': [
+              '../third_party/externals/libwebp',
+            ],
+          },
+          'conditions': [
+            ['OS!="win"', {'product_name': 'webp'}],
+          ],
+        },
+      ],
+    }, {
+      'targets': [
+        {
+          'target_name': 'libwebp',
+          'type': 'none',
+          'direct_dependent_settings': {
+            'defines': [
+              'ENABLE_WEBP',
+            ],
+          },
+          'link_settings': {
+            'libraries': [
+              '-lwebp',
+            ],
+          },
+        }
+      ],
+    }],
+  ],
+}
diff --git a/include/images/SkBitmapRegionDecoder.h b/include/images/SkBitmapRegionDecoder.h
new file mode 100644 (file)
index 0000000..0e20a5a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkBitmapRegionDecoder_DEFINED
+#define SkBitmapRegionDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+class SkIRect;
+
+/**
+ * SkBitmapRegionDecoder can be used to decode a specified rect from an image.
+ * This is particularly useful when the original image is large and you only
+ * need parts of the image.
+ *
+ * However, not all image codecs on all platforms support this feature so be
+ * prepared to fallback to standard decoding if decodeRegion(...) returns false.
+ */
+class SkBitmapRegionDecoder {
+public:
+    SkBitmapRegionDecoder(SkImageDecoder* decoder, SkStream* stream,
+                          int width, int height) {
+        fDecoder = decoder;
+        fStream = stream;
+        fWidth = width;
+        fHeight = height;
+    }
+    ~SkBitmapRegionDecoder() {
+        SkDELETE(fDecoder);
+        SkSafeUnref(fStream);
+    }
+
+    bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
+                      SkBitmap::Config pref, int sampleSize);
+
+    SkImageDecoder* getDecoder() const { return fDecoder; }
+    int getWidth() const { return fWidth; }
+    int getHeight() const { return fHeight; }
+
+private:
+    SkImageDecoder* fDecoder;
+    SkStream* fStream;
+    int fWidth;
+    int fHeight;
+};
+
+#endif
index 6d1bb7241d5dd3aeb44e584d02840de23e7eb678..8981b9542b96b6107469c5822320edc3ef71fef6 100644 (file)
@@ -13,6 +13,7 @@
 #include "SkBitmap.h"
 #include "SkBitmapFactory.h"
 #include "SkImage.h"
+#include "SkRect.h"
 #include "SkRefCnt.h"
 
 class SkStream;
@@ -25,6 +26,7 @@ class SkImageDecoder {
 public:
     virtual ~SkImageDecoder();
 
+    // Should be consistent with kFormatName
     enum Format {
         kUnknown_Format,
         kBMP_Format,
@@ -33,14 +35,19 @@ public:
         kJPEG_Format,
         kPNG_Format,
         kWBMP_Format,
+        kWEBP_Format,
 
-        kLastKnownFormat = kWBMP_Format
+        kLastKnownFormat = kWEBP_Format
     };
 
     /** Return the compressed data's format (see Format enum)
     */
     virtual Format getFormat() const;
 
+    /** Return the compressed data's format name.
+    */
+    const char* getFormatName() const;
+
     /** Returns true if the decoder should try to dither the resulting image.
         The default setting is true.
     */
@@ -51,6 +58,20 @@ public:
     */
     void setDitherImage(bool dither) { fDitherImage = dither; }
 
+    /** Returns true if the decoder should try to decode the
+        resulting image to a higher quality even at the expense of
+        the decoding speed.
+    */
+    bool getPreferQualityOverSpeed() const { return fPreferQualityOverSpeed; }
+
+    /** Set to true if the the decoder should try to decode the
+        resulting image to a higher quality even at the expense of
+        the decoding speed.
+    */
+    void setPreferQualityOverSpeed(bool qualityOverSpeed) {
+        fPreferQualityOverSpeed = qualityOverSpeed;
+    }
+
     /** \class Peeker
 
         Base class for optional callbacks to retrieve meta/chunk data out of
@@ -175,11 +196,29 @@ public:
 
         note: document use of Allocator, Peeker and Chooser
     */
-    bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode);
-    bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode) {
-        return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode);
+    bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode, bool reuseBitmap = false);
+    bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode, bool reuseBitmap = false) {
+        return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode, reuseBitmap);
     }
 
+    /**
+     * Given a stream, build an index for doing tile-based decode.
+     * The built index will be saved in the decoder, and the image size will
+     * be returned in width and height.
+     *
+     * Return true for success or false on failure.
+     */
+    bool buildTileIndex(SkStream*, int *width, int *height);
+
+    /**
+     * Decode a rectangle region in the image specified by rect.
+     * The method can only be called after buildTileIndex().
+     *
+     * Return true for success.
+     * Return false if the index is never built or failing in decoding.
+     */
+    bool decodeRegion(SkBitmap* bitmap, const 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.
     */
@@ -296,6 +335,38 @@ protected:
     // must be overridden in subclasses. This guy is called by decode(...)
     virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
 
+    // If the decoder wants to support tiled based decoding,
+    // this method must be overridden. This guy is called by buildTileIndex(...)
+    virtual bool onBuildTileIndex(SkStream*, int *width, int *height) {
+        return false;
+    }
+
+    // If the decoder wants to support tiled based decoding,
+    // this method must be overridden. This guy is called by decodeRegion(...)
+    virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) {
+        return false;
+    }
+
+    /*
+     * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dst are
+     * both sampled by sampleSize from an original Bitmap.
+     *
+     * @param dst 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 (dstX, dstY) the upper-left point of the dest bitmap in terms of
+     *                     the coordinate in the original bitmap.
+     * @param (width, height) the width and height of the unsampled dst.
+     * @param (srcX, srcY) the upper-left point of the src bitimap in terms of
+     *                     the coordinate in the original bitmap.
+     */
+    void cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
+                    int dstX, int dstY, 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.
@@ -346,6 +417,14 @@ private:
     bool                    fDitherImage;
     bool                    fUsePrefTable;
     mutable bool            fShouldCancelDecode;
+    bool                    fPreferQualityOverSpeed;
+
+    /** Contains the image format name.
+     *  This should be consistent with Format.
+     *
+     *  The format name gives a more meaningful error message than enum.
+     */
+    static const char* sFormatName[];
 
     // illegal
     SkImageDecoder(const SkImageDecoder&);
@@ -396,5 +475,6 @@ DECLARE_DECODER_CREATOR(ICOImageDecoder);
 DECLARE_DECODER_CREATOR(JPEGImageDecoder);
 DECLARE_DECODER_CREATOR(PNGImageDecoder);
 DECLARE_DECODER_CREATOR(WBMPImageDecoder);
+DECLARE_DECODER_CREATOR(WEBPImageDecoder);
 
 #endif
index 3fbfefa5f7f6425bb5c2fa8cf04bd65c1d1077d1..d4d71695654b0a00182b673520abcb69d879aa30 100644 (file)
@@ -17,7 +17,8 @@ class SkImageEncoder {
 public:
     enum Type {
         kJPEG_Type,
-        kPNG_Type
+        kPNG_Type,
+        kWEBP_Type
     };
     static SkImageEncoder* Create(Type);
 
@@ -80,5 +81,6 @@ protected:
 // not all of these will be available
 DECLARE_ENCODER_CREATOR(JPEGImageEncoder);
 DECLARE_ENCODER_CREATOR(PNGImageEncoder);
+DECLARE_ENCODER_CREATOR(WEBPImageEncoder);
 
 #endif
diff --git a/src/images/SkBitmapRegionDecoder.cpp b/src/images/SkBitmapRegionDecoder.cpp
new file mode 100644 (file)
index 0000000..4cf1cca
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapRegionDecoder.h"
+
+bool SkBitmapRegionDecoder::decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
+                                         SkBitmap::Config pref, int sampleSize) {
+    fDecoder->setSampleSize(sampleSize);
+    return fDecoder->decodeRegion(bitmap, rect, pref);
+}
index 7b4267615fc563b5eb0752f7a353c667f4db23a6..1e3b03929fd21d9b67051f22be6f448d77fd3dea 100644 (file)
 #include "SkPixelRef.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
+#include "SkCanvas.h"
 
 SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker)
 SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser)
 SK_DEFINE_INST_COUNT(SkImageDecoderFactory)
 
+const char *SkImageDecoder::sFormatName[] = {
+    "Unknown Format",
+    "BMP",
+    "GIF",
+    "ICO",
+    "JPEG",
+    "PNG",
+    "WBMP",
+    "WEBP",
+};
+
 static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
 
 SkBitmap::Config SkImageDecoder::GetDeviceConfig()
@@ -34,7 +46,7 @@ void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
 SkImageDecoder::SkImageDecoder()
     : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
       fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
-      fUsePrefTable(false) {
+      fUsePrefTable(false),fPreferQualityOverSpeed(false) {
 }
 
 SkImageDecoder::~SkImageDecoder() {
@@ -47,6 +59,11 @@ SkImageDecoder::Format SkImageDecoder::getFormat() const {
     return kUnknown_Format;
 }
 
+const char* SkImageDecoder::getFormatName() const {
+    SkASSERT(SK_ARRAY_COUNT(sFormatName) == kLastKnownFormat);
+    return sFormatName[this->getFormat()];
+}
+
 SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
     SkRefCnt_SafeAssign(fPeeker, peeker);
     return peeker;
@@ -129,16 +146,22 @@ SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
 }
 
 bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
-                            SkBitmap::Config pref, Mode mode) {
-    // pass a temporary bitmap, so that if we return false, we are assured of
-    // leaving the caller's bitmap untouched.
-    SkBitmap    tmp;
-
+                            SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
     // we reset this to false before calling onDecode
     fShouldCancelDecode = false;
     // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
     fDefaultPref = pref;
 
+    if (reuseBitmap) {
+        SkAutoLockPixels alp(*bm);
+        if (NULL != bm->getPixels()) {
+            return this->onDecode(stream, bm, mode);
+        }
+    }
+
+    // pass a temporary bitmap, so that if we return false, we are assured of
+    // leaving the caller's bitmap untouched.
+    SkBitmap    tmp;
     if (!this->onDecode(stream, &tmp, mode)) {
         return false;
     }
@@ -146,6 +169,55 @@ bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
     return true;
 }
 
+bool SkImageDecoder::decodeRegion(SkBitmap* bm, const SkIRect& rect,
+                                  SkBitmap::Config pref) {
+    // we reset this to false before calling onDecodeRegion
+    fShouldCancelDecode = false;
+    // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
+    fDefaultPref = pref;
+
+    return this->onDecodeRegion(bm, rect);
+}
+
+bool SkImageDecoder::buildTileIndex(SkStream* stream,
+                                int *width, int *height) {
+    // we reset this to false before calling onBuildTileIndex
+    fShouldCancelDecode = false;
+
+    return this->onBuildTileIndex(stream, width, height);
+}
+
+void SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
+                               int dstX, int dstY, int width, int height,
+                               int srcX, int srcY) {
+    int w = width / sampleSize;
+    int h = height / sampleSize;
+    // if the destination has no pixels then we must allocate them.
+    if (dst->isNull()) {
+        dst->setConfig(src->getConfig(), w, h);
+        dst->setIsOpaque(src->isOpaque());
+
+        if (!this->allocPixelRef(dst, NULL)) {
+            SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
+            return;
+        }
+    }
+    // check to see if the destination is large enough to decode the desired
+    // region. If this assert fails we will just draw as much of the source
+    // into the destination that we can.
+    SkASSERT(dst->width() >= w && dst->height() >= h);
+
+    // Set the Src_Mode for the paint to prevent transparency issue in the
+    // dest in the event that the dest was being re-used.
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+
+    SkCanvas canvas(*dst);
+    canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
+                            (srcY - dstY) / sampleSize,
+                            &paint);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
index 3aa834a6b567182e30646d89519b12f14133c21e..1dcea8ad64c38bc7b8cbc06ec9a3ad6e7b5fabc6 100644 (file)
@@ -19,12 +19,15 @@ class SkBMPImageDecoder : public SkImageDecoder {
 public:
     SkBMPImageDecoder() {}
 
-    virtual Format getFormat() const {
+    virtual Format getFormat() const SK_OVERRIDE {
         return kBMP_Format;
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+
+private:
+    typedef SkImageDecoder INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -115,11 +118,18 @@ bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 
     SkScaledBitmapSampler sampler(width, height, getSampleSize());
 
-    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
-    bm->setIsOpaque(true);
     if (justBounds) {
+        bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+        bm->setIsOpaque(true);
         return true;
     }
+    // No Bitmap reuse supported for this format
+    if (!bm->isNull()) {
+        return false;
+    }
+
+    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+    bm->setIsOpaque(true);
 
     if (!this->allocPixelRef(bm, NULL)) {
         return false;
index f81c601093851a9ed77611737572167f4ee98211..02bc67b7debeab463771ac107ea2a79730ada983 100644 (file)
 
 class SkGIFImageDecoder : public SkImageDecoder {
 public:
-    virtual Format getFormat() const {
+    virtual Format getFormat() const SK_OVERRIDE {
         return kGIF_Format;
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+
+private:
+    typedef SkImageDecoder INHERITED;
 };
 
 static const uint8_t gStartingIterlaceYValue[] = {
@@ -201,11 +204,18 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
                                            width, height)) {
                 return error_return(gif, *bm, "chooseFromOneChoice");
             }
-
-            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
-            if (SkImageDecoder::kDecodeBounds_Mode == mode)
+            
+            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+                bm->setConfig(SkBitmap::kIndex8_Config, width, height);
                 return true;
+            }
+
+            // No Bitmap reuse supported for this format
+            if (!bm->isNull()) {
+                return false;
+            }
 
+            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
             const GifImageDesc& desc = image->ImageDesc;
 
index edfaa2e62b3023fa6d4c9e4d5ac2cf7438c2f64d..14f575babab4162f9db3706e6350af9479692a1c 100644 (file)
@@ -16,19 +16,16 @@ class SkICOImageDecoder : public SkImageDecoder {
 public:
     SkICOImageDecoder();
 
-    virtual Format getFormat() const {
+    virtual Format getFormat() const SK_OVERRIDE {
         return kICO_Format;
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
-};
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
-#if 0 // UNUSED
-SkImageDecoder* SkCreateICOImageDecoder() {
-    return new SkICOImageDecoder;
-}
-#endif
+private:
+    typedef SkImageDecoder INHERITED;
+};
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -235,12 +232,16 @@ bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode)
     //if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap
     //however, with small images with large colortables, maybe it's better to still do argb_8888
 
-    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
-
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
         delete[] colors;
         return true;
     }
+    // No Bitmap reuse supported for this format
+    if (!bm->isNull()) {
+        return false;
+    }
+    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
 
     if (!this->allocPixelRef(bm, NULL))
     {
index cca69fac1049a2b0e31073075629fb7fb5a89b89..501a0d69f7f2f571eeee30d4ef9ea0a959dda52b 100644 (file)
@@ -23,14 +23,52 @@ extern "C" {
 #include "png.h"
 }
 
+class SkPNGImageIndex {
+public:
+    SkPNGImageIndex(png_structp png_ptr, png_infop info_ptr) {
+        this->png_ptr = png_ptr;
+        this->info_ptr = info_ptr;
+    }
+    ~SkPNGImageIndex() {
+        if (NULL != png_ptr) {
+            png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+        }
+    }
+
+    png_structp png_ptr;
+    png_infop info_ptr;
+};
+
 class SkPNGImageDecoder : public SkImageDecoder {
 public:
-    virtual Format getFormat() const {
+    SkPNGImageDecoder() {
+        fImageIndex = NULL;
+    }
+    virtual Format getFormat() const SK_OVERRIDE {
         return kPNG_Format;
     }
+    virtual ~SkPNGImageDecoder() {
+        SkDELETE(fImageIndex);
+    }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+#ifdef SK_BUILD_FOR_ANDROID
+    virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
+    virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
+#endif
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+
+private:
+    SkPNGImageIndex* fImageIndex;
+
+    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);
+
+    typedef SkImageDecoder INHERITED;
 };
 
 #ifndef png_jmpbuf
@@ -43,7 +81,7 @@ protected:
 struct PNGAutoClean {
     PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
     ~PNGAutoClean() {
-        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
     }
 private:
     png_structp png_ptr;
@@ -51,13 +89,21 @@ private:
 };
 
 static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
-    SkStream* sk_stream = (SkStream*)png_get_io_ptr(png_ptr);
+    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
     size_t bytes = sk_stream->read(data, length);
     if (bytes != length) {
         png_error(png_ptr, "Read Error!");
     }
 }
 
+#ifdef SK_BUILD_FOR_ANDROID
+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);
+}
+#endif
+
 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);
@@ -67,16 +113,14 @@ static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
 }
 
 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
-#if 0
-    SkDebugf("------ png error %s\n", msg);
-#endif
+    SkDEBUGF(("------ png error %s\n", msg));
     longjmp(png_jmpbuf(png_ptr), 1);
 }
 
 static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
     for (int i = 0; i < count; i++) {
         uint8_t* tmp = storage;
-        png_read_rows(png_ptr, &tmp, NULL, 1);
+        png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
     }
 }
 
@@ -128,10 +172,8 @@ static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
     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
@@ -143,15 +185,15 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
     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);
     if (info_ptr == NULL) {
-        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        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
@@ -165,6 +207,9 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
     * png_init_io() here you would call:
     */
     png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
+#ifdef SK_BUILD_FOR_ANDROID
+    png_set_seek_fn(png_ptr, sk_seek_fn);
+#endif
     /* 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 */ );
@@ -179,65 +224,242 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
     * PNG file before the first IDAT (image data chunk). */
     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, NULL, NULL);
+    int bitDepth, colorType;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+                 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
 
     /* tell libpng to strip 16 bit/color files down to 8 bits/color */
-    if (bit_depth == 16) {
+    if (bitDepth == 16) {
         png_set_strip_16(png_ptr);
     }
     /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
      * byte into separate bytes (useful for paletted and grayscale images). */
-    if (bit_depth < 8) {
+    if (bitDepth < 8) {
         png_set_packing(png_ptr);
     }
     /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
-    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
-        png_set_expand_gray_1_2_4_to_8(png_ptr);
+    if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 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) {
+    if (colorType == PNG_COLOR_TYPE_GRAY || colorType == 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)) {
+        return false;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    PNGAutoClean autoClean(png_ptr, info_ptr);
+
+    png_uint_32 origWidth, origHeight;
+    int bitDepth, colorType, interlaceType;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+                 &colorType, &interlaceType, 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)) {
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+    decodedBitmap->lockPixels();
+    void* rowptr = (void*) decodedBitmap->getPixels();
+    bool reuseBitmap = (rowptr != NULL);
+    decodedBitmap->unlockPixels();
+    if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
+                        sampler.scaledHeight() != decodedBitmap->height())) {
+        // Dimensions must match
+        return false;
+    }
+
+    if (!reuseBitmap) {
+        decodedBitmap->setConfig(config, sampler.scaledWidth(),
+                                 sampler.scaledHeight(), 0);
+    }
+    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
+    // 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 (colorType == PNG_COLOR_TYPE_PALETTE) {
+        decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
+    }
+
+    SkAutoUnref aur(colorTable);
+
+    if (!reuseBitmap) {
+        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 (colorType == PNG_COLOR_TYPE_RGB || colorType == 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 = (interlaceType != 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_read_update_info(png_ptr, info_ptr);
+
+    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+        for (int i = 0; i < number_passes; i++) {
+            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 rowBytes = origWidth * srcBytesPerPixel;
+
+            for (int i = 0; i < number_passes; i++) {
+                uint8_t* row = base;
+                for (png_uint_32 y = 0; y < origHeight; y++) {
+                    uint8_t* bmRow = row;
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                    row += rowBytes;
+                }
+            }
+            // now sample it
+            base += sampler.srcY0() * rowBytes;
+            for (int y = 0; y < height; y++) {
+                reallyHasAlpha |= sampler.next(base);
+                base += sampler.srcDY() * rowBytes;
+            }
+        } else {
+            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
+            uint8_t* srcRow = (uint8_t*)storage.get();
+            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+
+            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);
+                }
+            }
+
+            // skip the rest of the rows (if any)
+            png_uint_32 read = (height - 1) * sampler.srcDY() +
+                               sampler.srcY0() + 1;
+            SkASSERT(read <= origHeight);
+            skip_src_rows(png_ptr, srcRow, origHeight - read);
+        }
+    }
+
+    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+    png_read_end(png_ptr, info_ptr);
+
+    if (0 != theTranspColor) {
+        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+    }
+    decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    if (reuseBitmap) {
+        decodedBitmap->notifyPixelsChanged();
+    }
+    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 bitDepth, colorType;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+                 &colorType, int_p_NULL, 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 (doDither) {
-        png_color_8p sig_bit = NULL;
-        bool has_sbit = PNG_INFO_sBIT == png_get_sBIT(png_ptr, info_ptr,
-                                                      &sig_bit);
+    if (*doDitherp) {
 #if 0
-        if (has_sbit) {
-            SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
-                     sig_bit->blue, sig_bit->alpha);
-        }
+        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 (has_sbit && pos_le(sig_bit->red, SK_R16_BITS) &&
-                pos_le(sig_bit->green, SK_G16_BITS) &&
-                pos_le(sig_bit->blue, SK_B16_BITS)) {
-            doDither = false;
+        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) {
+    if (colorType == PNG_COLOR_TYPE_PALETTE) {
         bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
-        config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
+        *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
         // now see if we can upscale to their requested config
-        if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) {
-            config = SkBitmap::kIndex8_Config;
+        if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
+            *configp = SkBitmap::kIndex8_Config;
         }
     } else {
-        png_color_16p   transpColor = NULL;
-        int             numTransp = 0;
+        png_color_16p transpColor = NULL;
+        int numTransp = 0;
 
         png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
 
@@ -251,40 +473,44 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
                 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);
+            if (colorType & PNG_COLOR_MASK_COLOR) {
+                if (16 == bitDepth) {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
+                                                    transpColor->green >> 8,
+                                                    transpColor->blue >> 8);
                 } else {
-                    theTranspColor = SkPackARGB32(0xFF, transpColor->red,
-                                      transpColor->green, transpColor->blue);
+                    *theTranspColorp = 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);
+                if (16 == bitDepth) {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
+                                                    transpColor->gray >> 8,
+                                                    transpColor->gray >> 8);
                 } else {
-                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
-                                          transpColor->gray, transpColor->gray);
+                    *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) {
-            hasAlpha = true;
+            PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
+            PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
+            *hasAlphap = true;
         }
-        config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
+        *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
         // now match the request against our capabilities
-        if (hasAlpha) {
-            if (config != SkBitmap::kARGB_4444_Config) {
-                config = SkBitmap::kARGB_8888_Config;
+        if (*hasAlphap) {
+            if (*configp != SkBitmap::kARGB_4444_Config) {
+                *configp = SkBitmap::kARGB_8888_Config;
             }
         } else {
-            if (config != SkBitmap::kRGB_565_Config &&
-                config != SkBitmap::kARGB_4444_Config) {
-                config = SkBitmap::kARGB_8888_Config;
+            if (*configp != SkBitmap::kRGB_565_Config &&
+                *configp != SkBitmap::kARGB_4444_Config) {
+                *configp = SkBitmap::kARGB_8888_Config;
             }
         }
     }
@@ -302,99 +528,172 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
         }
     }
 
-    if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
-        return false;
+    return this->chooseFromOneChoice(*configp, origWidth, origHeight);
+}
+
+bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
+                                      bool *hasAlphap, bool *reallyHasAlphap,
+                                      SkColorTable **colorTablep) {
+    int numPalette;
+    png_colorp palette;
+    png_bytep trans;
+    int numTrans;
+    bool reallyHasAlpha = false;
+    SkColorTable* colorTable = NULL;
+
+    png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
+
+    /*  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 = numPalette + (numPalette < 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, &numTrans, NULL);
+        *hasAlphap = (numTrans > 0);
+    } else {
+        numTrans = 0;
+        colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+    }
+    // check for bad images that might make us crash
+    if (numTrans > numPalette) {
+        numTrans = numPalette;
     }
 
-    const int sampleSize = this->getSampleSize();
-    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+    int index = 0;
+    int transLessThanFF = 0;
 
-    decodedBitmap->setConfig(config, sampler.scaledWidth(),
-                             sampler.scaledHeight(), 0);
-    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+    for (; index < numTrans; index++) {
+        transLessThanFF |= (int)*trans - 0xFF;
+        *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
+        palette++;
     }
+    reallyHasAlpha |= (transLessThanFF < 0);
 
-    // from here down we are concerned with colortables and pixels
+    for (; index < numPalette; index++) {
+        *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+        palette++;
+    }
 
-    // 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;
+    // see BUGGY IMAGE WORKAROUND comment above
+    if (numPalette < 256) {
+        *colorPtr = colorPtr[-1];
+    }
+    colorTable->unlockColors(true);
+    *colorTablep = colorTable;
+    *reallyHasAlphap = reallyHasAlpha;
+    return true;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
 
-    if (color_type == PNG_COLOR_TYPE_PALETTE) {
-        int num_palette;
-        png_colorp palette;
-        png_bytep trans;
-        int num_trans;
+bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *height) {
+    png_structp png_ptr;
+    png_infop   info_ptr;
 
-        png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+    if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
+        return false;
+    }
 
-        /*  BUGGY IMAGE WORKAROUND
+    if (setjmp(png_jmpbuf(png_ptr)) != 0) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+        return false;
+    }
 
-            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);
+    png_uint_32 origWidth, origHeight;
+    int bitDepth, colorType;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+                 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
 
-        colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
+    *width = origWidth;
+    *height = origHeight;
 
-        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;
-        }
+    png_build_index(png_ptr);
 
-        int index = 0;
-        int transLessThanFF = 0;
+    if (fImageIndex) {
+        SkDELETE(fImageIndex);
+    }
+    fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (png_ptr, info_ptr));
 
-        for (; index < num_trans; index++) {
-            transLessThanFF |= (int)*trans - 0xFF;
-            *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
-            palette++;
-        }
-        reallyHasAlpha |= (transLessThanFF < 0);
+    return true;
+}
 
-        for (; index < num_palette; index++) {
-            *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
-            palette++;
-        }
+bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, const SkIRect& region) {
+    png_structp png_ptr = fImageIndex->png_ptr;
+    png_infop info_ptr = fImageIndex->info_ptr;
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
 
-        // see BUGGY IMAGE WORKAROUND comment above
-        if (num_palette < 256) {
-            *colorPtr = colorPtr[-1];
-        }
-        colorTable->unlockColors(true);
+    png_uint_32 origWidth, origHeight;
+    int bitDepth, colorType, interlaceType;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+                 &colorType, &interlaceType, int_p_NULL, int_p_NULL);
+
+    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
+
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
+        return false;
     }
 
-    SkAutoUnref aur(colorTable);
+    SkBitmap::Config    config;
+    bool                hasAlpha = false;
+    bool                doDither = this->getDitherImage();
+    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
 
-    if (!this->allocPixelRef(decodedBitmap,
-                             SkBitmap::kIndex8_Config == config ?
-                                colorTable : NULL)) {
+    if (!getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, &theTranspColor)) {
         return false;
     }
 
-    SkAutoLockPixels alp(*decodedBitmap);
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
+
+    SkBitmap 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 (colorType == PNG_COLOR_TYPE_PALETTE) {
+        decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
+    }
 
-    /* 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);
+    SkAutoUnref aur(colorTable);
 
-    /* swap bytes of 16 bit files to least significant byte first */
-    //   png_set_swap(png_ptr);
+    // Check ahead of time if the swap(dest, src) is possible.
+    // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
+    // If no, then we will use alloc to allocate pixels to prevent garbage collection.
+    int w = rect.width() / sampleSize;
+    int h = rect.height() / sampleSize;
+    const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
+                          (h == decodedBitmap.height()) && bm->isNull();
+    const bool needColorTable = SkBitmap::kIndex8_Config == config;
+    if (swapOnly) {
+        if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : NULL)) {
+            return false;
+        }
+    } else {
+        if (!decodedBitmap.allocPixels(NULL, needColorTable ? 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) {
+    if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) {
         png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
     }
 
@@ -402,20 +701,28 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
     * 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;
+    const int number_passes = (interlaceType != 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);
 
+    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, NULL, 1);
+                uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
             }
         }
     } else {
@@ -436,10 +743,10 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
             upscale png's palette to a direct model
          */
         SkAutoLockColors ctLock(colorTable);
-        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
+        if (!sampler.begin(&decodedBitmap, sc, doDither, ctLock.colors())) {
             return false;
         }
-        const int height = decodedBitmap->height();
+        const int height = decodedBitmap.height();
 
         if (number_passes > 1) {
             SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
@@ -447,10 +754,15 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
             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 = (uint8_t*)decodedBitmap.getPixels();
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                }
                 uint8_t* row = base;
-                for (png_uint_32 y = 0; y < origHeight; y++) {
+                for (int32_t y = 0; y < rect.height(); y++) {
                     uint8_t* bmRow = row;
-                    png_read_rows(png_ptr, &bmRow, NULL, 1);
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
                     row += rb;
                 }
             }
@@ -463,34 +775,40 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
         } 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 = (uint8_t*)decodedBitmap.getPixels();
+                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, NULL, 1);
+                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);
                 }
             }
-
-            // skip the rest of the rows (if any)
-            png_uint_32 read = (height - 1) * sampler.srcDY() +
-                               sampler.srcY0() + 1;
-            SkASSERT(read <= origHeight);
-            skip_src_rows(png_ptr, srcRow, origHeight - read);
         }
     }
 
-    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
-    png_read_end(png_ptr, info_ptr);
-
     if (0 != theTranspColor) {
-        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+        reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
     }
-    decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    decodedBitmap.setIsOpaque(!reallyHasAlpha);
+
+    if (swapOnly) {
+        bm->swap(decodedBitmap);
+    } else {
+        cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
+                   region.width(), region.height(), 0, rect.y());
+    }
+
     return true;
 }
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -498,7 +816,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
 #include "SkUnPreMultiply.h"
 
 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
-    SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
+    SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
     if (!sk_stream->write(data, len)) {
         png_error(png_ptr, "sk_write_fn Error!");
     }
@@ -609,12 +927,14 @@ static inline int pack_palette(SkColorTable* ctable,
 
 class SkPNGImageEncoder : public SkImageEncoder {
 protected:
-    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
 private:
     bool doEncode(SkWStream* stream, const SkBitmap& bm,
                   const bool& hasAlpha, int colorType,
                   int bitDepth, SkBitmap::Config config,
                   png_color_8& sig_bit);
+
+    typedef SkImageEncoder INHERITED;
 };
 
 bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
@@ -697,7 +1017,7 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
 
     info_ptr = png_create_info_struct(png_ptr);
     if (NULL == info_ptr) {
-        png_destroy_write_struct(&png_ptr,  NULL);
+        png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
         return false;
     }
 
@@ -709,7 +1029,7 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
         return false;
     }
 
-    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, NULL);
+    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
 
     /* Set the image information here.  Width and height are up to 2^31,
     * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
new file mode 100644 (file)
index 0000000..d4e40c0
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkTScopedPtr.h"
+
+// A WebP decoder only, on top of (subset of) libwebp
+// For more information on WebP image format, and libwebp library, see:
+//   http://code.google.com/speed/webp/
+//   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
+//   http://review.webmproject.org/gitweb?p=libwebp.git
+
+#include <stdio.h>
+extern "C" {
+// If moving libwebp out of skia source tree, path for webp headers must be
+// updated accordingly. Here, we enforce using local copy in webp sub-directory.
+#include "webp/decode.h"
+#include "webp/encode.h"
+}
+
+#ifdef ANDROID
+#include <cutils/properties.h>
+
+// Key to lookup the size of memory buffer set in system property
+static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap";
+#endif
+
+// this enables timing code to report milliseconds for a decode
+//#define TIME_DECODE
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+// Define VP8 I/O on top of Skia stream
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+static const size_t WEBP_VP8_HEADER_SIZE = 64;
+static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
+
+// Parse headers of RIFF container, and check for valid Webp (VP8) content.
+static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) {
+    unsigned char buffer[WEBP_VP8_HEADER_SIZE];
+    const uint32_t contentSize = stream->getLength();
+    const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE);
+    const uint32_t read_bytes =
+            (contentSize < WEBP_VP8_HEADER_SIZE) ? contentSize : WEBP_VP8_HEADER_SIZE;
+    if (len != read_bytes) {
+        return false; // can't read enough
+    }
+
+    WebPBitstreamFeatures features;
+    VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features);
+    if (VP8_STATUS_OK != status) {
+        return false; // Invalid WebP file.
+    }
+    *width = features.width;
+    *height = features.height;
+    *alpha = features.has_alpha;
+
+    // sanity check for image size that's about to be decoded.
+    {
+        Sk64 size;
+        size.setMul(*width, *height);
+        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;
+        }
+    }
+    return true;
+}
+
+class SkWEBPImageDecoder: public SkImageDecoder {
+public:
+    SkWEBPImageDecoder() {
+        fInputStream = NULL;
+        fOrigWidth = 0;
+        fOrigHeight = 0;
+        fHasAlpha = 0;
+    }
+    virtual ~SkWEBPImageDecoder() {
+        SkSafeUnref(fInputStream);
+    }
+
+    virtual Format getFormat() const SK_OVERRIDE {
+        return kWEBP_Format;
+    }
+
+protected:
+    virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
+    virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+
+private:
+    bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
+    SkStream* fInputStream;
+    int fOrigWidth;
+    int fOrigHeight;
+    int fHasAlpha;
+
+    typedef SkImageDecoder INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef TIME_DECODE
+
+#include "SkTime.h"
+
+class AutoTimeMillis {
+public:
+    AutoTimeMillis(const char label[]) :
+        fLabel(label) {
+        if (NULL == fLabel) {
+            fLabel = "";
+        }
+        fNow = SkTime::GetMSecs();
+    }
+    ~AutoTimeMillis() {
+        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
+    }
+private:
+    const char* fLabel;
+    SkMSec fNow;
+};
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const SkBitmap& bm, const char msg[]) {
+    SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
+    return false; // must always return false
+}
+
+static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, int hasAlpha) {
+    WEBP_CSP_MODE mode = MODE_LAST;
+    SkBitmap::Config config = decodedBitmap->config();
+    // For images that have alpha, choose appropriate color mode (MODE_rgbA,
+    // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency
+    // factor (alpha).
+    if (config == SkBitmap::kARGB_8888_Config) {
+      mode = hasAlpha ? MODE_rgbA : MODE_RGBA;
+    } else if (config == SkBitmap::kARGB_4444_Config) {
+      mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444;
+    } else if (config == SkBitmap::kRGB_565_Config) {
+      mode = MODE_RGB_565;
+    }
+    SkASSERT(MODE_LAST != mode);
+    return mode;
+}
+
+// Incremental WebP image decoding. Reads input buffer of 64K size iteratively
+// and decodes this block to appropriate color-space as per config object.
+static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) {
+    WebPIDecoder* idec = WebPIDecode(NULL, 0, config);
+    if (NULL == idec) {
+        WebPFreeDecBuffer(&config->output);
+        return false;
+    }
+
+    stream->rewind();
+    const uint32_t contentSize = stream->getLength();
+    const uint32_t readBufferSize = (contentSize < WEBP_IDECODE_BUFFER_SZ) ?
+                                       contentSize : WEBP_IDECODE_BUFFER_SZ;
+    SkAutoMalloc srcStorage(readBufferSize);
+    unsigned char* input = (uint8_t*)srcStorage.get();
+    if (NULL == input) {
+        WebPIDelete(idec);
+        WebPFreeDecBuffer(&config->output);
+        return false;
+    }
+
+    uint32_t bytesRemaining = contentSize;
+    while (bytesRemaining > 0) {
+        const uint32_t bytesToRead = (bytesRemaining < WEBP_IDECODE_BUFFER_SZ) ?
+                                      bytesRemaining : WEBP_IDECODE_BUFFER_SZ;
+        const size_t bytesRead = stream->read(input, bytesToRead);
+        if (0 == bytesRead) {
+            break;
+        }
+
+        VP8StatusCode status = WebPIAppend(idec, input, bytesRead);
+        if (VP8_STATUS_OK == status || VP8_STATUS_SUSPENDED == status) {
+            bytesRemaining -= bytesRead;
+        } else {
+            break;
+        }
+    }
+    srcStorage.free();
+    WebPIDelete(idec);
+    WebPFreeDecBuffer(&config->output);
+
+    if (bytesRemaining > 0) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+static bool webp_get_config_resize(WebPDecoderConfig* config,
+                                   SkBitmap* decodedBitmap,
+                                   int width, int height, int hasAlpha) {
+    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha);
+    if (MODE_LAST == mode) {
+        return false;
+    }
+
+    if (0 == WebPInitDecoderConfig(config)) {
+        return false;
+    }
+
+    config->output.colorspace = mode;
+    config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
+    config->output.u.RGBA.stride = decodedBitmap->rowBytes();
+    config->output.u.RGBA.size = decodedBitmap->getSize();
+    config->output.is_external_memory = 1;
+
+    if (width != decodedBitmap->width() || height != decodedBitmap->height()) {
+        config->options.use_scaling = 1;
+        config->options.scaled_width = decodedBitmap->width();
+        config->options.scaled_height = decodedBitmap->height();
+    }
+
+    return true;
+}
+
+static bool webp_get_config_resize_crop(WebPDecoderConfig* config,
+                                        SkBitmap* decodedBitmap,
+                                        const SkIRect& region, int hasAlpha) {
+
+    if (!webp_get_config_resize(config, decodedBitmap, region.width(),
+                                region.height(), hasAlpha)) {
+      return false;
+    }
+
+    config->options.use_cropping = 1;
+    config->options.crop_left = region.fLeft;
+    config->options.crop_top = region.fTop;
+    config->options.crop_width = region.width();
+    config->options.crop_height = region.height();
+
+    return true;
+}
+
+bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
+                                         int width, int height) {
+    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, fHasAlpha);
+
+    // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
+    if (fHasAlpha) {
+        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;
+        }
+    }
+
+    if (!this->chooseFromOneChoice(config, width, height)) {
+        return false;
+    }
+
+    decodedBitmap->setConfig(config, width, height, 0);
+
+    decodedBitmap->setIsOpaque(!fHasAlpha);
+
+    return true;
+}
+
+bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
+                                          int *width, int *height) {
+    int origWidth, origHeight, hasAlpha;
+    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
+        return false;
+    }
+
+    stream->rewind();
+    *width = origWidth;
+    *height = origHeight;
+
+    SkRefCnt_SafeAssign(this->fInputStream, stream);
+    this->fOrigWidth = origWidth;
+    this->fOrigHeight = origHeight;
+    this->fHasAlpha = hasAlpha;
+
+    return true;
+}
+
+static bool is_config_compatible(const SkBitmap& bitmap) {
+    SkBitmap::Config config = bitmap.config();
+    return config == SkBitmap::kARGB_4444_Config ||
+           config == SkBitmap::kRGB_565_Config ||
+           config == SkBitmap::kARGB_8888_Config;
+}
+
+bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
+                                        const SkIRect& region) {
+    SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight);
+
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, return false
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
+    const int width = sampler.scaledWidth();
+    const int height = sampler.scaledHeight();
+
+    // The image can be decoded directly to decodedBitmap if
+    //   1. the region is within the image range
+    //   2. bitmap's config is compatible
+    //   3. bitmap's size is same as the required region (after sampled)
+    bool directDecode = (rect == region) &&
+                        (decodedBitmap->isNull() ||
+                         (is_config_compatible(*decodedBitmap) &&
+                         (decodedBitmap->width() == width) &&
+                         (decodedBitmap->height() == height)));
+    SkTScopedPtr<SkBitmap> adb;
+    SkBitmap *bitmap = decodedBitmap;
+
+    if (!directDecode) {
+        // allocates a temp bitmap
+        bitmap = new SkBitmap;
+        adb.reset(bitmap);
+    }
+
+    if (bitmap->isNull()) {
+        if (!setDecodeConfig(bitmap, width, height)) {
+            return false;
+        }
+        // alloc from native heap if it is a temp bitmap. (prevent GC)
+        bool allocResult = (bitmap == decodedBitmap)
+                               ? allocPixelRef(bitmap, NULL)
+                               : bitmap->allocPixels();
+        if (!allocResult) {
+            return return_false(*decodedBitmap, "allocPixelRef");
+        }
+    } else {
+        // This is also called in setDecodeConfig in above block.
+        // i.e., when bitmap->isNull() is true.
+        if (!chooseFromOneChoice(bitmap->config(), width, height)) {
+            return false;
+        }
+    }
+
+    SkAutoLockPixels alp(*bitmap);
+    WebPDecoderConfig config;
+    if (!webp_get_config_resize_crop(&config, bitmap, rect, fHasAlpha)) {
+        return false;
+    }
+
+    // Decode the WebP image data stream using WebP incremental decoding for
+    // the specified cropped image-region.
+    if (!webp_idecode(this->fInputStream, &config)) {
+        return false;
+    }
+
+    if (!directDecode) {
+        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
+                   region.width(), region.height(), rect.x(), rect.y());
+    }
+    return true;
+}
+
+bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                  Mode mode) {
+#ifdef TIME_DECODE
+    AutoTimeMillis atm("WEBP Decode");
+#endif
+
+    int origWidth, origHeight, hasAlpha;
+    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
+        return false;
+    }
+    this->fHasAlpha = hasAlpha;
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+    // If only bounds are requested, done
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+                             sampler.scaledHeight())) {
+            return false;
+        }
+        return true;
+    }
+
+    // No Bitmap reuse supported for this format
+    if (!decodedBitmap->isNull()) {
+        return false;
+    }
+    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+                         sampler.scaledHeight())) {
+        return false;
+    }
+
+    if (!this->allocPixelRef(decodedBitmap, NULL)) {
+        return return_false(*decodedBitmap, "allocPixelRef");
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    WebPDecoderConfig config;
+    if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
+                                hasAlpha)) {
+        return false;
+    }
+
+    // Decode the WebP image data stream using WebP incremental decoding.
+    return webp_idecode(stream, &config);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
+                                 const SkPMColor* SK_RESTRICT ctable);
+
+static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                             const SkPMColor*) {
+  const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint32_t c = *src++;
+      rgb[0] = SkGetPackedR32(c);
+      rgb[1] = SkGetPackedG32(c);
+      rgb[2] = SkGetPackedB32(c);
+      rgb += 3;
+  }
+}
+
+static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                           const SkPMColor*) {
+  const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint16_t c = *src++;
+      rgb[0] = SkPacked16ToR32(c);
+      rgb[1] = SkPacked16ToG32(c);
+      rgb[2] = SkPacked16ToB32(c);
+      rgb += 3;
+  }
+}
+
+static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                             const SkPMColor*) {
+  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
+  for (int i = 0; i < width; ++i) {
+      const SkPMColor16 c = *src++;
+      rgb[0] = SkPacked4444ToR32(c);
+      rgb[1] = SkPacked4444ToG32(c);
+      rgb[2] = SkPacked4444ToB32(c);
+      rgb += 3;
+  }
+}
+
+static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                          const SkPMColor* SK_RESTRICT ctable) {
+  const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint32_t c = ctable[*src++];
+      rgb[0] = SkGetPackedR32(c);
+      rgb[1] = SkGetPackedG32(c);
+      rgb[2] = SkGetPackedB32(c);
+      rgb += 3;
+  }
+}
+
+static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return ARGB_8888_To_RGB;
+        case SkBitmap::kRGB_565_Config:
+            return RGB_565_To_RGB;
+        case SkBitmap::kARGB_4444_Config:
+            return ARGB_4444_To_RGB;
+        case SkBitmap::kIndex8_Config:
+            return Index8_To_RGB;
+        default:
+            return NULL;
+    }
+}
+
+static int stream_writer(const uint8_t* data, size_t data_size,
+                         const WebPPicture* const picture) {
+  SkWStream* const stream = (SkWStream*)picture->custom_ptr;
+  return stream->write(data, data_size) ? 1 : 0;
+}
+
+class SkWEBPImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+
+private:
+    typedef SkImageEncoder INHERITED;
+};
+
+bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
+                                  int quality) {
+    const SkBitmap::Config config = bm.getConfig();
+    const ScanlineImporter scanline_import = ChooseImporter(config);
+    if (NULL == scanline_import) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(bm);
+    SkAutoLockColors ctLocker;
+    if (NULL == bm.getPixels()) {
+        return false;
+    }
+
+    WebPConfig webp_config;
+    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) {
+        return false;
+    }
+
+    WebPPicture pic;
+    WebPPictureInit(&pic);
+    pic.width = bm.width();
+    pic.height = bm.height();
+    pic.writer = stream_writer;
+    pic.custom_ptr = (void*)stream;
+
+    const SkPMColor* colors = ctLocker.lockColors(bm);
+    const uint8_t* src = (uint8_t*)bm.getPixels();
+    const int rgbStride = pic.width * 3;
+
+    // Import (for each scanline) the bit-map image (in appropriate color-space)
+    // to RGB color space.
+    uint8_t* rgb = new uint8_t[rgbStride * pic.height];
+    for (int y = 0; y < pic.height; ++y) {
+        scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride,
+                        pic.width, colors);
+    }
+
+    bool ok = WebPPictureImportRGB(&pic, rgb, rgbStride);
+    delete[] rgb;
+
+    ok = ok && WebPEncode(&webp_config, &pic);
+    WebPPictureFree(&pic);
+
+    return ok;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(WEBPImageDecoder);
+DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
+    int width, height, hasAlpha;
+    if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
+        return NULL;
+    }
+
+    // Magic matches, call decoder
+    return SkNEW(SkWEBPImageDecoder);
+}
+
+static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
+      return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory);
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory);
index c475ec126e9ec14ef677a938977c4b735fcb7ca4..5e395e592fbeba50077b0ff773cd36841370925e 100644 (file)
 
 class SkWBMPImageDecoder : public SkImageDecoder {
 public:
-    virtual Format getFormat() const {
+    virtual Format getFormat() const SK_OVERRIDE {
         return kWBMP_Format;
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+
+private:
+    typedef SkImageDecoder INHERITED;
 };
 
 static bool read_byte(SkStream* stream, uint8_t* data)
@@ -107,14 +110,21 @@ bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
 
     int width = head.fWidth;
     int height = head.fHeight;
+    
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+        decodedBitmap->setIsOpaque(true);
+        return true;
+    }
+
+    // No Bitmap reuse supported for this format
+    if (!decodedBitmap->isNull()) {
+        return false;
+    }
 
-    // assign these directly, in case we return kDimensions_Result
     decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
     decodedBitmap->setIsOpaque(true);
 
-    if (SkImageDecoder::kDecodeBounds_Mode == mode)
-        return true;
-
     const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
     SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
     SkAutoUnref   aur(ct);