Add Options to SkDecodingImageGenerator, simplify API.
authorhalcanary@google.com <halcanary@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 2 Jan 2014 13:15:13 +0000 (13:15 +0000)
committerhalcanary@google.com <halcanary@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 2 Jan 2014 13:15:13 +0000 (13:15 +0000)
Motivation: We want to remove redundant classes from Skia.  To
that end we want to remove SkImageRef and its subclasses and
replace their uses with SkDiscardablePixelRef +
SkDecodingImageGenerator.  Since Android uses SkImageRef, we need
to make sure that SkDecodingImageGenerator allows all of the
settings that Android exposes in BitmapFactory.Options.

To that end, we have created an Options struct for the
SkDecodingImageGenerator which lets the client of the generator set
sample size, dithering, and bitmap config.

We have made the SkDecodingImageGenerator constructor private
and replaced the SkDecodingImageGenerator::Install functions
with a SkDecodingImageGenerator::Create functions (one for
SkData and one for SkStream) which now take a
SkDecodingImageGenerator::Options struct.

Also added a ImageDecoderOptions test which loops through a list
of sets of options and tries them on a set of 5 small encoded
images.

Also updated several users of SkDecodingImageGenerator::Install to
follow new call signature - gm/factory.cpp, LazyDecodeBitmap.cpp,
and PictureTest.cpp, CachedDecodingPixelRefTest.cpp.

We also added a new ImprovedBitmapFactory Test which simulates the
exact function that Android will need to modify to use this,
installPixelRef() in BitmapFactory.

R=reed@google.com, scroggo@google.com

Committed: https://code.google.com/p/skia/source/detail?r=12744

Review URL: https://codereview.chromium.org/93703004

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

14 files changed:
gm/factory.cpp
include/core/SkImageGenerator.h
samplecode/SamplePicture.cpp
src/image/SkImagePriv.cpp
src/image/SkImagePriv.h
src/images/SkDecodingImageGenerator.cpp
src/images/SkDecodingImageGenerator.h
src/lazy/SkCachingPixelRef.cpp
src/lazy/SkDiscardablePixelRef.cpp
src/lazy/SkDiscardablePixelRef.h
tests/CachedDecodingPixelRefTest.cpp
tests/ImageDecodingTest.cpp
tests/PictureTest.cpp
tools/LazyDecodeBitmap.cpp

index 19eb63b..efd817d 100644 (file)
@@ -12,6 +12,7 @@
 #include "SkDiscardableMemoryPool.h"
 #include "SkDiscardablePixelRef.h"
 #include "SkImageDecoder.h"
+#include "SkImageGenerator.h"
 #include "SkOSFile.h"
 #include "SkStream.h"
 
@@ -35,8 +36,10 @@ protected:
             // bitmap is unlocked.
             SkAutoTUnref<SkDiscardableMemoryPool> pool(
                 SkNEW_ARGS(SkDiscardableMemoryPool, (1)));
-            SkAssertResult(SkDecodingImageGenerator::Install(data,
-                                                             &fBitmap, pool));
+            SkAssertResult(SkInstallDiscardablePixelRef(
+                SkDecodingImageGenerator::Create(
+                    data, SkDecodingImageGenerator::Options()),
+                &fBitmap, pool));
         }
     }
 
@@ -68,4 +71,4 @@ private:
 static GM* MyFactory(void*) { return new FactoryGM; }
 static GMRegistry reg(MyFactory);
 
-}
+}  // namespace skiagm
index d56f8f8..220973a 100644 (file)
@@ -21,6 +21,8 @@ class SkImageGenerator;
  *  the generator.  If it succeeds, it will modify destination
  *  bitmap.
  *
+ *  If generator is NULL, will safely return false.
+ *
  *  If this fails or when the SkDiscardablePixelRef that is
  *  installed into destination is destroyed, it will call
  *  SkDELETE() on the generator.  Therefore, generator should be
index af06e82..767fd61 100644 (file)
@@ -40,7 +40,8 @@ static SkBitmap load_bitmap() {
     SkString path = SkOSPath::SkPathJoin(directory.c_str(), "mandrill_512.png");
     SkAutoDataUnref data(SkData::NewFromFileName(path.c_str()));
     if (data.get() != NULL) {
-        SkDecodingImageGenerator::Install(data.get(), &bm);
+        SkInstallDiscardablePixelRef(SkDecodingImageGenerator::Create(
+            data, SkDecodingImageGenerator::Options()), &bm, NULL);
     }
     return bm;
 }
index 976a5b3..43cc44b 100644 (file)
@@ -9,8 +9,8 @@
 #include "SkCanvas.h"
 #include "SkPicture.h"
 
-SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) {
-    switch (info.fColorType) {
+SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType colorType) {
+    switch (colorType) {
         case kAlpha_8_SkColorType:
             return SkBitmap::kA8_Config;
 
@@ -33,6 +33,39 @@ SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) {
     return SkBitmap::kNo_Config;
 }
 
+SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) {
+    return SkColorTypeToBitmapConfig(info.fColorType);
+}
+
+bool SkBitmapConfigToColorType(SkBitmap::Config config, SkColorType* ctOut) {
+    SkColorType ct;
+    switch (config) {
+        case SkBitmap::kA8_Config:
+            ct = kAlpha_8_SkColorType;
+            break;
+        case SkBitmap::kIndex8_Config:
+            ct = kIndex_8_SkColorType;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            ct = kRGB_565_SkColorType;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            ct = kARGB_4444_SkColorType;
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            ct = kPMColor_SkColorType;
+            break;
+        case SkBitmap::kNo_Config:
+        default:
+            return false;
+    }
+    if (ctOut) {
+        *ctOut = ct;
+    }
+    return true;
+}
+
+
 SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef) {
     SkImageInfo info;
     if (!bm.asImageInfo(&info)) {
index bf28f59..7c19c73 100644 (file)
@@ -14,6 +14,8 @@
 class SkPicture;
 
 extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo&);
+extern SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType);
+extern bool SkBitmapConfigToColorType(SkBitmap::Config, SkColorType* ctOut);
 
 // Call this if you explicitly want to use/share this pixelRef in the image
 extern SkImage* SkNewImageFromPixelRef(const SkImageInfo&, SkPixelRef*,
index a833c63..153d1e2 100644 (file)
@@ -5,13 +5,14 @@
  * found in the LICENSE file.
  */
 
-#include "SkDecodingImageGenerator.h"
 #include "SkData.h"
+#include "SkDecodingImageGenerator.h"
 #include "SkImageDecoder.h"
+#include "SkImageInfo.h"
 #include "SkImageGenerator.h"
 #include "SkImagePriv.h"
 #include "SkStream.h"
-
+#include "SkUtils.h"
 
 namespace {
 /**
@@ -20,50 +21,89 @@ namespace {
  */
 class TargetAllocator : public SkBitmap::Allocator {
 public:
-    TargetAllocator(void* target, size_t rowBytes, const SkImageInfo& info)
+    TargetAllocator(void* target,
+                    size_t rowBytes,
+                    int width,
+                    int height,
+                    SkBitmap::Config config)
         : fTarget(target)
         , fRowBytes(rowBytes)
-        , fInfo(info) { }
+        , fWidth(width)
+        , fHeight(height)
+        , fConfig(config) { }
 
-    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
-        if ((SkImageInfoToBitmapConfig(fInfo) != bm->config())
-            || (bm->width() != fInfo.fWidth)
-            || (bm->height() != fInfo.fHeight)) {
-            return false;
+    bool isReady() { return (fTarget != NULL); }
+
+    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
+        if ((NULL == fTarget)
+            || (fConfig != bm->config())
+            || (fWidth != bm->width())
+            || (fHeight != bm->height())
+            || (ct != NULL)) {
+            // Call default allocator.
+            return bm->allocPixels(NULL, ct);
         }
-        bm->setConfig(bm->config(), bm->width(), bm->height(),
-                      fRowBytes, bm->alphaType());
-        bm->setPixels(fTarget, ct);
+        // make sure fRowBytes is correct.
+        bm->setConfig(fConfig, fWidth, fHeight, fRowBytes, bm->alphaType());
+        // TODO(halcanary): verify that all callers of this function
+        // will respect new RowBytes.  Will be moot once rowbytes belongs
+        // to PixelRef.
+        bm->setPixels(fTarget, NULL);
+        fTarget = NULL;  // never alloc same pixels twice!
         return true;
     }
 
 private:
-    void* fTarget;
-    size_t fRowBytes;
-    SkImageInfo fInfo;
+    void* fTarget;  // Block of memory to be supplied as pixel memory
+                    // in allocPixelRef.  Must be large enough to hold
+                    // a bitmap described by fWidth, fHeight, and
+                    // fRowBytes.
+    size_t fRowBytes;  // rowbytes for the destination bitmap
+    int fWidth;   // Along with fHeight and fConfig, the information
+    int fHeight;  // about the bitmap whose pixels this allocator is
+                  // expected to allocate. If they do not match the
+                  // bitmap passed to allocPixelRef, it is assumed
+                  // that the bitmap will be copied to a bitmap with
+                  // the correct info using this allocator, so the
+                  // default allocator will be used instead of
+                  // fTarget.
+    SkBitmap::Config fConfig;
     typedef SkBitmap::Allocator INHERITED;
 };
+
+// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
+#ifdef SK_DEBUG
+    #define SkCheckResult(expr, value)  SkASSERT((value) == (expr))
+#else
+    #define SkCheckResult(expr, value)  (void)(expr)
+#endif
+
+#ifdef SK_DEBUG
+inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) {
+    return ((reported == actual)
+            || ((reported == kPremul_SkAlphaType)
+                && (actual == kOpaque_SkAlphaType)));
+}
+#endif  // SK_DEBUG
+
 }  // namespace
 ////////////////////////////////////////////////////////////////////////////////
 
-SkDecodingImageGenerator::SkDecodingImageGenerator(SkData* data)
+SkDecodingImageGenerator::SkDecodingImageGenerator(
+        SkData* data,
+        SkStreamRewindable* stream,
+        const SkImageInfo& info,
+        int sampleSize,
+        bool ditherImage,
+        SkBitmap::Config requestedConfig)
     : fData(data)
-    , fHasInfo(false)
-    , fDoCopyTo(false) {
-    SkASSERT(fData != NULL);
-    fStream = SkNEW_ARGS(SkMemoryStream, (fData));
-    SkASSERT(fStream != NULL);
-    SkASSERT(fStream->unique());
-    fData->ref();
-}
-
-SkDecodingImageGenerator::SkDecodingImageGenerator(SkStreamRewindable* stream)
-    : fData(NULL)
     , fStream(stream)
