Move all knowledge of X sampling into SkScaledCodec

Prior to this CL, each SkCodec subclass that allows sampling does an
extra check in onStartScanlineDecode to determine whether the X dimension
is supported for sampling. Remove this check, and provide a way for
SkScaledCodec to directly access the SkSwizzler, and update it to do
sampling. This way, the SkCodec knows nothing of sampling, but we can
still save the extra step of sampling afterwards.

FIXME: SkBmpRLECodec still calls SkScaledCodec::DimensionsSupported. It
seems like it could directly support sampling, rather than dealing with
SkScaledCodec (partially).

Add a new base class for SkSwizzler. It allows updating the swizzler
after it was created, which is done by SkScaledCodec. Modify SkSwizzler's
constructor/factory function to stop taking any info about sampling,
assume the sample size is one, and move modifying that into a virtual
function overridden from the base class.

BUG=skia:4284

Review URL: https://codereview.chromium.org/1372973002
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index c0cce85..7300a5c 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -17,6 +17,7 @@
 #include "SkTypes.h"
 
 class SkData;
+class SkSampler;
 
 /**
  *  Abstraction layer directly on top of an image codec.
@@ -382,6 +383,15 @@
         return this->getInfo().dimensions();
     }
 
+    // FIXME: What to do about subsets??
+    /**
+     *  Subclasses should override if they support dimensions other than the
+     *  srcInfo's.
+     */
+    virtual bool onDimensionsSupported(const SkISize&) {
+        return false;
+    }
+
     virtual SkEncodedFormat onGetEncodedFormat() const = 0;
 
     virtual Result onGetPixels(const SkImageInfo& info,
@@ -455,6 +465,19 @@
     SkCodec::Options        fOptions;
     int                     fCurrScanline;
 
+    /**
+     *  Return whether these dimensions are supported as a scale.
+     *
+     *  The codec may choose to cache the information about scale and subset.
+     *  Either way, the same information will be passed to onGetPixels/onStart
+     *  on success.
+     *
+     *  This must return true for a size returned from getScaledDimensions.
+     */
+    bool dimensionsSupported(const SkISize& dim) {
+        return dim == fSrcInfo.dimensions() || this->onDimensionsSupported(dim);
+    }
+
     // Methods for scanline decoding.
     virtual SkCodec::Result onStartScanlineDecode(const SkImageInfo& dstInfo,
             const SkCodec::Options& options, SkPMColor ctable[], int* ctableCount) {
@@ -477,5 +500,17 @@
         return kUnimplemented;
     }
 
+    /**
+     *  Return an object which will allow forcing scanline decodes to sample in X.
+     *
+     *  May create a sampler, if one is not currently being used. Otherwise, does
+     *  not affect ownership.
+     *
+     *  Only valid during scanline decoding.
+     */
+    virtual SkSampler* getSampler() { return nullptr; }
+
+    // Needed to call getSampler and dimensionsSupported.
+    friend class SkScaledCodec;
 };
 #endif // SkCodec_DEFINED
diff --git a/include/codec/SkScaledCodec.h b/include/codec/SkScaledCodec.h
index 20428d8..92ef19e 100644
--- a/include/codec/SkScaledCodec.h
+++ b/include/codec/SkScaledCodec.h
@@ -22,23 +22,7 @@
 
     virtual ~SkScaledCodec();
 
-    /**
-     * returns whether a destination's dimensions are supported for down sampling
-     */
-    static bool DimensionsSupportedForSampling(const SkImageInfo& srcInfo, 
-                                               const SkImageInfo& dstInfo) {
-        // heights must be equal as no native y sampling is supported
-        if (dstInfo.height() != srcInfo.height()) {
-            return false;
-        }
-        // only support down sampling, dstWidth cannot be larger that srcWidth
-        if(dstInfo.width() > srcInfo.width()) {
-            return false;
-        }
-        return true;   
-    }
-
-    static void ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
+    static void ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim,
                                   int* sampleSizeX, int* sampleSizeY);
 
 protected:
@@ -46,6 +30,7 @@
      * Recommend a set of destination dimensions given a requested scale
      */
     SkISize onGetScaledDimensions(float desiredScale) const override;
+    bool onDimensionsSupported(const SkISize&) override;
 
     Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
             override;
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 4ae57c0..da3e0a2 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -580,11 +580,6 @@
         // Subsets are not supported.
         return kUnimplemented;
     }
-    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
-        if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
-            return SkCodec::kInvalidScale;
-        }
-    }
     if (!conversion_possible(dstInfo, this->getInfo())) {
         SkCodecPrintf("Error: cannot convert input type to output type.\n");
         return kInvalidConversion;
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index ea6f789..19f6603 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -10,7 +10,6 @@
 #include "SkCodec.h"
 #include "SkColorTable.h"
 #include "SkImageInfo.h"
-#include "SkMaskSwizzler.h"
 #include "SkStream.h"
 #include "SkSwizzler.h"
 #include "SkTypes.h"
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 0b12912..1c44c7f 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -45,6 +45,7 @@
 private:
 
     bool initializeSwizzler(const SkImageInfo& dstInfo);
+    SkSampler* getSampler() override { return fMaskSwizzler; }
 
     Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
                       const Options& opts) override;
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index e76a23e..fe499c6 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -43,10 +43,6 @@
         // Subsets are not supported.
         return kUnimplemented;
     }
