Add Options to SkDecodingImageGenerator, simplify API.

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
diff --git a/gm/factory.cpp b/gm/factory.cpp
index 19eb63b..efd817d 100644
--- a/gm/factory.cpp
+++ b/gm/factory.cpp
@@ -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 @@
             // 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 @@
 static GM* MyFactory(void*) { return new FactoryGM; }
 static GMRegistry reg(MyFactory);
 
-}
+}  // namespace skiagm
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index d56f8f8..220973a 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -21,6 +21,8 @@
  *  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
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
index af06e82..767fd61 100644
--- a/samplecode/SamplePicture.cpp
+++ b/samplecode/SamplePicture.cpp
@@ -40,7 +40,8 @@
     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;
 }
diff --git a/src/image/SkImagePriv.cpp b/src/image/SkImagePriv.cpp
index 976a5b3..43cc44b 100644
--- a/src/image/SkImagePriv.cpp
+++ b/src/image/SkImagePriv.cpp
@@ -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 @@
     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)) {
diff --git a/src/image/SkImagePriv.h b/src/image/SkImagePriv.h
index bf28f59..7c19c73 100644
--- a/src/image/SkImagePriv.h
+++ b/src/image/SkImagePriv.h
@@ -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*,
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp
index a833c63..153d1e2 100644
--- a/src/images/SkDecodingImageGenerator.cpp
+++ b/src/images/SkDecodingImageGenerator.cpp
@@ -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,56 +21,55 @@
  */
 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;
 };
-}  // namespace
-////////////////////////////////////////////////////////////////////////////////
-
-SkDecodingImageGenerator::SkDecodingImageGenerator(SkData* data)
-    : 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());
-}
-
-SkDecodingImageGenerator::~SkDecodingImageGenerator() {
-    SkSafeUnref(fData);
-    fStream->unref();
-}
 
 // TODO(halcanary): Give this macro a better name and move it into SkTypes.h
 #ifdef SK_DEBUG
@@ -78,8 +78,49 @@
     #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,
+        SkStreamRewindable* stream,
+        const SkImageInfo& info,
+        int sampleSize,
+        bool ditherImage,
+        SkBitmap::Config requestedConfig)
+    : fData(data)
+    , fStream(stream)
+    , fInfo(info)
+    , fSampleSize(sampleSize)
+    , fDitherImage(ditherImage)
+    , fRequestedConfig(requestedConfig) {
+    SkASSERT(stream != NULL);
+    SkSafeRef(fData);  // may be NULL.
+}
+
+SkDecodingImageGenerator::~SkDecodingImageGenerator() {
+    SkSafeUnref(fData);
+    fStream->unref();
+}
+
+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 @@
     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)) {
+    if (fInfo != info) {
+        // 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;
     }
-    if (SkImageInfoToBitmapConfig(info) == SkBitmap::kNo_Config) {
-        return false;  // Unsupported SkColorType.
+    int bpp = SkBitmap::ComputeBytesPerPixel(fRequestedConfig);
+    if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) {
+        // The caller has specified a bad rowBytes.
+        return false;
     }
+
     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.
-        return false;
-    }
-    int bpp = SkBitmap::ComputeBytesPerPixel(SkImageInfoToBitmapConfig(info));
-    if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) {
-        return false;
-    }
-    SkBitmap bitmap;
-    if (!bitmap.setConfig(info, rowBytes)) {
-        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;
     }
-    SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (stream)));
-    return SkInstallDiscardablePixelRef(gen, dst, factory);
+    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);
+    }
+    return SkNEW_ARGS(SkDecodingImageGenerator,
+                      (data, autoStream.detach(), info,
+                       opts.fSampleSize, opts.fDitherImage, config));
 }
diff --git a/src/images/SkDecodingImageGenerator.h b/src/images/SkDecodingImageGenerator.h
index dba234b..12a49d5 100644
--- a/src/images/SkDecodingImageGenerator.h
+++ b/src/images/SkDecodingImageGenerator.h
@@ -8,113 +8,134 @@
 #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.