-    , fHasInfo(false)
-    , fDoCopyTo(false) {
-    SkASSERT(fStream != NULL);
-    SkASSERT(fStream->unique());
+    , fInfo(info)
+    , fSampleSize(sampleSize)
+    , fDitherImage(ditherImage)
+    , fRequestedConfig(requestedConfig) {
+    SkASSERT(stream != NULL);
+    SkSafeRef(fData);  // may be NULL.
 }
 
 SkDecodingImageGenerator::~SkDecodingImageGenerator() {
@@ -71,15 +111,16 @@ SkDecodingImageGenerator::~SkDecodingImageGenerator() {
     fStream->unref();
 }
 
-// TODO(halcanary): Give this macro a better name and move it into SkTypes.h
-#ifdef SK_DEBUG
-    #define SkCheckResult(expr, value)  SkASSERT((value) == (expr))
-#else
-    #define SkCheckResult(expr, value)  (void)(expr)
-#endif
+bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
+    if (info != NULL) {
+        *info = fInfo;
+    }
+    return true;
+}
 
 SkData* SkDecodingImageGenerator::refEncodedData() {
     // This functionality is used in `gm --serialize`
+    // Does not encode options.
     if (fData != NULL) {
         return SkSafeRef(fData);
     }
@@ -98,111 +139,149 @@ SkData* SkDecodingImageGenerator::refEncodedData() {
     return SkSafeRef(fData);
 }
 
-bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
-    // info can be NULL.  If so, will update fInfo, fDoCopyTo, and fHasInfo.
-    if (fHasInfo) {
-        if (info != NULL) {
-            *info = fInfo;
-        }
-        return true;
-    }
-    SkAssertResult(fStream->rewind());
-    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
-    if (NULL == decoder.get()) {
-        return false;
-    }
-    SkBitmap bitmap;
-    if (!decoder->decode(fStream, &bitmap,
-                         SkImageDecoder::kDecodeBounds_Mode)) {
-        return false;
-    }
-    if (bitmap.config() == SkBitmap::kNo_Config) {
-        return false;
-    }
-    if (!bitmap.asImageInfo(&fInfo)) {
-        // We can't use bitmap.config() as is.
-        if (!bitmap.canCopyTo(SkBitmap::kARGB_8888_Config)) {
-            SkDEBUGFAIL("!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)");
-            return false;
-        }
-        fDoCopyTo = true;
-        fInfo.fWidth = bitmap.width();
-        fInfo.fHeight = bitmap.height();
-        fInfo.fColorType = kPMColor_SkColorType;
-        fInfo.fAlphaType = bitmap.alphaType();
-    }
-    if (info != NULL) {
-        *info = fInfo;
-    }
-    fHasInfo = true;
-    return true;
-}
-
 bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
                                          void* pixels,
                                          size_t rowBytes) {
     if (NULL == pixels) {
         return false;
     }
-    if (!this->getInfo(NULL)) {
-        return false;
-    }
-    if (SkImageInfoToBitmapConfig(info) == SkBitmap::kNo_Config) {
-        return false;  // Unsupported SkColorType.
-    }
-    SkAssertResult(fStream->rewind());
-    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
-    if (NULL == decoder.get()) {
-        return false;
-    }
     if (fInfo != info) {
-        // The caller has specified a different info.  For now, this
-        // is an error.  In the future, we will check to see if we can
-        // convert.
+        // The caller has specified a different info.  This is an
+        // error for this kind of SkImageGenerator.  Use the Options
+        // to change the settings.
         return false;
     }
-    int bpp = SkBitmap::ComputeBytesPerPixel(SkImageInfoToBitmapConfig(info));
+    int bpp = SkBitmap::ComputeBytesPerPixel(fRequestedConfig);
     if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) {
+        // The caller has specified a bad rowBytes.
         return false;
     }
-    SkBitmap bitmap;
-    if (!bitmap.setConfig(info, rowBytes)) {
+
+    SkAssertResult(fStream->rewind());
+    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
+    if (NULL == decoder.get()) {
         return false;
     }
+    decoder->setDitherImage(fDitherImage);
+    decoder->setSampleSize(fSampleSize);
 
-    TargetAllocator allocator(pixels, rowBytes, info);
-    if (!fDoCopyTo) {
-        decoder->setAllocator(&allocator);
-    }
-    bool success = decoder->decode(fStream, &bitmap,
+    SkBitmap bitmap;
+    TargetAllocator allocator(pixels, rowBytes, info.fWidth,
+                              info.fHeight, fRequestedConfig);
+    decoder->setAllocator(&allocator);
+    bool success = decoder->decode(fStream, &bitmap, fRequestedConfig,
                                    SkImageDecoder::kDecodePixels_Mode);
     decoder->setAllocator(NULL);
     if (!success) {
         return false;
     }
-    if (fDoCopyTo) {
-        SkBitmap bm8888;
-        bitmap.copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator);
+    if (allocator.isReady()) {  // Did not use pixels!
+        SkBitmap bm;
+        SkASSERT(bitmap.canCopyTo(fRequestedConfig));
+        if (!bitmap.copyTo(&bm, fRequestedConfig, &allocator)
+            || allocator.isReady()) {
+            SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed.");
+            // Earlier we checked canCopyto(); we expect consistency.
+            return false;
+        }
+        SkASSERT(check_alpha(fInfo.fAlphaType, bm.alphaType()));
+    } else {
+        SkASSERT(check_alpha(fInfo.fAlphaType, bitmap.alphaType()));
     }
     return true;
 }
-bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst,
-                                       SkDiscardableMemory::Factory* factory) {
+
+SkImageGenerator* SkDecodingImageGenerator::Create(
+        SkData* data,
+        const SkDecodingImageGenerator::Options& opts) {
     SkASSERT(data != NULL);
-    SkASSERT(dst != NULL);
-    SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data)));
-    return SkInstallDiscardablePixelRef(gen, dst, factory);
+    if (NULL == data) {
+        return NULL;
+    }
+    SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data));
+    SkASSERT(stream != NULL);
+    SkASSERT(stream->unique());
+    return SkDecodingImageGenerator::Create(data, stream, opts);
 }
 