-    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
-        SkCodecPrintf("Error: scaling not supported.\n");
-        return kInvalidScale;
-    }
     if (!conversion_possible(dstInfo, this->getInfo())) {
         SkCodecPrintf("Error: cannot convert input type to output type.\n");
         return kInvalidConversion;
@@ -253,6 +249,9 @@
 
 SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo,
         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
+    // Reset fSampleX. If it needs to be a value other than 1, it will get modified by
+    // the sampler.
+    fSampleX = 1;
     // Create the color table if necessary and prepare the stream for decode
     // Note that if it is non-NULL, inputColorCount will be modified
     if (!this->createColorTable(inputColorCount)) {
@@ -269,8 +268,6 @@
         return SkCodec::kInvalidConversion;
     }
 
-    SkScaledCodec::ComputeSampleSize(dstInfo, this->getInfo(), &fSampleX, NULL);
-
     return SkCodec::kSuccess;
 }
 
@@ -278,7 +275,7 @@
  * Performs the bitmap decoding for RLE input format
  * RLE decoding is performed all at once, rather than a one row at a time
  */
-SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& dstInfo,
+SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
                                           void* dst, size_t dstRowBytes,
                                           const Options& opts) {
     // Set RLE flags
@@ -289,7 +286,10 @@
 
     // Set constant values
     const int width = this->getInfo().width();
-    const int height = dstInfo.height();
+    const int height = info.height();
+
+    // Account for sampling.
+    SkImageInfo dstInfo = info.makeWH(get_scaled_dimension(width, fSampleX), height);
 
     // Destination parameters
     int x = 0;
@@ -466,3 +466,33 @@
         }
     }
 }
+
+class SkBmpRLESampler : public SkSampler {
+public:
+    SkBmpRLESampler(SkBmpRLECodec* codec)
+        : fCodec(codec)
+    {
+        SkASSERT(fCodec);
+    }
+
+private:
+    int onSetSampleX(int sampleX) {
+        return fCodec->setSampleX(sampleX);
+    }
+
+    // Unowned pointer. fCodec will delete this class in its destructor.
+    SkBmpRLECodec* fCodec;
+};
+
+SkSampler* SkBmpRLECodec::getSampler() {
+    if (!fSampler) {
+        fSampler.reset(new SkBmpRLESampler(this));
+    }
+
+    return fSampler;
+}
+
+int SkBmpRLECodec::setSampleX(int sampleX) {
+    fSampleX = sampleX;
+    return get_scaled_dimension(this->getInfo().width(), sampleX);
+}
diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h
index ea2c2b6..8721969 100644
--- a/src/codec/SkBmpRLECodec.h
+++ b/src/codec/SkBmpRLECodec.h
@@ -8,6 +8,7 @@
 #include "SkBmpCodec.h"
 #include "SkColorTable.h"
 #include "SkImageInfo.h"
+#include "SkSampler.h"
 #include "SkTypes.h"
 
 /*
@@ -39,6 +40,8 @@
             uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
             size_t RLEBytes);
 
+    int setSampleX(int);
+
 protected:
 
     Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
@@ -84,6 +87,8 @@
     Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
                       const Options& opts) override;
 
+    SkSampler* getSampler() override;
+
     SkAutoTUnref<SkColorTable>          fColorTable;    // owned
     const uint32_t                      fNumColors;
     const uint32_t                      fBytesPerColor;
@@ -92,6 +97,7 @@
     size_t                              fRLEBytes;
     uint32_t                            fCurrRLEByte;
     int                                 fSampleX;
+    SkAutoTDelete<SkSampler>            fSampler;
 
     typedef SkBmpCodec INHERITED;
 };
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index 5e65ebb..540b9f3 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -196,7 +196,7 @@
 
     // Create swizzler
     fSwizzler.reset(SkSwizzler::CreateSwizzler(config,
-            colorPtr, dstInfo, opts.fZeroInitialized, this->getInfo()));
+            colorPtr, dstInfo, opts.fZeroInitialized));
 
     if (nullptr == fSwizzler.get()) {
         return false;
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
index 0d81cbf..0263570 100644
--- a/src/codec/SkBmpStandardCodec.h
+++ b/src/codec/SkBmpStandardCodec.h
@@ -54,6 +54,8 @@
             const SkCodec::Options& options, SkPMColor inputColorPtr[],
             int* inputColorCount) override;
 
+    SkSampler* getSampler() override { return fSwizzler; }
+
 private:
 
     /*
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index cabcadc..1a901a9 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -152,7 +152,21 @@
     Options optsStorage;
     if (nullptr == options) {
         options = &optsStorage;
+    } else if (options->fSubset) {
+        SkIRect subset(*options->fSubset);
+        if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
+            // FIXME: How to differentiate between not supporting subset at all
+            // and not supporting this particular subset?
+            return kUnimplemented;
+        }
     }
+
+    // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
+    // because it supports arbitrary scaling/subset combinations.
+    if (!this->dimensionsSupported(info.dimensions())) {
+        return kInvalidScale;
+    }
+
     const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount);
 
     if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
@@ -190,6 +204,18 @@
     Options optsStorage;
     if (nullptr == options) {
         options = &optsStorage;
+    } else if (options->fSubset) {
+        SkIRect subset(*options->fSubset);
+        if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
+            // FIXME: How to differentiate between not supporting subset at all
+            // and not supporting this particular subset?
+            return kUnimplemented;
+        }
+    }
+
+    // FIXME: Support subsets somehow?
+    if (!this->dimensionsSupported(dstInfo.dimensions())) {
+        return kInvalidScale;
     }
 
     const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
@@ -213,8 +239,8 @@
     }
 
     SkASSERT(!fDstInfo.isEmpty());
-    if ((rowBytes < fDstInfo.minRowBytes() && countLines > 1 ) || countLines <= 0
-         || fCurrScanline + countLines > fDstInfo.height()) {
+
+    if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
         return kInvalidParameters;
     }
 
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp
index e6d1141..6134e96 100644
--- a/src/codec/SkCodec_libgif.cpp
+++ b/src/codec/SkCodec_libgif.cpp
@@ -9,7 +9,6 @@
 #include "SkCodecPriv.h"
 #include "SkColorPriv.h"
 #include "SkColorTable.h"
-#include "SkScaledCodec.h"
 #include "SkStream.h"
 #include "SkSwizzler.h"
 #include "SkUtils.h"
@@ -485,7 +484,7 @@
         ZeroInitialized zeroInit) {
     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
     fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex,
-            colorPtr, dstInfo, zeroInit, this->getInfo()));
+            colorPtr, dstInfo, zeroInit));
     if (nullptr != fSwizzler.get()) {
         return kSuccess;
     }
@@ -585,20 +584,9 @@
         return result;
     }
 
-    // Check to see if scaling was requested.
-    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
-        if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
-            return gif_error("Scaling not supported.\n", SkCodec::kInvalidScale);
-        }
-    }
-
     // Initialize the swizzler
     if (fFrameIsSubset) {
-        int sampleX;
-        SkScaledCodec::ComputeSampleSize(dstInfo, this->getInfo(), &sampleX, NULL);
-        const SkImageInfo subsetDstInfo = dstInfo.makeWH(
-                    get_scaled_dimension(fFrameDims.width(), sampleX),
-                    fFrameDims.height());
+        const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFrameDims.height());
         if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) {
             return gif_error("Could not initialize swizzler.\n", kUnimplemented);
         }
diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h
index d7dc2b9..c29cbdb 100644
--- a/src/codec/SkCodec_libgif.h
+++ b/src/codec/SkCodec_libgif.h
@@ -124,6 +124,8 @@
      */
     Result initializeSwizzler(const SkImageInfo& dstInfo, ZeroInitialized zeroInit);
 