+    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;
+    /**
+     *  These options will be passed on to the image decoder.  The
+     *  defaults are sensible.
      *
-     *  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
+     *  @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 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.
+     *
+     *  @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.
+     */
+    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;
+    };
+
+    /**
+     *  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.
+     *
+     *  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().
      *
      *  For example:
      *    SkStreamRewindable* stream;
      *    ...
      *    SkImageGenerator* gen
-     *        = SkNEW_ARGS(SkDecodingImageGenerator,
-     *                     (stream->duplicate()));
+     *        = SkDecodingImageGenerator::Create(
+     *            stream->duplicate(), SkDecodingImageGenerator::Options());
      *    ...
      *    SkDELETE(gen);
+     *
+     *  @param Options (see above)
+     *
+     *  @return NULL on failure, a new SkImageGenerator on success.
      */
-    explicit SkDecodingImageGenerator(SkStreamRewindable* stream);
-
-    virtual ~SkDecodingImageGenerator();
-
-    virtual SkData* refEncodedData() SK_OVERRIDE;
-
-    virtual bool getInfo(SkImageInfo* info) SK_OVERRIDE;
-
-    virtual bool getPixels(const SkImageInfo& info,
-                           void* pixels,
-                           size_t rowBytes) SK_OVERRIDE;
+    static SkImageGenerator* Create(SkStreamRewindable* stream,
+                                    const Options& opt);
 
     /**
-     *  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.
-     *
-     *  @param destination Upon success, this bitmap will be
-     *  configured and have a pixelref installed.
-     *
-     *  @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.
-     *
-     *  @return true iff successful.
+     *  @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 bool Install(SkData* data, SkBitmap* destination,
-                        SkDiscardableMemory::Factory* factory = NULL);
-    /**
-     *  Install the stream into the destination bitmap, using a new
-     *  SkDiscardablePixelRef and a new SkDecodingImageGenerator.
-     *
-     *  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()).
-     *
-     *  @param destination Upon success, this bitmap will be
-     *  configured and have a pixelref installed.
-     *
-     *  @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.
-     *
-     *  @return true iff successful.
-     */
-    static bool Install(SkStreamRewindable* stream, SkBitmap* destination,
-                        SkDiscardableMemory::Factory* factory = NULL);
+    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
diff --git a/src/lazy/SkCachingPixelRef.cpp b/src/lazy/SkCachingPixelRef.cpp
index 668f57e..fb30d05 100644
--- a/src/lazy/SkCachingPixelRef.cpp
+++ b/src/lazy/SkCachingPixelRef.cpp
@@ -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))
diff --git a/src/lazy/SkDiscardablePixelRef.cpp b/src/lazy/SkDiscardablePixelRef.cpp
index 160ca5b..2886156 100644
--- a/src/lazy/SkDiscardablePixelRef.cpp
+++ b/src/lazy/SkDiscardablePixelRef.cpp
@@ -72,20 +72,19 @@
                                   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;
 }
diff --git a/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h
index 4b66938..3367096 100644
--- a/src/lazy/SkDiscardablePixelRef.h
+++ b/src/lazy/SkDiscardablePixelRef.h
@@ -13,6 +13,15 @@
 #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()
diff --git a/tests/CachedDecodingPixelRefTest.cpp b/tests/CachedDecodingPixelRefTest.cpp
index 04eddd9..434724d 100644
--- a/tests/CachedDecodingPixelRefTest.cpp
+++ b/tests/CachedDecodingPixelRefTest.cpp
@@ -146,11 +146,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 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 @@
         }
         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());
diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp
index bbc31cc..d39a51a 100644
--- a/tests/ImageDecodingTest.cpp
+++ b/tests/ImageDecodingTest.cpp
@@ -13,10 +13,13 @@
 #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 @@
     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 @@
         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 @@
 // 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 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+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 @@
         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 @@
         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);
+                }
+            }
+        }
+    }
+}
+////////////////////////////////////////////////////////////////////////////////
+
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 447ce4e..b90cc03 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -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 @@
 }
 #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 @@
     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.
diff --git a/tools/LazyDecodeBitmap.cpp b/tools/LazyDecodeBitmap.cpp
index 9a4a36f..83fca5f 100644
--- a/tools/LazyDecodeBitmap.cpp
+++ b/tools/LazyDecodeBitmap.cpp
@@ -31,10 +31,11 @@
         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;