-bool SkDecodingImageGenerator::Install(SkStreamRewindable* stream,
-                                       SkBitmap* dst,
-                                       SkDiscardableMemory::Factory* factory) {
+SkImageGenerator* SkDecodingImageGenerator::Create(
+        SkStreamRewindable* stream,
+        const SkDecodingImageGenerator::Options& opts) {
     SkASSERT(stream != NULL);
-    SkASSERT(dst != NULL);
+    SkASSERT(stream->unique());
     if ((stream == NULL) || !stream->unique()) {
         SkSafeUnref(stream);
-        return false;
+        return NULL;
+    }
+    return SkDecodingImageGenerator::Create(NULL, stream, opts);
+}
+
+// A contructor-type function that returns NULL on failure.  This
+// prevents the returned SkImageGenerator from ever being in a bad
+// state.  Called by both Create() functions
+SkImageGenerator* SkDecodingImageGenerator::Create(
+        SkData* data,
+        SkStreamRewindable* stream,
+        const SkDecodingImageGenerator::Options& opts) {
+    SkASSERT(stream);
+    SkAutoTUnref<SkStreamRewindable> autoStream(stream);  // always unref this.
+    if (opts.fUseRequestedColorType &&
+        (kIndex_8_SkColorType == opts.fRequestedColorType)) {
+        // We do not support indexed color with SkImageGenerators,
+        return NULL;
+    }
+    SkAssertResult(autoStream->rewind());
+    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream));
+    if (NULL == decoder.get()) {
+        return NULL;
+    }
+    SkBitmap bitmap;
+    decoder->setSampleSize(opts.fSampleSize);
+    if (!decoder->decode(stream, &bitmap,
+                         SkImageDecoder::kDecodeBounds_Mode)) {
+        return NULL;
+    }
+    if (bitmap.config() == SkBitmap::kNo_Config) {
+        return NULL;
+    }
+
+    SkImageInfo info;
+    SkBitmap::Config config;
+
+    if (!opts.fUseRequestedColorType) {
+        // Use default config.
+        if (SkBitmap::kIndex8_Config == bitmap.config()) {
+            // We don't support kIndex8 because we don't support
+            // colortables in this workflow.
+            config = SkBitmap::kARGB_8888_Config;
+            info.fWidth = bitmap.width();
+            info.fHeight = bitmap.height();
+            info.fColorType = kPMColor_SkColorType;
+            info.fAlphaType = bitmap.alphaType();
+        } else {
+            config = bitmap.config();  // Save for later!
+            if (!bitmap.asImageInfo(&info)) {
+                SkDEBUGFAIL("Getting SkImageInfo from bitmap failed.");
+                return NULL;
+            }
+        }
+    } else {
+        config = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
+        if (!bitmap.canCopyTo(config)) {
+            SkASSERT(bitmap.config() != config);
+            return NULL;  // Can not translate to needed config.
+        }
+        info.fWidth = bitmap.width();
+        info.fHeight = bitmap.height();
+        info.fColorType = opts.fRequestedColorType;
+        info.fAlphaType = bitmap.alphaType();
+
+        // Sanity check.
+        SkDEBUGCODE(SkColorType tmp;)
+        SkASSERT(SkBitmapConfigToColorType(config, &tmp));
+        SkASSERT(tmp == opts.fRequestedColorType);
     }
-    SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (stream)));
-    return SkInstallDiscardablePixelRef(gen, dst, factory);
+    return SkNEW_ARGS(SkDecodingImageGenerator,
+                      (data, autoStream.detach(), info,
+                       opts.fSampleSize, opts.fDitherImage, config));
 }
index dba234b..12a49d5 100644 (file)
 #ifndef SkDecodingImageGenerator_DEFINED
 #define SkDecodingImageGenerator_DEFINED
 
-#include "SkDiscardableMemory.h"
+#include "SkBitmap.h"
 #include "SkImageGenerator.h"
-#include "SkImageInfo.h"
 