+    SkSampler* getSampler() override { return fSwizzler; }
+
     /*
      * @return kSuccess if the read is successful and kIncompleteInput if the
      *         read fails.
diff --git a/src/codec/SkCodec_libico.cpp b/src/codec/SkCodec_libico.cpp
index 798169d..8b38148 100644
--- a/src/codec/SkCodec_libico.cpp
+++ b/src/codec/SkCodec_libico.cpp
@@ -206,10 +206,6 @@
     // We set the dimensions to the largest candidate image by default.
     // Regardless of the scale request, this is the largest image that we
     // will decode.
-    if (desiredScale >= 1.0) {
-        return this->getInfo().dimensions();
-    }
-
     int origWidth = this->getInfo().width();
     int origHeight = this->getInfo().height();
     float desiredSize = desiredScale * origWidth * origHeight;
@@ -230,6 +226,17 @@
     return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions();
 }
 
+bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) {
+    // FIXME: Cache the index from onGetScaledDimensions?
+    for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) {
+        if (fEmbeddedCodecs->operator[](i)->getInfo().dimensions() == dim) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 /*
  * Initiates the Ico decode
  */
@@ -286,6 +293,9 @@
         }
     }
 
+    // This should never be reached, since onDimensionsSupported should have rejected the
+    // dimensions sooner.
+    SkASSERT(false);
     SkCodecPrintf("Error: No matching candidate image in ico.\n");
     return result;
 }
diff --git a/src/codec/SkCodec_libico.h b/src/codec/SkCodec_libico.h
index a690cdd..a815e30 100644
--- a/src/codec/SkCodec_libico.h
+++ b/src/codec/SkCodec_libico.h
@@ -35,6 +35,8 @@
      */
     SkISize onGetScaledDimensions(float desiredScale) const override;
 
+    bool onDimensionsSupported(const SkISize&) override;
+
     /*
      * Initiates the Ico decode
      */
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 1af8658..ee8d492 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -436,7 +436,7 @@
     // Create the swizzler.  SkPngCodec retains ownership of the color table.
     const SkPMColor* colors = get_color_ptr(fColorTable.get());
     fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
-                    options.fZeroInitialized, this->getInfo()));
+                                               options.fZeroInitialized));
     if (!fSwizzler) {
         // FIXME: CreateSwizzler could fail for another reason.
         return kUnimplemented;
@@ -474,9 +474,6 @@
         // Subsets are not supported.
         return kUnimplemented;
     }
-    if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
-        return kInvalidScale;
-    }
 
     // Note that ctable and ctableCount may be modified if there is a color table
     const Result result = this->initializeSwizzler(requestedInfo, options,
@@ -593,13 +590,6 @@
             return kInvalidConversion;
         }
 
-        // Check to see if scaling was requested.
-        if (dstInfo.dimensions() != this->getInfo().dimensions()) {
-            if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
-                return kInvalidScale;   
-            }
-        }
-
         const Result result = this->initializeSwizzler(dstInfo, options, ctable,
                                                        ctableCount);
         if (result != kSuccess) {
@@ -687,16 +677,9 @@
             return kInvalidConversion;    
         }
 
-        // Check to see if scaling was requested.
-        if (dstInfo.dimensions() != this->getInfo().dimensions()) {
-            if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
-                return kInvalidScale;
-            }
-        }
-
-        const Result result = this->initializeSwizzler(dstInfo, options, ctable,
-                                                       ctableCount);
-        if (result != kSuccess) {
+        const SkCodec::Result result = this->initializeSwizzler(dstInfo, options, ctable,
+                                                                ctableCount);
+        if (result != SkCodec::kSuccess) {
             return result;
         }
 
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
index 8ef1ae2..3a16cc6 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -41,6 +41,7 @@
     // Helper to set up swizzler and color table. Also calls png_read_update_info.
     Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&,
                               SkPMColor*, int* ctableCount);
+    SkSampler* getSampler() override { return fSwizzler; }
 
     SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int, int);
 
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
index 1696dfb..d7f446b 100644
--- a/src/codec/SkCodec_wbmp.cpp
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -9,7 +9,6 @@
 #include "SkCodecPriv.h"
 #include "SkColorPriv.h"
 #include "SkColorTable.h"
-#include "SkScaledCodec.h"
 #include "SkStream.h"
 #include "SkCodec_wbmp.h"
 
@@ -79,11 +78,12 @@
         case kN32_SkColorType:
         case kRGB_565_SkColorType:
         case kGray_8_SkColorType:
-            return SkSwizzler::CreateSwizzler(SkSwizzler::kBit, ctable, info, opts.fZeroInitialized, 
-                                              this->getInfo());
+            break;
         default:
             return nullptr;
     }
+    return SkSwizzler::CreateSwizzler(SkSwizzler::kBit, ctable, info,
+                                      opts.fZeroInitialized);
 }
 
 SkCodec::Result SkWbmpCodec::readRow(uint8_t* row) {
@@ -114,9 +114,6 @@
         // Subsets are not supported.
         return kUnimplemented;
     }
-    if (info.dimensions() != this->getInfo().dimensions()) {
-        return kInvalidScale;
-    }
 
     if (!valid_alpha(info.alphaType(), this->getInfo().alphaType())) {
         return kInvalidConversion;
@@ -125,7 +122,6 @@
     // Prepare a color table if necessary
     setup_color_table(info.colorType(), ctable, ctableCount);
 
-
     // Initialize the swizzler
     SkAutoTDelete<SkSwizzler> swizzler(this->initializeSwizzler(info, ctable, options));
     if (nullptr == swizzler.get()) {
@@ -181,11 +177,6 @@
         // Subsets are not supported.
         return kUnimplemented;
     }
-    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
-        if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
-                return kInvalidScale;
-        }
-    }
 
     if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) {
         return kInvalidConversion;
diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h
index 220570b..976a5a2 100644
--- a/src/codec/SkCodec_wbmp.h
+++ b/src/codec/SkCodec_wbmp.h
@@ -33,6 +33,7 @@
      */
     SkSwizzler* initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
                                    const Options& opts);
+    SkSampler* getSampler() override { return fSwizzler; }
 
     /*
      * Read a src row from the encoded stream
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index b4c794c..c9dee9a 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -155,14 +155,28 @@
     return dinfo->output_width * colorBytes;
 
 }
+
+/*
+ *  Calculate output dimensions based on the provided factors.
+ *
+ *  Not to be used on the actual jpeg_decompress_struct used for decoding, since it will
+ *  incorrectly modify num_components.
+ */
+void calc_output_dimensions(jpeg_decompress_struct* dinfo, unsigned int num, unsigned int denom) {
+    dinfo->num_components = 0;
+    dinfo->scale_num = num;
+    dinfo->scale_denom = denom;
+    jpeg_calc_output_dimensions(dinfo);
+}
+
 /*
  * Return a valid set of output dimensions for this decoder, given an input scale
  */
 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
     // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will
     // support these as well