-class SkBitmap;
+class SkData;
 class SkStreamRewindable;
 
 /**
- * Calls into SkImageDecoder::DecodeMemoryToTarget to implement a
- * SkImageGenerator
+ *  An implementation of SkImageGenerator that calls into
+ *  SkImageDecoder.
  */
 class SkDecodingImageGenerator : public SkImageGenerator {
 public:
-    /*
-     *  The constructor will take a reference to the SkData.  The
-     *  destructor will unref() it.
-     */
-    explicit SkDecodingImageGenerator(SkData* data);
-
-    /*
-     *  The SkData version of this constructor is preferred.  If the
-     *  stream has an underlying SkData (such as a SkMemoryStream)
-     *  pass that in.
-     *
-     *  This object will unref the stream when done.  Since streams
-     *  have internal state (position), the caller should not pass a
-     *  shared stream in.  Pass either a new duplicated stream in or
-     *  transfer ownership of the stream.  In the latter case, be sure
-     *  that there are no other consumers of the stream who will
-     *  modify the stream's position.  This constructor asserts
-     *  stream->unique().
-     *
-     *  For example:
-     *    SkStreamRewindable* stream;
-     *    ...
-     *    SkImageGenerator* gen
-     *        = SkNEW_ARGS(SkDecodingImageGenerator,
-     *                     (stream->duplicate()));
-     *    ...
-     *    SkDELETE(gen);
-     */
-    explicit SkDecodingImageGenerator(SkStreamRewindable* stream);
-
     virtual ~SkDecodingImageGenerator();
-
     virtual SkData* refEncodedData() SK_OVERRIDE;
-
+    // This implementaion of getInfo() always returns true.
     virtual bool getInfo(SkImageInfo* info) SK_OVERRIDE;
-
     virtual bool getPixels(const SkImageInfo& info,
                            void* pixels,
                            size_t rowBytes) SK_OVERRIDE;
-
     /**
-     *  Install the SkData into the destination bitmap, using a new
-     *  SkDiscardablePixelRef and a new SkDecodingImageGenerator.
-     *
-     *  @param data Contains the encoded image data that will be used
-     *  by the SkDecodingImageGenerator.  Will be ref()ed.
+     *  These options will be passed on to the image decoder.  The
+     *  defaults are sensible.
      *
-     *  @param destination Upon success, this bitmap will be
-     *  configured and have a pixelref installed.
+     *  @param fSampleSize If set to > 1, tells the decoder to return a
+     *         smaller than original bitmap, sampling 1 pixel for
+     *         every size pixels. e.g. if sample size is set to 3,
+     *         then the returned bitmap will be 1/3 as wide and high,
+     *         and will contain 1/9 as many pixels as the original.
+     *         Note: this is a hint, and the codec may choose to
+     *         ignore this, or only approximate the sample size.
      *
-     *  @param factory If not NULL, this object will be used as a
-     *  source of discardable memory when decoding.  If NULL, then
-     *  SkDiscardableMemory::Create() will be called.
+     *  @param fDitherImage Set to true if the the decoder should try to
+     *         dither the resulting image when decoding to a smaller
+     *         color-space.  The default is true.
      *
-     *  @return true iff successful.
+     *  @param fRequestedColorType If not given, then use whichever
+     *         config the decoder wants.  Else try to use this color
+     *         type.  If the decoder won't support this color type,
+     *         SkDecodingImageGenerator::Create will return
+     *         NULL. kIndex_8_SkColorType is not supported.
      */
-    static bool Install(SkData* data, SkBitmap* destination,
-                        SkDiscardableMemory::Factory* factory = NULL);
+    struct Options {
+        Options()
+            : fSampleSize(1)
+            , fDitherImage(true)
+            , fUseRequestedColorType(false)
+            , fRequestedColorType() { }
+        Options(int sampleSize, bool dither)
+            : fSampleSize(sampleSize)
+            , fDitherImage(dither)
+            , fUseRequestedColorType(false)
+            , fRequestedColorType() { }
+        Options(int sampleSize, bool dither, SkColorType colorType)
+            : fSampleSize(sampleSize)
+            , fDitherImage(dither)
+            , fUseRequestedColorType(true)
+            , fRequestedColorType(colorType) { }
+        const int         fSampleSize;
+        const bool        fDitherImage;
+        const bool        fUseRequestedColorType;
+        const SkColorType fRequestedColorType;
+    };
+
     /**
-     *  Install the stream into the destination bitmap, using a new
-     *  SkDiscardablePixelRef and a new SkDecodingImageGenerator.
+     *  These two functions return a SkImageGenerator that calls into
+     *  SkImageDecoder.  They return NULL on failure.
      *
-     *  The SkData version of this function is preferred.  If the
-     *  stream has an underlying SkData (such as a SkMemoryStream)
-     *  pass that in.
+     *  The SkData version of this function is preferred.  If the stream
+     *  has an underlying SkData (such as a SkMemoryStream) pass that in.
      *
-     *  @param stream The source of encoded data that will be passed
-     *  to the decoder.  The installed SkDecodingImageGenerator will
-     *  unref the stream when done.  If false is returned, this
-     *  function will perform the unref.  Since streams have internal
-     *  state (position), the caller should not pass a shared stream
-     *  in.  Pass either a new duplicated stream in or transfer
-     *  ownership of the stream.  In the latter case, be sure that
-     *  there are no other consumers of the stream who will modify the
-     *  stream's position.  This function will fail if
-     *  (!stream->unique()).
+     *  This object will unref the stream when done or on failure.  Since
+     *  streams have internal state (position), the caller should not pass
+     *  a shared stream in.  Pass either a new duplicated stream in or
+     *  transfer ownership of the stream.  This factory asserts
+     *  stream->unique().
      *
-     *  @param destination Upon success, this bitmap will be
-     *  configured and have a pixelref installed.
+     *  For example:
+     *    SkStreamRewindable* stream;
+     *    ...
+     *    SkImageGenerator* gen
+     *        = SkDecodingImageGenerator::Create(
+     *            stream->duplicate(), SkDecodingImageGenerator::Options());
+     *    ...
+     *    SkDELETE(gen);
      *
-     *  @param factory If not NULL, this object will be used as a
-     *  source of discardable memory when decoding.  If NULL, then
-     *  SkDiscardableMemory::Create() will be called.
+     *  @param Options (see above)
      *
-     *  @return true iff successful.
+     *  @return NULL on failure, a new SkImageGenerator on success.
      */
-    static bool Install(SkStreamRewindable* stream, SkBitmap* destination,
-                        SkDiscardableMemory::Factory* factory = NULL);
+    static SkImageGenerator* Create(SkStreamRewindable* stream,
+                                    const Options& opt);
+
+    /**
+     *  @param data Contains the encoded image data that will be used by
+     *         the SkDecodingImageGenerator.  Will be ref()ed by the
+     *         SkImageGenerator constructor and and unref()ed on deletion.
+     */
+    static SkImageGenerator* Create(SkData* data, const Options& opt);
 
 private:
-    SkData*             fData;
-    SkStreamRewindable* fStream;
-    SkImageInfo         fInfo;
-    bool                fHasInfo;
-    bool                fDoCopyTo;
+    SkData*                fData;
+    SkStreamRewindable*    fStream;
+    const SkImageInfo      fInfo;
+    const int              fSampleSize;
+    const bool             fDitherImage;
+    const SkBitmap::Config fRequestedConfig;
+    SkDecodingImageGenerator(SkData* data,
+                             SkStreamRewindable* stream,
+                             const SkImageInfo& info,
+                             int sampleSize,
+                             bool ditherImage,
+                             SkBitmap::Config requestedConfig);
+    static SkImageGenerator* Create(SkData*, SkStreamRewindable*,
+                                    const Options&);
+    typedef SkImageGenerator INHERITED;
 };
+
+//  // Example of most basic use case:
+//
+//  bool install_data(SkData* data, SkBitmap* dst) {
+//     return SkInstallDiscardablePixelRef(
+//         SkDecodingImageGenerator::Create(
+//             data, SkDecodingImageGenerator::Options()), dst, NULL);
+//  }
+//  bool install_stream(SkStreamRewindable* stream, SkBitmap* dst) {
+//     return SkInstallDiscardablePixelRef(
+//         SkDecodingImageGenerator::Create(
+//             stream, SkDecodingImageGenerator::Options()), dst, NULL);
+//  }
+
 #endif  // SkDecodingImageGenerator_DEFINED