-    long num;
-    long denom = 8;
+    unsigned int num;
+    unsigned int denom = 8;
     if (desiredScale > 0.875f) {
         num = 8;
     } else if (desiredScale > 0.75f) {
@@ -187,10 +201,7 @@
     dinfo.image_width = this->getInfo().width();
     dinfo.image_height = this->getInfo().height();
     dinfo.global_state = fReadyState;
-    dinfo.num_components = 0;
-    dinfo.scale_num = num;
-    dinfo.scale_denom = denom;
-    jpeg_calc_output_dimensions(&dinfo);
+    calc_output_dimensions(&dinfo, num, denom);
 
     // Return the calculated output dimensions for the given scale
     return SkISize::Make(dinfo.output_width, dinfo.output_height);
@@ -272,28 +283,40 @@
  * Checks if we can natively scale to the requested dimensions and natively scales the 
  * dimensions if possible
  */
-bool SkJpegCodec::nativelyScaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) {
+bool SkJpegCodec::onDimensionsSupported(const SkISize& size) {
+    if (setjmp(fDecoderMgr->getJmpBuf())) {
+        return fDecoderMgr->returnFalse("onDimensionsSupported/setjmp");
+    }
+
+    const unsigned int dstWidth = size.width();
+    const unsigned int dstHeight = size.height();
+
+    // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
+    // FIXME: Why is this necessary?
+    jpeg_decompress_struct dinfo;
+    sk_bzero(&dinfo, sizeof(dinfo));
+    dinfo.image_width = this->getInfo().width();
+    dinfo.image_height = this->getInfo().height();
+    dinfo.global_state = fReadyState;
+
     // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1
-    fDecoderMgr->dinfo()->scale_denom = 8;
-    fDecoderMgr->dinfo()->scale_num = 8;
-    jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
-    while (fDecoderMgr->dinfo()->output_width != dstWidth ||
-            fDecoderMgr->dinfo()->output_height != dstHeight) {
+    unsigned int num = 8;
+    const unsigned int denom = 8;
+    calc_output_dimensions(&dinfo, num, denom);
+    while (dinfo.output_width != dstWidth || dinfo.output_height != dstHeight) {
 
         // Return a failure if we have tried all of the possible scales
-        if (1 == fDecoderMgr->dinfo()->scale_num ||
-                dstWidth > fDecoderMgr->dinfo()->output_width ||
-                dstHeight > fDecoderMgr->dinfo()->output_height) {
-            // reset native scale settings on failure because this may be supported by the swizzler 
-            this->fDecoderMgr->dinfo()->scale_num = 8;
-            jpeg_calc_output_dimensions(this->fDecoderMgr->dinfo());
+        if (1 == num || dstWidth > dinfo.output_width || dstHeight > dinfo.output_height) {
             return false;
         }
 
         // Try the next scale
-        fDecoderMgr->dinfo()->scale_num -= 1;
-        jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
+        num -= 1;
+        calc_output_dimensions(&dinfo, num, denom);
     }
+
+    fDecoderMgr->dinfo()->scale_num = num;
+    fDecoderMgr->dinfo()->scale_denom = denom;
     return true;
 }
 
@@ -321,11 +344,6 @@
         return fDecoderMgr->returnFailure("conversion_possible", kInvalidConversion);
     }
 
-    // Perform the necessary scaling
-    if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
-        return fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale);
-    }
-
     // Now, given valid output dimensions, we can start the decompress
     if (!jpeg_start_decompress(dinfo)) {
         return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
@@ -376,7 +394,13 @@
     return kSuccess;
 }
 
-SkCodec::Result SkJpegCodec::initializeSwizzler(const SkImageInfo& info, const Options& options) {
+SkSampler* SkJpegCodec::getSampler() {
+    if (fSwizzler) {
+        SkASSERT(fSrcRow && static_cast<uint8_t*>(fStorage.get()) == fSrcRow);
+        return fSwizzler;
+    }
+
+    const SkImageInfo& info = this->dstInfo();
     SkSwizzler::SrcConfig srcConfig;
     switch (info.colorType()) {
         case kGray_8_SkColorType:
@@ -396,13 +420,15 @@
             SkASSERT(false);
     }
 
-    fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info, options.fZeroInitialized, 
-                                               this->getInfo()));
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info,
+                                               this->options().fZeroInitialized));
     if (!fSwizzler) {
-        return SkCodec::kUnimplemented;
+        return nullptr;
     }
 
-    return kSuccess;
+    fStorage.reset(get_row_bytes(fDecoderMgr->dinfo()));
+    fSrcRow = static_cast<uint8_t*>(fStorage.get());
+    return fSwizzler;
 }
 
 SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
@@ -418,25 +444,10 @@
         return kInvalidConversion;
     }
 
-    // Perform the necessary scaling
-    if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
-        // full native scaling to dstInfo dimensions not supported
-
-        if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
-            return kInvalidScale;
-        }
-        // create swizzler for sampling
-        Result result = this->initializeSwizzler(dstInfo, options);
-        if (kSuccess != result) {
-            SkCodecPrintf("failed to initialize the swizzler.\n");
-            return result;
-        }
-        fStorage.reset(get_row_bytes(fDecoderMgr->dinfo()));
-        fSrcRow = static_cast<uint8_t*>(fStorage.get());
-    } else {
-        fSrcRow = nullptr;
-        fSwizzler.reset(nullptr);
-    }
+    // Remove objects used for sampling.
+    fSwizzler.reset(nullptr);
+    fSrcRow = nullptr;
+    fStorage.free();
 
     // Now, given valid output dimensions, we can start the decompress
     if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index b863963..24594c1 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -58,6 +58,8 @@
 
     bool onRewind() override;
 
+    bool onDimensionsSupported(const SkISize&) override;
+
 private:
 
     /*
@@ -102,14 +104,8 @@
      */
     bool setOutputColorSpace(const SkImageInfo& dst);
 
-    /*
-     * Checks if we can natively scale to the requested dimensions and natively scales the 
-     * dimensions if possible
-     */
-    bool nativelyScaleToDimensions(uint32_t width, uint32_t height); 
-
     // scanline decoding
-    Result initializeSwizzler(const SkImageInfo&, const SkCodec::Options&);
+    SkSampler* getSampler() override;
     Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
                    SkPMColor ctable[], int* ctableCount) override;
     Result onGetScanlines(void* dst, int count, size_t rowBytes) override;
diff --git a/src/codec/SkMaskSwizzler.cpp b/src/codec/SkMaskSwizzler.cpp
index 6ca9b58..9772d87 100644
--- a/src/codec/SkMaskSwizzler.cpp
+++ b/src/codec/SkMaskSwizzler.cpp
@@ -352,11 +352,7 @@
             return nullptr;
     }
 
-    // Get the sample size
-    int sampleX;
-    SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, &sampleX, NULL);
-
-    return new SkMaskSwizzler(dstInfo, masks, proc, sampleX);
+    return new SkMaskSwizzler(dstInfo.width(), masks, proc);
 }
 
 /*
@@ -364,15 +360,28 @@
  * Constructor for mask swizzler
  *
  */
-SkMaskSwizzler::SkMaskSwizzler(const SkImageInfo& dstInfo, SkMasks* masks,
-                               RowProc proc, uint32_t sampleX)
-    : fDstInfo(dstInfo)
-    , fMasks(masks)
+SkMaskSwizzler::SkMaskSwizzler(int width, SkMasks* masks, RowProc proc)
+    : fMasks(masks)
     , fRowProc(proc)
-    , fSampleX(sampleX)
-    , fStartX(get_start_coord(sampleX))
+    , fSrcWidth(width)
+    , fDstWidth(width)
+    , fSampleX(1)
+    , fX0(0)
 {}
 
+int SkMaskSwizzler::onSetSampleX(int sampleX) {
+    // FIXME: Share this function with SkSwizzler?
+    SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be
+                           // way to report failure?
+    fSampleX = sampleX;
+    fX0 = get_start_coord(sampleX);
+    fDstWidth = get_scaled_dimension(fSrcWidth, sampleX);
+
+    // check that fX0 is less than original width
+    SkASSERT(fX0 >= 0 && fX0 < fSrcWidth);
+    return fDstWidth;
+}
+
 /*
  *
  * Swizzle the specified row
@@ -380,5 +389,5 @@
  */
 SkSwizzler::ResultAlpha SkMaskSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) {
     SkASSERT(nullptr != dst && nullptr != src);
-    return fRowProc(dst, src, fDstInfo.width(), fMasks, fStartX, fSampleX);
+    return fRowProc(dst, src, fDstWidth, fMasks, fX0, fSampleX);
 }
diff --git a/src/codec/SkMaskSwizzler.h b/src/codec/SkMaskSwizzler.h
index 794dcd1..fbc951a 100644
--- a/src/codec/SkMaskSwizzler.h
+++ b/src/codec/SkMaskSwizzler.h
@@ -8,6 +8,7 @@
 #define SkMaskSwizzler_DEFINED
 
 #include "SkMasks.h"
+#include "SkSampler.h"
 #include "SkSwizzler.h"
 #include "SkTypes.h"
 
@@ -17,7 +18,7 @@
  * Currently only used by bmp
  *
  */
-class SkMaskSwizzler {
+class SkMaskSwizzler : public SkSampler {
 public:
 
     /*
@@ -46,15 +47,18 @@
     /*
      * Constructor for mask swizzler
      */
-    SkMaskSwizzler(const SkImageInfo& info, SkMasks* masks, RowProc proc,
-            uint32_t sampleX);
+    SkMaskSwizzler(int width, SkMasks* masks, RowProc proc);
 
-    // Fields
-    const SkImageInfo& fDstInfo;
-    SkMasks*           fMasks;       // unowned
-    const RowProc      fRowProc;
-    const uint32_t     fSampleX;
-    const uint32_t     fStartX;
+    int onSetSampleX(int) override;
+
+    SkMasks*        fMasks;           // unowned
+    const RowProc   fRowProc;
+
+    // FIXME: Can this class share more with SkSwizzler? These variables are all the same.
+    const int       fSrcWidth;        // Width of the source - i.e. before any sampling.
+    int             fDstWidth;        // Width of dst, which may differ with sampling.
+    int             fSampleX;
+    int             fX0;
 };
 
 #endif
diff --git a/src/codec/SkSampler.h b/src/codec/SkSampler.h
new file mode 100644
index 0000000..d7b4c98
--- /dev/null
+++ b/src/codec/SkSampler.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkSampler_DEFINED
+#define SkSampler_DEFINED
+
+#include "SkTypes.h"
+
+class SkSampler : public SkNoncopyable {
+public:
+    /**
+     *  Update the sampler to sample every sampleX'th pixel. Returns the
+     *  width after sampling.
+     */
+    int setSampleX(int sampleX) {
+        return this->onSetSampleX(sampleX);
+    }
+
+    virtual ~SkSampler() {}
+private:
+    virtual int onSetSampleX(int) = 0;
+};
+
+#endif // SkSampler_DEFINED
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp
index 36aeda9..078a026 100644
--- a/src/codec/SkScaledCodec.cpp
+++ b/src/codec/SkScaledCodec.cpp
@@ -100,13 +100,13 @@
 }
 
 // check if scaling to dstInfo size from srcInfo size using sampleSize is possible
-static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
+static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim,
                               int* sampleX, int* sampleY) {
-    SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, sampleX, sampleY);
-    const int dstWidth = dstInfo.width();
-    const int dstHeight = dstInfo.height();
-    const int srcWidth = srcInfo.width();
-    const int srcHeight = srcInfo.height();
+    SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY);
+    const int dstWidth = dstDim.width();
+    const int dstHeight = dstDim.height();
+    const int srcWidth = srcDim.width();
+    const int srcHeight = srcDim.height();
      // only support down sampling, not up sampling
     if (dstWidth > srcWidth || dstHeight  > srcHeight) {
         return false;
@@ -130,13 +130,29 @@
     return true;
 }
 