index 668f57e..fb30d05 100644 (file)
@@ -12,7 +12,6 @@
 bool SkCachingPixelRef::Install(SkImageGenerator* generator,
                                 SkBitmap* dst) {
     SkImageInfo info;
-    SkASSERT(generator != NULL);
     SkASSERT(dst != NULL);
     if ((NULL == generator)
         || !(generator->getInfo(&info))
index 160ca5b..2886156 100644 (file)
@@ -72,20 +72,19 @@ bool SkInstallDiscardablePixelRef(SkImageGenerator* generator,
                                   SkBitmap* dst,
                                   SkDiscardableMemory::Factory* factory) {
     SkImageInfo info;
-    SkASSERT(generator != NULL);
-    if ((NULL == generator)
-        || (!generator->getInfo(&info))
+    SkAutoTDelete<SkImageGenerator> autoGenerator(generator);
+    if ((NULL == autoGenerator.get())
+        || (!autoGenerator->getInfo(&info))
         || (!dst->setConfig(info, 0))) {
-        SkDELETE(generator);
         return false;
     }
     SkASSERT(dst->config() != SkBitmap::kNo_Config);
-    if (dst->empty()) { // Use a normal pixelref.
-        SkDELETE(generator);  // Do not need this anymore.
+    if (dst->empty()) {  // Use a normal pixelref.
         return dst->allocPixels(NULL, NULL);
     }
-    SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef,
-                               (info, generator, dst->rowBytes(), factory)));
+    SkAutoTUnref<SkDiscardablePixelRef> ref(
+        SkNEW_ARGS(SkDiscardablePixelRef,
+                   (info, autoGenerator.detach(), dst->rowBytes(), factory)));
     dst->setPixelRef(ref);
     return true;
 }
index 4b66938..3367096 100644 (file)
 #include "SkImageInfo.h"
 #include "SkPixelRef.h"
 
+/**
+ *  A PixelRef backed by SkDiscardableMemory, with the ability to
+ *  re-generate the pixels (via a SkImageGenerator) if the DM is
+ *  purged.
+ *
+ *  Since SkColorTable is reference-counted, we do not support indexed
+ *  color with this class; there would be no way for the discardable
+ *  memory system to unref the color table.
+ */
 class SkDiscardablePixelRef : public SkPixelRef {
 public:
     SK_DECLARE_UNFLATTENABLE_OBJECT()
index 04eddd9..434724d 100644 (file)
@@ -146,11 +146,14 @@ static void test_three_encodings(skiatest::Reporter* reporter,
 ////////////////////////////////////////////////////////////////////////////////
 static bool install_skCachingPixelRef(SkData* encoded, SkBitmap* dst) {
     return SkCachingPixelRef::Install(
-        SkNEW_ARGS(SkDecodingImageGenerator, (encoded)), dst);
+        SkDecodingImageGenerator::Create(
+            encoded, SkDecodingImageGenerator::Options()), dst);
 }
 static bool install_skDiscardablePixelRef(SkData* encoded, SkBitmap* dst) {
     // Use system-default discardable memory.
-    return SkDecodingImageGenerator::Install(encoded, dst, NULL);
+    return SkInstallDiscardablePixelRef(
+        SkDecodingImageGenerator::Create(
+            encoded, SkDecodingImageGenerator::Options()), dst, NULL);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -213,10 +216,12 @@ public:
         }
         return true;
     }
+
 private:
     const TestType fType;
     skiatest::Reporter* const fReporter;
 };
+
 void CheckTestImageGeneratorBitmap(skiatest::Reporter* reporter,
                                    const SkBitmap& bm) {
     REPORTER_ASSERT(reporter, TestImageGenerator::Width() == bm.width());
index bbc31cc..d39a51a 100644 (file)
 #include "SkColorPriv.h"
 #include "SkData.h"
 #include "SkDecodingImageGenerator.h"
+#include "SkDiscardableMemoryPool.h"
 #include "SkForceLinking.h"
 #include "SkGradientShader.h"
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
+#include "SkImageGenerator.h"
+#include "SkImagePriv.h"
 #include "SkOSFile.h"
 #include "SkPoint.h"
 #include "SkShader.h"
@@ -153,7 +156,7 @@ static void test_unpremul(skiatest::Reporter* reporter) {
     if (iter.next(&basename)) {
         do {
             SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
-            //SkDebugf("about to decode \"%s\"\n", filename.c_str());
+            // SkDebugf("about to decode \"%s\"\n", filename.c_str());
             compare_unpremul(reporter, filename);
         } while (iter.next(&basename));
     } else {
@@ -201,7 +204,7 @@ static void test_stream_life() {
         SkImageEncoder::kWEBP_Type,
     };
     for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
-        //SkDebugf("encoding to %i\n", i);
+        // SkDebugf("encoding to %i\n", i);
         SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
         if (NULL == stream.get()) {
             SkDebugf("no stream\n");
@@ -228,7 +231,7 @@ static void test_stream_life() {
 // Test inside SkScaledBitmapSampler.cpp
 extern void test_row_proc_choice();
 
-#endif // SK_DEBUG
+#endif  // SK_DEBUG
 
 DEF_TEST(ImageDecoding, reporter) {
     test_unpremul(reporter);
@@ -239,6 +242,31 @@ DEF_TEST(ImageDecoding, reporter) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+namespace {
+// expected output for 8x8 bitmap
+const int kExpectedWidth = 8;
+const int kExpectedHeight = 8;
+const SkColor kExpectedPixels[] = {
+    0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
+    0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
+    0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
+    0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
+    0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
+    0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
+    0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
+    0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
+    0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
+    0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
+    0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
+    0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
+    0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
+    0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
+    0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
+    0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
+};
+SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
+                  == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
+}  // namespace
 
 DEF_TEST(WebP, reporter) {
     const unsigned char encodedWebP[] = {
@@ -269,38 +297,26 @@ DEF_TEST(WebP, reporter) {
         0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
         0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
     };
-    const SkColor thePixels[]  = {
-        0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
-        0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
-        0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
-        0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
-        0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
-        0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
-        0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
-        0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
-        0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
-        0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
-        0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
-        0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
-        0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
-        0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
-        0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
-        0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
-    };
     SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
                                                 sizeof(encodedWebP)));
     SkBitmap bm;
-    bool success = SkDecodingImageGenerator::Install(encoded, &bm, NULL);
+
+    bool success = SkInstallDiscardablePixelRef(
+        SkDecodingImageGenerator::Create(encoded,
+            SkDecodingImageGenerator::Options()), &bm, NULL);
+
     REPORTER_ASSERT(reporter, success);
     if (!success) {
         return;
     }
     SkAutoLockPixels alp(bm);
-    bool rightSize = SK_ARRAY_COUNT(thePixels) == bm.width() * bm.height();
+
+    bool rightSize = ((kExpectedWidth == bm.width()) 
+                      && (kExpectedHeight == bm.height()));
     REPORTER_ASSERT(reporter, rightSize);
     if (rightSize) {
         bool error = false;
-        const SkColor* correctPixel = thePixels;
+        const SkColor* correctPixel = kExpectedPixels;
         for (int y = 0; y < bm.height(); ++y) {
             for (int x = 0; x < bm.width(); ++x) {
                 error |= (*correctPixel != bm.getColor(x, y));
@@ -310,3 +326,256 @@ DEF_TEST(WebP, reporter) {
         REPORTER_ASSERT(reporter, !error);
     }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+// example of how Android will do this inside their BitmapFactory
+static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
+                                     SkStreamRewindable* stream,
+                                     int sampleSize, bool ditherImage) {
+    SkASSERT(bitmap != NULL);
+    SkASSERT(stream != NULL);
+    SkASSERT(stream->rewind());
+    SkASSERT(stream->unique());
+    SkColorType colorType;
+    if (!SkBitmapConfigToColorType(bitmap->config(), &colorType)) {
+        return NULL;
+    }
+    SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
+    SkAutoTDelete<SkImageGenerator> gen(
+        SkDecodingImageGenerator::Create(stream, opts));
+    SkImageInfo info;
+    if ((NULL == gen.get()) || !gen->getInfo(&info)) {
+        return NULL;
+    }
+    SkDiscardableMemory::Factory* factory = NULL;
+    if (info.getSafeSize(info.minRowBytes()) < (32 * 1024)) {
+        // only use ashmem for large images, since mmaps come at a price
+        factory = SkGetGlobalDiscardableMemoryPool();
+    }
+    if (SkInstallDiscardablePixelRef(gen.detach(), bitmap, factory)) {
+        return bitmap->pixelRef();
+    }
+    return NULL;
+}
+/**
+ *  A test for the SkDecodingImageGenerator::Create and
+ *  SkInstallDiscardablePixelRef functions.
+ */
+DEF_TEST(ImprovedBitmapFactory, reporter) {
+    SkString resourcePath = skiatest::Test::GetResourcePath();
+    SkString directory = SkOSPath::SkPathJoin(resourcePath.c_str(), "encoding");
+    SkString path = SkOSPath::SkPathJoin(directory.c_str(), "randPixels.png");
+    SkAutoTUnref<SkStreamRewindable> stream(
+        SkStream::NewFromFile(path.c_str()));
+    if (sk_exists(path.c_str())) {
+        SkBitmap bm;
+        SkAssertResult(bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1));
+        REPORTER_ASSERT(reporter,
+            NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
+        SkAutoLockPixels alp(bm);
+        REPORTER_ASSERT(reporter, NULL != bm.getPixels());
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
+static inline bool check_rounding(int value, int dividend, int divisor) {
+    // returns true if (dividend/divisor) rounds up OR down to value
+    return (((divisor * value) > (dividend - divisor))
+            && ((divisor * value) < (dividend + divisor)));
+}
+#endif  // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
+
+
+#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
+    #define kBackwards_SkColorType kRGBA_8888_SkColorType
+#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
+    #define kBackwards_SkColorType kBGRA_8888_SkColorType
+#else
+    #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
+#endif
+
+static inline const char* SkColorType_to_string(SkColorType colorType) {
+    switch(colorType) {
+        case kAlpha_8_SkColorType:   return "Alpha_8";
+        case kRGB_565_SkColorType:   return "RGB_565";
+        case kARGB_4444_SkColorType: return "ARGB_4444";
+        case kPMColor_SkColorType:   return "PMColor";
+        case kBackwards_SkColorType: return "Backwards";
+        case kIndex_8_SkColorType:   return "Index_8";
+        default:                     return "ERROR";
+    }
+}
+
+/**
+ * Given either a SkStream or a SkData, try to decode the encoded
+ * image using the specified options and report errors.
+ */
+static void test_options(skiatest::Reporter* reporter,
+                         const SkDecodingImageGenerator::Options& opts,
+                         SkStreamRewindable* encodedStream,
+                         SkData* encodedData,
+                         bool useData,
+                         const SkString& path) {
+    SkBitmap bm;
+    bool success = false;
+    if (useData) {
+        if (NULL == encodedData) {
+            return;
+        }
+        success = SkInstallDiscardablePixelRef(
+            SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL);
+    } else {
+        if (NULL == encodedStream) {
+            return;
+        }
+        success = SkInstallDiscardablePixelRef(
+            SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts),
+            &bm, NULL);
+    }
+    if (!success) {
+        if (opts.fUseRequestedColorType
+            && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
+            return;  // Ignore known conversion inabilities.
+        }
+        // If we get here, it's a failure and we will need more
+        // information about why it failed.
+        reporter->reportFailed(SkStringPrintf(
+            "Bounds decode failed "
+            "[sampleSize=%d dither=%s colorType=%s %s] %s:%d",
+            opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
+            (opts.fUseRequestedColorType
+             ? SkColorType_to_string(opts.fRequestedColorType) : "(none)"),
+            path.c_str(), __FILE__, __LINE__));
+        return;
+    }
+    #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
+    // Android is the only system that use Skia's image decoders in
+    // production.  For now, we'll only verify that samplesize works
+    // on systems where it already is known to work.
+    REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
+                                             opts.fSampleSize));
+    REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
+                                             opts.fSampleSize));
+    #endif  // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
+    SkAutoLockPixels alp(bm);
+    if (bm.getPixels() == NULL) {
+        reporter->reportFailed(SkStringPrintf(
+            "Pixel decode failed "
+            "[sampleSize=%d dither=%s colorType=%s %s] %s:%d",
+            opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
+            (opts.fUseRequestedColorType
+             ? SkColorType_to_string(opts.fRequestedColorType) : "(none)"),
+            path.c_str(), __FILE__, __LINE__));
+        return;
+    }
+
+    SkBitmap::Config requestedConfig
+        = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
+    REPORTER_ASSERT(reporter,
+                    (!opts.fUseRequestedColorType)
+                    || (bm.config() == requestedConfig));
+
+    // Condition under which we should check the decoding results:
+    if ((SkBitmap::kARGB_8888_Config == bm.config())
+        && (!path.endsWith(".jpg"))  // lossy
+        && (opts.fSampleSize == 1)) {  // scaled
+        const SkColor* correctPixels = kExpectedPixels;
+        SkASSERT(bm.height() == kExpectedHeight);
+        SkASSERT(bm.width() == kExpectedWidth);
+        int pixelErrors = 0;
+        for (int y = 0; y < bm.height(); ++y) {
+            for (int x = 0; x < bm.width(); ++x) {
+                if (*correctPixels != bm.getColor(x, y)) {
+                    ++pixelErrors;
+                }
+                ++correctPixels;
+            }
+        }
+        if (pixelErrors != 0) {
+            reporter->reportFailed(SkStringPrintf(
+                "Pixel-level mismatch (%d of %d) [sampleSize=%d "
+                "dither=%s colorType=%s %s] %s:%d",
+                pixelErrors, kExpectedHeight * kExpectedWidth,
+                opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
+                (opts.fUseRequestedColorType
+                 ? SkColorType_to_string(opts.fRequestedColorType)
+                 : "(none)"), path.c_str(), __FILE__, __LINE__));
+        }
+    }
+}
+
+/**
+ *  SkDecodingImageGenerator has an Options struct which lets the
+ *  client of the generator set sample size, dithering, and bitmap
+ *  config.  This test loops through many possible options and tries
+ *  them on a set of 5 small encoded images (each in a different
+ *  format).  We test both SkData and SkStreamRewindable decoding.
+ */
+DEF_TEST(ImageDecoderOptions, reporter) {
+    const char* files[]  = {
+        "randPixels.bmp",
+        "randPixels.jpg",
+        "randPixels.png",
+        "randPixels.webp",
+        #if !defined(SK_BUILD_FOR_WIN)
+        // TODO(halcanary): Find out why this fails sometimes.
+        "randPixels.gif",
+        #endif
+    };
+
+    SkString resourceDir = skiatest::Test::GetResourcePath();
+    SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding");
+    if (!sk_exists(directory.c_str())) {
+        return;
+    }
+
+    int scaleList[] = {1, 2, 3, 4};
+    bool ditherList[] = {true, false};
+    SkColorType colorList[] = {
+        kAlpha_8_SkColorType,
+        kRGB_565_SkColorType,
+        kARGB_4444_SkColorType,  // Most decoders will fail on 4444.
+        kPMColor_SkColorType
+        // Note that indexed color is left out of the list.  Lazy
+        // decoding doesn't do indexed color.
+    };
+    const bool useDataList[] = {true, false};
+
+    for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
+        SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]);
+        if (!sk_exists(path.c_str())) {
+            continue;
+        }
+
+        SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
+        REPORTER_ASSERT(reporter, encodedData.get() != NULL);
+        SkAutoTUnref<SkStreamRewindable> encodedStream(
+            SkStream::NewFromFile(path.c_str()));
+        REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
+            for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
+                for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
+                    for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
+                        SkDecodingImageGenerator::Options opts(scaleList[i],
+                                                               ditherList[j],
+                                                               colorList[k]);
+                        test_options(reporter, opts, encodedStream, encodedData,
+                                     useDataList[m], path);
+
+                    }
+                    SkDecodingImageGenerator::Options options(scaleList[i],
+                                                              ditherList[j]);
+                    test_options(reporter, options, encodedStream, encodedData,
+                                 useDataList[m], path);
+                }
+            }
+        }
+    }
+}
+////////////////////////////////////////////////////////////////////////////////
+
index 447ce4e..b90cc03 100644 (file)
@@ -13,6 +13,8 @@
 #include "SkData.h"
 #include "SkDecodingImageGenerator.h"
 #include "SkError.h"