+bool SkScaledCodec::onDimensionsSupported(const SkISize& dim) {
+    // Check with fCodec first. No need to call the non-virtual version, which
+    // just checks if it matches the original, since a match means this method
+    // will not be called.
+    if (fCodec->onDimensionsSupported(dim)) {
+        return true;
+    }
+
+    // FIXME: These variables are unused, but are needed by scaling_supported.
+    // This class could also cache these values, and avoid calling this in
+    // onGetPixels (since getPixels already calls it).
+    int sampleX;
+    int sampleY;
+    return scaling_supported(dim, this->getInfo().dimensions(), &sampleX, &sampleY);
+}
+
 // calculates sampleSize in x and y direction
-void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
+void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim,
                                       int* sampleXPtr, int* sampleYPtr) {
-    int srcWidth = srcInfo.width();
-    int dstWidth = dstInfo.width();
-    int srcHeight = srcInfo.height();
-    int dstHeight = dstInfo.height();
+    int srcWidth = srcDim.width();
+    int dstWidth = dstDim.width();
+    int srcHeight = srcDim.height();
+    int dstHeight = dstDim.height();
 
     int sampleX = srcWidth / dstWidth;
     int sampleY = srcHeight / dstHeight;
@@ -183,62 +199,46 @@
     if (options.fSubset) {
         // Subsets are not supported.
         return kUnimplemented;
-    } 
-
-    // FIXME: If no scaling/subsets are requested, we can call fCodec->getPixels.
-    Result result = fCodec->startScanlineDecode(requestedInfo, &options, ctable, ctableCount);
-    if (kSuccess == result) {
-        // native decode supported
-        switch (fCodec->getScanlineOrder()) {
-            case SkCodec::kTopDown_SkScanlineOrder:
-            case SkCodec::kBottomUp_SkScanlineOrder:
-            case SkCodec::kNone_SkScanlineOrder:
-                return fCodec->getScanlines(dst, requestedInfo.height(), rowBytes);
-            case SkCodec::kOutOfOrder_SkScanlineOrder: {
-                for (int y = 0; y < requestedInfo.height(); y++) {
-                    int dstY = fCodec->nextScanline();
-                    void* dstPtr = SkTAddOffset<void>(dst, rowBytes * dstY);
-                    result = fCodec->getScanlines(dstPtr, 1, rowBytes);
-                    // FIXME (msarett): Make the SkCodec base class take care of filling
-                    // uninitialized pixels so we can return immediately on kIncompleteInput.
-                    if (kSuccess != result && kIncompleteInput != result) {
-                        return result;
-                    }
-                }
-                return result;
-            }
-        }
     }
 
-    if (kInvalidScale != result) {
-        // no scaling requested
-        return result;
+    if (fCodec->dimensionsSupported(requestedInfo.dimensions())) {
+        return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount);
     }
 
     // scaling requested
     int sampleX;
     int sampleY;
-    if (!scaling_supported(requestedInfo, fCodec->getInfo(), &sampleX, &sampleY)) {
+    if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensions(),
+                           &sampleX, &sampleY)) {
+        // onDimensionsSupported would have returned false, meaning we should never reach here.
+        SkASSERT(false);
         return kInvalidScale;
     }
+
     // set first sample pixel in y direction
-    int Y0 = get_start_coord(sampleY);
+    const int Y0 = get_start_coord(sampleY);
 
-    int dstHeight = requestedInfo.height();
-    int srcHeight = fCodec->getInfo().height();
-    
-    SkImageInfo info = requestedInfo;
-    // use original height as codec does not support y sampling natively
-    info = info.makeWH(requestedInfo.width(), srcHeight);
+    const int dstHeight = requestedInfo.height();
+    const int srcWidth = fCodec->getInfo().width();
+    const int srcHeight = fCodec->getInfo().height();
 
-    // update codec with new info
-    // FIXME: The previous call to start returned kInvalidScale. This call may
-    // require a rewind. (skbug.com/4284)
-    result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount);
+    const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight);
+
+    Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount);
+
     if (kSuccess != result) {
         return result;
     }
 
+    SkSampler* sampler = fCodec->getSampler();
+    if (!sampler) {
+        return kUnimplemented;
+    }
+
+    if (sampler->setSampleX(sampleX) != requestedInfo.width()) {
+        return kInvalidScale;
+    }
+
     switch(fCodec->getScanlineOrder()) {
         case SkCodec::kTopDown_SkScanlineOrder: {
             result = fCodec->skipScanlines(Y0);
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index a3f1488..214655b 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -516,8 +516,7 @@
 SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
                                        const SkPMColor* ctable,
                                        const SkImageInfo& dstInfo, 
-                                       SkCodec::ZeroInitialized zeroInit, 
-                                       const SkImageInfo& srcInfo) {
+                                       SkCodec::ZeroInitialized zeroInit) {
     if (dstInfo.colorType() == kUnknown_SkColorType || kUnknown == sc) {
         return nullptr;
     }
@@ -688,29 +687,35 @@
     // Store deltaSrc in bytes if it is an even multiple, otherwise use bits
     int deltaSrc = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc);
 
-    // get sampleX based on srcInfo and dstInfo dimensions
-    int sampleX;
-    SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, &sampleX, nullptr);
-
-    return new SkSwizzler(proc, ctable, deltaSrc, dstInfo, sampleX);
+    return new SkSwizzler(proc, ctable, deltaSrc, dstInfo.width());
 }
 
 SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable,
-                       int deltaSrc, const SkImageInfo& info, int sampleX)
+                       int deltaSrc, int srcWidth)
     : fRowProc(proc)
     , fColorTable(ctable)
     , fDeltaSrc(deltaSrc)
-    , fDstInfo(info)
-    , fSampleX(sampleX)
-    , fX0(get_start_coord(sampleX))
-{
+    , fSrcWidth(srcWidth)
+    , fDstWidth(srcWidth)
+    , fSampleX(1)
+    , fX0(0)
+{}
+
+int SkSwizzler::onSetSampleX(int sampleX) {
+    SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be
+                           // way to report failure?
+    fSampleX = sampleX;
+    fX0 = get_start_coord(sampleX);
+    fDstWidth = get_scaled_dimension(fSrcWidth, sampleX);
+
     // check that fX0 is less than original width
-    SkASSERT(fX0 >= 0 && fX0 < fDstInfo.width() * fSampleX);
+    SkASSERT(fX0 >= 0 && fX0 < fSrcWidth);
+    return fDstWidth;
 }
 
 SkSwizzler::ResultAlpha SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) {
     SkASSERT(nullptr != dst && nullptr != src);
-    return fRowProc(dst, src, fDstInfo.width(), fDeltaSrc, fSampleX * fDeltaSrc,
+    return fRowProc(dst, src, fDstWidth, fDeltaSrc, fSampleX * fDeltaSrc,
             fX0 * fDeltaSrc, fColorTable);
 }
 
diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h
index a7f29b0..9bffccb 100644
--- a/src/codec/SkSwizzler.h
+++ b/src/codec/SkSwizzler.h
@@ -11,8 +11,9 @@
 #include "SkCodec.h"
 #include "SkColor.h"
 #include "SkImageInfo.h"
+#include "SkSampler.h"
 
-class SkSwizzler : public SkNoncopyable {
+class SkSwizzler : public SkSampler {
 public:
     /**
      *  Enum describing the config of the source data.
@@ -117,22 +118,17 @@
     /**
      *  Create a new SkSwizzler.
      *  @param SrcConfig Description of the format of the source.
-     *  @param dstInfo describes the destination.
+     *  @param ctable Unowned pointer to an array of up to 256 colors for an
+     *                index source.
+     *  @param dstInfo Describes the destination.
      *  @param ZeroInitialized Whether dst is zero-initialized. The
-                               implementation may choose to skip writing zeroes
+     *                         implementation may choose to skip writing zeroes
      *                         if set to kYes_ZeroInitialized.
-     *  @param srcInfo is the info of the source. Used to calculate the width samplesize.
-     *                  Width sampling is supported by the swizzler, by skipping pixels when 
-                        swizzling the row. Height sampling is not supported by the swizzler, 
-                        but is implemented in SkScaledCodec.
-                        Sampling in Y can be done by a client with a scanline decoder, 
-                        but sampling in X allows the swizzler to skip swizzling pixels and
-                        reading from and writing to memory.
      *  @return A new SkSwizzler or nullptr on failure.
      */
     static SkSwizzler* CreateSwizzler(SrcConfig, const SkPMColor* ctable,
-                                      const SkImageInfo& dstInfo, SkCodec::ZeroInitialized, 
-                                      const SkImageInfo& srcInfo);
+                                      const SkImageInfo& dstInfo, SkCodec::ZeroInitialized);
+
     /**
      * Fill the remainder of the destination with a single color
      *
@@ -210,12 +206,13 @@
                                           //     deltaSrc is bytesPerPixel
                                           // else
                                           //     deltaSrc is bitsPerPixel
-    const SkImageInfo   fDstInfo;
-    int                 fCurrY;
-    const int           fX0;              // first X coord to sample
-    const int           fSampleX;         // step between X samples
+    const int           fSrcWidth;        // Width of the source - i.e. before any sampling.
+    int                 fDstWidth;        // Width of dst, which may differ with sampling.
+    int                 fX0;              // first X coord to sample
+    int                 fSampleX;         // step between X samples
 
-    SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc, const SkImageInfo& info, 
-               int sampleX);
+    SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc, int srcWidth);
+
+    int onSetSampleX(int) override;
 };
 #endif // SkSwizzler_DEFINED
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index 5970bae..ccffda9 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -114,6 +114,13 @@
     return dim;
 }
 
+bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) {
+    const SkImageInfo& info = this->getInfo();
+    return dim.width() >= 1 && dim.width() <= info.width()
+            && dim.height() >= 1 && dim.height() <= info.height();
+}
+
+
 static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
     switch (ct) {
         case kBGRA_8888_SkColorType:
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index 1fd3acb..d60aced 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -31,6 +31,8 @@
 
     SkISize onGetScaledDimensions(float desiredScale) const override;
 
+    bool onDimensionsSupported(const SkISize&) override;
+
     bool onGetValidSubset(SkIRect* /* desiredSubset */) const override;
 private:
     SkWebpCodec(const SkImageInfo&, SkStream*);