+#include "SkImageEncoder.h"
+#include "SkImageGenerator.h"
 #include "SkPaint.h"
 #include "SkPicture.h"
 #include "SkPictureUtils.h"
@@ -338,8 +340,6 @@ static void test_bad_bitmap() {
 }
 #endif
 
-#include "SkImageEncoder.h"
-
 static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
     *offset = 0;
     return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
@@ -381,7 +381,8 @@ static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
     SkAutoDataUnref data(wStream.copyToData());
 
     SkBitmap bm;
-    bool installSuccess = SkDecodingImageGenerator::Install(data, &bm);
+    bool installSuccess = SkInstallDiscardablePixelRef(
+         SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL);
     REPORTER_ASSERT(reporter, installSuccess);
 
     // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
index 9a4a36f..83fca5f 100644 (file)
@@ -31,10 +31,11 @@ bool sk_tools::LazyDecodeBitmap(const void* src,
         return false;
     }
 
-    SkAutoTDelete<SkImageGenerator> gen(SkNEW_ARGS(SkDecodingImageGenerator,
-                                                   (data)));
+    SkAutoTDelete<SkImageGenerator> gen(
+        SkDecodingImageGenerator::Create(
+            data, SkDecodingImageGenerator::Options()));
     SkImageInfo info;
-    if (!gen->getInfo(&info)) {
+    if ((NULL == gen.get()) || !gen->getInfo(&info)) {
         return false;
     }
     SkDiscardableMemory::Factory* pool = NULL;