Add subsetting to SkScanlineDecoder

This CL allows the SkScanlineDecoder to decode partial
scanlines.

This is a first step in efficiently implementing subsetting
in SkScaledCodec.

BUG=skia:4209

Review URL: https://codereview.chromium.org/1390213002
diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp
index b4e92e4..47a897f 100644
--- a/bench/subset/SubsetSingleBench.cpp
+++ b/bench/subset/SubsetSingleBench.cpp
@@ -65,56 +65,29 @@
     if (fUseCodec) {
         for (int count = 0; count < n; count++) {
             SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
+            SkASSERT(SkCodec::kOutOfOrder_SkScanlineOrder != codec->getScanlineOrder());
             const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
 
+            // The scanline decoder will handle subsetting in the x-dimension.
+            SkIRect subset = SkIRect::MakeXYWH(fOffsetLeft, 0, fSubsetWidth,
+                    codec->getInfo().height());
+            SkCodec::Options options;
+            options.fSubset = &subset;
+
             SkDEBUGCODE(SkCodec::Result result =)
-            codec->startScanlineDecode(info, nullptr, colors, &colorCount);
+            codec->startScanlineDecode(info, &options, colors, &colorCount);
             SkASSERT(result == SkCodec::kSuccess);
 
             SkBitmap bitmap;
             SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight);
             alloc_pixels(&bitmap, subsetInfo, colors, colorCount);
 
-            SkDEBUGCODE(int lines = ) codec->skipScanlines(fOffsetTop);
-            SkASSERT((uint32_t) lines == fOffsetTop);
+            SkDEBUGCODE(bool success = ) codec->skipScanlines(fOffsetTop);
+            SkASSERT(success);
 
-            const uint32_t bpp = info.bytesPerPixel();
-
-            switch (codec->getScanlineOrder()) {
-                case SkCodec::kTopDown_SkScanlineOrder: {
-                    SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]);
-                    for (uint32_t y = 0; y < fSubsetHeight; y++) {
-                        SkDEBUGCODE(lines = ) codec->getScanlines(row.get(), 1, 0);
-                        SkASSERT(lines == 1);
-
-                        memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp,
-                                fSubsetWidth * bpp);
-                    }
-                    break;
-                }
-                case SkCodec::kNone_SkScanlineOrder: {
-                    // decode all scanlines that intersect the subset, and copy the subset
-                    // into the output.
-                    SkImageInfo stripeInfo = info.makeWH(info.width(), fSubsetHeight);
-                    SkBitmap stripeBm;
-                    alloc_pixels(&stripeBm, stripeInfo, colors, colorCount);
-
-                    SkDEBUGCODE(lines = ) codec->getScanlines(stripeBm.getPixels(), fSubsetHeight,
-                                                               stripeBm.rowBytes());
-                    SkASSERT((uint32_t) lines == fSubsetHeight);
-
-                    for (uint32_t y = 0; y < fSubsetHeight; y++) {
-                        memcpy(bitmap.getAddr(0, y), stripeBm.getAddr(fOffsetLeft, y),
-                                fSubsetWidth * bpp);
-                    }
-                    break;
-                }
-                default:
-                    // We currently are only testing kTopDown and kNone, which are the only
-                    // two used by the subsets we care about. skbug.com/4428
-                    SkASSERT(false);
-
-            }
+            SkDEBUGCODE(uint32_t lines = ) codec->getScanlines(bitmap.getPixels(), fSubsetHeight,
+                    bitmap.rowBytes());
+            SkASSERT(lines == fSubsetHeight);
         }
     } else {
         for (int count = 0; count < n; count++) {
diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp
index 27c628e..d8da6db 100644
--- a/bench/subset/SubsetTranslateBench.cpp
+++ b/bench/subset/SubsetTranslateBench.cpp
@@ -71,11 +71,8 @@
     if (fUseCodec) {
         for (int count = 0; count < n; count++) {
             SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
+            SkASSERT(SkCodec::kOutOfOrder_SkScanlineOrder != codec->getScanlineOrder());
             const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
-            SkAutoTDeleteArray<uint8_t> row(nullptr);
-            if (codec->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder) {
-                row.reset(new uint8_t[info.minRowBytes()]);
-            }
 
             SkBitmap bitmap;
             // Note that we use the same bitmap for all of the subsets.
@@ -83,17 +80,8 @@
             SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight);
             alloc_pixels(&bitmap, subsetInfo, colors, colorCount);
 
-            const uint32_t bpp = info.bytesPerPixel();
-
             for (int x = 0; x < info.width(); x += fSubsetWidth) {
                 for (int y = 0; y < info.height(); y += fSubsetHeight) {
-                    SkDEBUGCODE(SkCodec::Result result =)
-                    codec->startScanlineDecode(info, nullptr, get_colors(&bitmap), &colorCount);
-                    SkASSERT(SkCodec::kSuccess == result);
-
-                    SkDEBUGCODE(int lines =) codec->skipScanlines(y);
-                    SkASSERT(y == lines);
-
                     const uint32_t currSubsetWidth =
                             x + (int) fSubsetWidth > info.width() ?
                             info.width() - x : fSubsetWidth;
@@ -101,38 +89,22 @@
                             y + (int) fSubsetHeight > info.height() ?
                             info.height() - y : fSubsetHeight;
 
-                    switch (codec->getScanlineOrder()) {
-                        case SkCodec::kTopDown_SkScanlineOrder:
-                            for (uint32_t y = 0; y < currSubsetHeight; y++) {
-                                SkDEBUGCODE(lines =) codec->getScanlines(row.get(), 1, 0);
-                                SkASSERT(1 == lines);
+                    // The scanline decoder will handle subsetting in the x-dimension.
+                    SkIRect subset = SkIRect::MakeXYWH(x, 0, currSubsetWidth,
+                            codec->getInfo().height());
+                    SkCodec::Options options;
+                    options.fSubset = &subset;
 
-                                memcpy(bitmap.getAddr(0, y), row.get() + x * bpp,
-                                        currSubsetWidth * bpp);
-                            }
-                            break;
-                        case SkCodec::kNone_SkScanlineOrder: {
-                            // decode all scanlines that intersect the subset, and copy the subset
-                            // into the output.
-                            SkImageInfo stripeInfo = info.makeWH(info.width(), currSubsetHeight);
-                            SkBitmap stripeBm;
-                            alloc_pixels(&stripeBm, stripeInfo, colors, colorCount);
+                    SkDEBUGCODE(SkCodec::Result result =)
+                    codec->startScanlineDecode(info, &options, get_colors(&bitmap), &colorCount);
+                    SkASSERT(SkCodec::kSuccess == result);
 
-                            SkDEBUGCODE(lines =) codec->getScanlines(stripeBm.getPixels(),
-                                    currSubsetHeight, stripeBm.rowBytes());
-                            SkASSERT(currSubsetHeight == (uint32_t) lines);
+                    SkDEBUGCODE(bool success =) codec->skipScanlines(y);
+                    SkASSERT(success);
 
-                            for (uint32_t subsetY = 0; subsetY < currSubsetHeight; subsetY++) {
-                                memcpy(bitmap.getAddr(0, subsetY), stripeBm.getAddr(x, subsetY),
-                                        currSubsetWidth * bpp);
-                            }
-                            break;
-                        }
-                        default:
-                            // We currently are only testing kTopDown and kNone, which are the only
-                            // two used by the subsets we care about. skbug.com/4428
-                            SkASSERT(false);
-                    }
+                    SkDEBUGCODE(uint32_t lines =) codec->getScanlines(bitmap.getPixels(),
+                            currSubsetHeight, bitmap.rowBytes());
+                    SkASSERT(currSubsetHeight == lines);
                 }
             }
         }
diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp
index 3edc17f..f17a97c 100644
--- a/bench/subset/SubsetZoomBench.cpp
+++ b/bench/subset/SubsetZoomBench.cpp
@@ -61,69 +61,41 @@
     if (fUseCodec) {
         for (int count = 0; count < n; count++) {
             SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
+            SkASSERT(SkCodec::kOutOfOrder_SkScanlineOrder != codec->getScanlineOrder());
             const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
-            SkAutoTDeleteArray<uint8_t> row(nullptr);
-            if (codec->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder) {
-                row.reset(new uint8_t[info.minRowBytes()]);
-            }
 
             const int centerX = info.width() / 2;
             const int centerY = info.height() / 2;
             int w = fSubsetWidth;
             int h = fSubsetHeight;
             do {
-                SkDEBUGCODE(SkCodec::Result result = )
-                codec->startScanlineDecode(info, nullptr, colors, &colorCount);
-                SkASSERT(SkCodec::kSuccess == result);
-
                 const int subsetStartX = SkTMax(0, centerX - w / 2);
                 const int subsetStartY = SkTMax(0, centerY - h / 2);
                 const int subsetWidth = SkTMin(w, info.width() - subsetStartX);
                 const int subsetHeight = SkTMin(h, info.height() - subsetStartY);
+
+                // The scanline decoder will handle subsetting in the x-dimension.
+                SkIRect subset = SkIRect::MakeXYWH(subsetStartX, 0, subsetWidth,
+                        codec->getInfo().height());
+                SkCodec::Options options;
+                options.fSubset = &subset;
+
+                SkDEBUGCODE(SkCodec::Result result = )
+                codec->startScanlineDecode(info, &options, colors, &colorCount);
+                SkASSERT(SkCodec::kSuccess == result);
+
                 // Note that if we subsetted and scaled in a single step, we could use the
                 // same bitmap - as is often done in actual use cases.
                 SkBitmap bitmap;
                 SkImageInfo subsetInfo = info.makeWH(subsetWidth, subsetHeight);
                 alloc_pixels(&bitmap, subsetInfo, colors, colorCount);
 
-                uint32_t bpp = info.bytesPerPixel();
+                SkDEBUGCODE(bool success = ) codec->skipScanlines(subsetStartY);
+                SkASSERT(success);
 
-                SkDEBUGCODE(int lines = ) codec->skipScanlines(subsetStartY);
-                SkASSERT(subsetStartY == lines);
-
-                switch (codec->getScanlineOrder()) {
-                    case SkCodec::kTopDown_SkScanlineOrder:
-                        for (int y = 0; y < subsetHeight; y++) {
-                            SkDEBUGCODE(lines = ) codec->getScanlines(row.get(), 1, 0);
-                            SkASSERT(1 == lines);
-
-                            memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp,
-                                    subsetWidth * bpp);
-                        }
-                        break;
-                    case SkCodec::kNone_SkScanlineOrder: {
-                        // decode all scanlines that intersect the subset, and copy the subset
-                        // into the output.
-                        SkImageInfo stripeInfo = info.makeWH(info.width(), subsetHeight);
-                        SkBitmap stripeBm;
-                        alloc_pixels(&stripeBm, stripeInfo, colors, colorCount);
-
-                        SkDEBUGCODE(lines = ) codec->getScanlines(stripeBm.getPixels(),
-                                subsetHeight, stripeBm.rowBytes());
-                        SkASSERT(subsetHeight == lines);
-
-                        for (int y = 0; y < subsetHeight; y++) {
-                            memcpy(bitmap.getAddr(0, y),
-                                   stripeBm.getAddr(subsetStartX, y),
-                                   subsetWidth * bpp);
-                        }
-                        break;
-                    }
-                    default:
-                        // We currently are only testing kTopDown and kNone, which are the only
-                        // two used by the subsets we care about. skbug.com/4428
-                        SkASSERT(false);
-                }
+                SkDEBUGCODE(int lines = ) codec->getScanlines(bitmap.getPixels(),
+                        subsetHeight, bitmap.rowBytes());
+                SkASSERT(subsetHeight == lines);
 
                 w <<= 1;
                 h <<= 1;
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index c7b389d..4137154 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -404,9 +404,6 @@
                 return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
                         largestSubsetDecodeInfo.width(), largestSubsetDecodeInfo.height());
             }
-            const size_t rowBytes = decodeInfo.minRowBytes();
-            char* buffer = new char[largestSubsetDecodeInfo.height() * rowBytes];
-            SkAutoTDeleteArray<char> lineDeleter(buffer);
             for (int col = 0; col < divisor; col++) {
                 //currentSubsetWidth may be larger than subsetWidth for rightmost subsets
                 const int currentSubsetWidth = (col + 1 == divisor) ?
@@ -418,10 +415,13 @@
                             subsetHeight + extraY : subsetHeight;
                     const int y = row * subsetHeight;
                     //create scanline decoder for each subset
-                    if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
-                                                                        colorCountPtr)
-                            // TODO (msarett): Support this mode for all scanline orderings.
-                            || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
+                    SkCodec::Options options;
+                    SkIRect subset = SkIRect::MakeXYWH(x, 0, currentSubsetWidth, h);
+                    options.fSubset = &subset;
+                    // TODO (msarett): Support this mode for all scanline orderings.
+                    if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options,
+                            colorPtr, colorCountPtr) ||
+                            SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
                         if (x == 0 && y == 0) {
                             //first try, image may not be compatible
                             return Error::Nonfatal("Could not start top-down scanline decoder");
@@ -440,17 +440,9 @@
                     SkBitmap subsetBm;
                     SkIRect bounds = SkIRect::MakeWH(currentSubsetWidth, currentSubsetHeight);
                     SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds));
-                    SkAutoLockPixels autlockSubsetBm(subsetBm, true);
-                    codec->getScanlines(buffer, currentSubsetHeight, rowBytes);
-
-                    const size_t bpp = decodeInfo.bytesPerPixel();
-                    char* bufferRow = buffer;
-                    for (int subsetY = 0; subsetY < currentSubsetHeight; ++subsetY) {
-                        memcpy(subsetBm.getAddr(0, subsetY), bufferRow + x*bpp,
-                                currentSubsetWidth*bpp);
-                        bufferRow += rowBytes;
-                    }
-
+                    SkAutoLockPixels autolock(subsetBm, true);
+                    codec->getScanlines(subsetBm.getAddr(0, 0), currentSubsetHeight,
+                            subsetBm.rowBytes());
                     subsetBm.notifyPixelsChanged();
                     canvas->drawBitmap(subsetBm, SkIntToScalar(x), SkIntToScalar(y));
                 }
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index 1b9cb0f..8b6e210 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -168,11 +168,20 @@
         ZeroInitialized fZeroInitialized;
         /**
          *  If not NULL, represents a subset of the original image to decode.
-         *
          *  Must be within the bounds returned by getInfo().
-         *
          *  If the EncodedFormat is kWEBP_SkEncodedFormat (the only one which
          *  currently supports subsets), the top and left values must be even.
+         *
+         *  In getPixels, we will attempt to decode the exact rectangular
+         *  subset specified by fSubset.
+         *
+         *  In a scanline decode, it does not make sense to specify a subset
+         *  top or subset height, since the client already controls which rows
+         *  to get and which rows to skip.  During scanline decodes, we will
+         *  require that the subset top be zero and the subset height be equal
+         *  to the full height.  We will, however, use the values of
+         *  subset left and subset width to decode partial scanlines on calls
+         *  to getScanlines().
          */
         SkIRect*        fSubset;
     };
@@ -259,7 +268,7 @@
      *  @return Enum representing success or reason for failure.
      */
     Result startScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options* options,
-                 SkPMColor ctable[], int* ctableCount);
+            SkPMColor ctable[], int* ctableCount);
 
     /**
      *  Simplified version of startScanlineDecode() that asserts that info is NOT
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 1aa43f5..0222c8c 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -564,10 +564,6 @@
 
 SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
-    if (options.fSubset) {
-        // Subsets are not supported.
-        return kUnimplemented;
-    }
     if (!conversion_possible(dstInfo, this->getInfo())) {
         SkCodecPrintf("Error: cannot convert input type to output type.\n");
         return kInvalidConversion;
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index 336698d..2bc47b5 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -58,10 +58,10 @@
     return kSuccess;
 }
 
-bool SkBmpMaskCodec::initializeSwizzler(const SkImageInfo& dstInfo) {
+bool SkBmpMaskCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
     // Create the swizzler
-    fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(
-            dstInfo, this->getInfo(), fMasks, this->bitsPerPixel()));
+    fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(dstInfo, this->getInfo(), fMasks,
+            this->bitsPerPixel(), options));
 
     if (nullptr == fMaskSwizzler.get()) {
         return false;
@@ -73,7 +73,7 @@
 SkCodec::Result SkBmpMaskCodec::prepareToDecode(const SkImageInfo& dstInfo,
         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
     // Initialize a the mask swizzler
-    if (!this->initializeSwizzler(dstInfo)) {
+    if (!this->initializeSwizzler(dstInfo, options)) {
         SkCodecPrintf("Error: cannot initialize swizzler.\n");
         return SkCodec::kInvalidConversion;
     }
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 1c1d1d8..4ec868d 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -44,7 +44,7 @@
 
 private:
 
-    bool initializeSwizzler(const SkImageInfo& dstInfo);
+    bool initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
     SkSampler* getSampler(bool createIfNecessary) override {
         SkASSERT(fMaskSwizzler);
         return fMaskSwizzler;
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index 37af476..e215095 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -259,6 +259,12 @@
 
 SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo,
         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
+    // FIXME: Support subsets for scanline decodes.
+    if (options.fSubset) {
+        // Subsets are not supported.
+        return kUnimplemented;
+    }
+
     // Reset fSampleX. If it needs to be a value other than 1, it will get modified by
     // the sampler.
     fSampleX = 1;
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index 938fe8c..9557609 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -161,8 +161,7 @@
     return true;
 }
 
-bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo,
-                                            const Options& opts) {
+bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
     // Get swizzler configuration
     SkSwizzler::SrcConfig config;
     switch (this->bitsPerPixel()) {
@@ -197,8 +196,7 @@
     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
 
     // Create swizzler
-    fSwizzler.reset(SkSwizzler::CreateSwizzler(config,
-            colorPtr, dstInfo, opts.fZeroInitialized));
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(config, colorPtr, dstInfo, opts));
 
     if (nullptr == fSwizzler.get()) {
         return false;
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 0047d59..56f6a8d 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -220,11 +220,15 @@
     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;
+        SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
+        if (!size.contains(*options->fSubset)) {
+            return kInvalidInput;
+        }
+
+        // We only support subsetting in the x-dimension for scanline decoder.
+        // Subsetting in the y-dimension can be accomplished using skipScanlines().
+        if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
+            return kInvalidInput;
         }
     }
 
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp
index 856f69b..0187891 100644
--- a/src/codec/SkCodec_libgif.cpp
+++ b/src/codec/SkCodec_libgif.cpp
@@ -435,10 +435,6 @@
 SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
         int* inputColorCount, const Options& opts) {
     // Check for valid input parameters
-    if (opts.fSubset) {
-        // Subsets are not supported.
-        return kUnimplemented;
-    }
     if (!conversion_possible(dstInfo, this->getInfo())) {
         return gif_error("Cannot convert input type to output type.\n",
                 kInvalidConversion);
@@ -449,11 +445,9 @@
     return kSuccess;
 }
 
-SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo,
-        ZeroInitialized zeroInit) {
+SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
-    fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex,
-            colorPtr, dstInfo, zeroInit));
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts));
     if (nullptr != fSwizzler.get()) {
         return kSuccess;
     }
@@ -485,7 +479,7 @@
     // Initialize the swizzler
     if (fFrameIsSubset) {
         const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height());
-        if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) {
+        if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) {
             return gif_error("Could not initialize swizzler.\n", kUnimplemented);
         }
 
@@ -499,7 +493,7 @@
         dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameRect.top() +
                 dstBytesPerPixel * fFrameRect.left());
     } else {
-        if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized)) {
+        if (kSuccess != this->initializeSwizzler(dstInfo, opts)) {
             return gif_error("Could not initialize swizzler.\n", kUnimplemented);
         }
     }
@@ -535,11 +529,11 @@
     // Initialize the swizzler
     if (fFrameIsSubset) {
         const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height());
-        if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) {
+        if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) {
             return gif_error("Could not initialize swizzler.\n", kUnimplemented);
         }
     } else {
-        if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized)) {
+        if (kSuccess != this->initializeSwizzler(dstInfo, opts)) {
             return gif_error("Could not initialize swizzler.\n", kUnimplemented);
         }
     }
diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h
index 10fdac9..2e0c5a4 100644
--- a/src/codec/SkCodec_libgif.h
+++ b/src/codec/SkCodec_libgif.h
@@ -129,9 +129,11 @@
      * @param dstInfo  Output image information.  Dimensions may have been
      *                 adjusted if the image frame size does not match the size
      *                 indicated in the header.
-     * @param zeroInit Indicates if destination memory is zero initialized.
+     * @param options  Informs the swizzler if destination memory is zero initialized.
+     *                 Contains subset information.
      */
-    Result initializeSwizzler(const SkImageInfo& dstInfo, ZeroInitialized zeroInit);
+    Result initializeSwizzler(const SkImageInfo& dstInfo,
+            const Options& options);
 
     SkSampler* getSampler(bool createIfNecessary) override {
         SkASSERT(fSwizzler);
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index e828e24..7d41623 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -435,8 +435,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));
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, options));
     if (!fSwizzler) {
         // FIXME: CreateSwizzler could fail for another reason.
         return kUnimplemented;
@@ -477,8 +476,7 @@
     }
 
     // Note that ctable and ctableCount may be modified if there is a color table
-    const Result result = this->initializeSwizzler(requestedInfo, options,
-                                                   ctable, ctableCount);
+    const Result result = this->initializeSwizzler(requestedInfo, options, ctable, ctableCount);
     if (result != kSuccess) {
         return result;
     }
@@ -699,15 +697,14 @@
     }
 
     Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
-            SkPMColor ctable[], int* ctableCount) override
-    {
+            SkPMColor ctable[], int* ctableCount) override {
         if (!conversion_possible(dstInfo, this->getInfo())) {
             return kInvalidConversion;    
         }
 
-        const SkCodec::Result result = this->initializeSwizzler(dstInfo, options, ctable,
-                                                                ctableCount);
-        if (result != SkCodec::kSuccess) {
+        const Result result = this->initializeSwizzler(dstInfo, options, ctable,
+                                                       ctableCount);
+        if (result != kSuccess) {
             return result;
         }
 
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
index 6bdf580..f003cbd 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -18,7 +18,6 @@
 #endif
 #include "png.h"
 
-class SkScanlineDecoder;
 class SkStream;
 
 class SkPngCodec : public SkCodec {
@@ -27,7 +26,6 @@
 
     // Assume IsPng was called and returned true.
     static SkCodec* NewFromStream(SkStream*);
-    static SkScanlineDecoder* NewSDFromStream(SkStream*);
 
     virtual ~SkPngCodec();
 
@@ -78,7 +76,6 @@
     SkSwizzler::SrcConfig       fSrcConfig;
     const int                   fNumberPasses;
     int                         fBitDepth;
-
     AlphaState                  fAlphaState;
 
     bool decodePalette(bool premultiply, int* ctableCount);
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
index 14b7209..19ae4ea 100644
--- a/src/codec/SkCodec_wbmp.cpp
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -70,8 +70,8 @@
     return read_header(this->stream(), nullptr);
 }
 
-SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info,
-        const SkPMColor* ctable, const Options& opts) {
+SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
+        const Options& opts) {
     // Create the swizzler based on the desired color type
     switch (info.colorType()) {
         case kIndex_8_SkColorType:
@@ -82,8 +82,7 @@
         default:
             return nullptr;
     }
-    return SkSwizzler::CreateSwizzler(SkSwizzler::kBit, ctable, info,
-                                      opts.fZeroInitialized);
+    return SkSwizzler::CreateSwizzler(SkSwizzler::kBit, ctable, info, opts);
 }
 
 bool SkWbmpCodec::readRow(uint8_t* row) {
@@ -188,8 +187,7 @@
     }
 
     // Initialize the swizzler
-    fSwizzler.reset(this->initializeSwizzler(dstInfo,
-            get_color_ptr(fColorTable.get()), options));
+    fSwizzler.reset(this->initializeSwizzler(dstInfo, get_color_ptr(fColorTable.get()), options));
     if (nullptr == fSwizzler.get()) {
         return kInvalidConversion;
     }
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 196543b..d0d11b1 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -177,19 +177,19 @@
     // support these as well
     unsigned int num;
     unsigned int denom = 8;
-    if (desiredScale > 0.875f) {
+    if (desiredScale >= 0.9375) {
         num = 8;
-    } else if (desiredScale > 0.75f) {
+    } else if (desiredScale >= 0.8125) {
         num = 7;
-    } else if (desiredScale > 0.625f) {
+    } else if (desiredScale >= 0.6875f) {
         num = 6;
-    } else if (desiredScale > 0.5f) {
+    } else if (desiredScale >= 0.5625f) {
         num = 5;
-    } else if (desiredScale > 0.375f) {
+    } else if (desiredScale >= 0.4375f) {
         num = 4;
-    } else if (desiredScale > 0.25f) {
+    } else if (desiredScale >= 0.3125f) {
         num = 3;
-    } else if (desiredScale > 0.125f) {
+    } else if (desiredScale >= 0.1875f) {
         num = 2;
     } else {
         num = 1;
@@ -380,15 +380,9 @@
     return kSuccess;
 }
 
-SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
-    if (!createIfNecessary || fSwizzler) {
-        SkASSERT(!fSwizzler || (fSrcRow && static_cast<uint8_t*>(fStorage.get()) == fSrcRow));
-        return fSwizzler;
-    }
-
-    const SkImageInfo& info = this->dstInfo();
+void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
     SkSwizzler::SrcConfig srcConfig;
-    switch (info.colorType()) {
+    switch (dstInfo.colorType()) {
         case kGray_8_SkColorType:
             srcConfig = SkSwizzler::kGray;
             break;
@@ -406,14 +400,18 @@
             SkASSERT(false);
     }
 
-    fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info,
-                                               this->options().fZeroInitialized));
-    if (!fSwizzler) {
-        return nullptr;
-    }
-
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, options));
     fStorage.reset(get_row_bytes(fDecoderMgr->dinfo()));
     fSrcRow = static_cast<uint8_t*>(fStorage.get());
+}
+
+SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
+    if (!createIfNecessary || fSwizzler) {
+        SkASSERT(!fSwizzler || (fSrcRow && static_cast<uint8_t*>(fStorage.get()) == fSrcRow));
+        return fSwizzler;
+    }
+
+    this->initializeSwizzler(this->dstInfo(), this->options());
     return fSwizzler;
 }
 
@@ -441,6 +439,11 @@
         return kInvalidInput;
     }
 
+    // We will need a swizzler if we are performing a subset decode
+    if (options.fSubset) {
+        this->initializeSwizzler(dstInfo, options);
+    }
+
     return kSuccess;
 }
 
@@ -452,7 +455,7 @@
     // Read rows one at a time
     JSAMPLE* dstRow;
     if (fSwizzler) {
-        // write data to storage row, then sample using swizzler         
+        // write data to storage row, then sample using swizzler
         dstRow = fSrcRow;
     } else {
         // write data directly to dst
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 67680d6..687cf4b 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -103,9 +103,10 @@
     bool setOutputColorSpace(const SkImageInfo& dst);
 
     // scanline decoding
+    void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
     SkSampler* getSampler(bool createIfNecessary) override;
     Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
-                   SkPMColor ctable[], int* ctableCount) override;
+            SkPMColor ctable[], int* ctableCount) override;
     int onGetScanlines(void* dst, int count, size_t rowBytes) override;
     bool onSkipScanlines(int count) override;
 
diff --git a/src/codec/SkMaskSwizzler.cpp b/src/codec/SkMaskSwizzler.cpp
index 9772d87..72dca28 100644
--- a/src/codec/SkMaskSwizzler.cpp
+++ b/src/codec/SkMaskSwizzler.cpp
@@ -250,9 +250,9 @@
  * Create a new mask swizzler
  *
  */
-SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(
-        const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, SkMasks* masks,
-        uint32_t bitsPerPixel) {
+SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(const SkImageInfo& dstInfo,
+        const SkImageInfo& srcInfo, SkMasks* masks, uint32_t bitsPerPixel,
+        const SkCodec::Options& options) {
 
     // Choose the appropriate row procedure
     RowProc proc = nullptr;
@@ -352,7 +352,14 @@
             return nullptr;
     }
 
-    return new SkMaskSwizzler(dstInfo.width(), masks, proc);
+    int srcOffset = 0;
+    int srcWidth = dstInfo.width();
+    if (options.fSubset) {
+        srcOffset = options.fSubset->left();
+        srcWidth = options.fSubset->width();
+    }
+
+    return new SkMaskSwizzler(masks, proc, srcOffset, srcWidth);
 }
 
 /*
@@ -360,13 +367,14 @@
  * Constructor for mask swizzler
  *
  */
-SkMaskSwizzler::SkMaskSwizzler(int width, SkMasks* masks, RowProc proc)
+SkMaskSwizzler::SkMaskSwizzler(SkMasks* masks, RowProc proc, int srcOffset, int srcWidth)
     : fMasks(masks)
     , fRowProc(proc)
-    , fSrcWidth(width)
-    , fDstWidth(width)
+    , fSrcWidth(srcWidth)
+    , fDstWidth(srcWidth)
     , fSampleX(1)
-    , fX0(0)
+    , fSrcOffset(srcOffset)
+    , fX0(srcOffset)
 {}
 
 int SkMaskSwizzler::onSetSampleX(int sampleX) {
@@ -374,7 +382,7 @@
     SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be
                            // way to report failure?
     fSampleX = sampleX;
-    fX0 = get_start_coord(sampleX);
+    fX0 = get_start_coord(sampleX) + fSrcOffset;
     fDstWidth = get_scaled_dimension(fSrcWidth, sampleX);
 
     // check that fX0 is less than original width
diff --git a/src/codec/SkMaskSwizzler.h b/src/codec/SkMaskSwizzler.h
index 0513d83..9aea5d8 100644
--- a/src/codec/SkMaskSwizzler.h
+++ b/src/codec/SkMaskSwizzler.h
@@ -28,7 +28,8 @@
     static SkMaskSwizzler* CreateMaskSwizzler(const SkImageInfo& dstInfo,
                                               const SkImageInfo& srcInfo,
                                               SkMasks* masks,
-                                              uint32_t bitsPerPixel);
+                                              uint32_t bitsPerPixel,
+                                              const SkCodec::Options& options);
 
     /*
      * Swizzle a row
@@ -49,14 +50,10 @@
     /*
      * Row procedure used for swizzle
      */
-    typedef SkSwizzler::ResultAlpha (*RowProc)(
-            void* dstRow, const uint8_t* srcRow, int width,
+    typedef SkSwizzler::ResultAlpha (*RowProc)(void* dstRow, const uint8_t* srcRow, int width,
             SkMasks* masks, uint32_t startX, uint32_t sampleX);
 
-    /*
-     * Constructor for mask swizzler
-     */
-    SkMaskSwizzler(int width, SkMasks* masks, RowProc proc);
+    SkMaskSwizzler(SkMasks* masks, RowProc proc, int srcWidth, int srcOffset);
 
     int onSetSampleX(int) override;
 
@@ -67,6 +64,7 @@
     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             fSrcOffset;
     int             fX0;
 };
 
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index 8d13e56..95ed1d7 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -230,9 +230,6 @@
     //                 SkScaledBitmap sampler just guesses that it is opaque.  This is dangerous
     //                 and probably wrong since gif and bmp (rarely) may have alpha.
     if (1 == deltaSrc) {
-        // A non-zero offset is only used when sampling, meaning that deltaSrc will be
-        // greater than 1. The below loop relies on the fact that src remains unchanged.
-        SkASSERT(0 == offset);
         memcpy(dst, src, dstWidth);
         for (int x = 0; x < dstWidth; x++) {
             UPDATE_RESULT_ALPHA(ctable[src[x]] >> SK_A32_SHIFT);
@@ -514,8 +511,8 @@
 
 SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
                                        const SkPMColor* ctable,
-                                       const SkImageInfo& dstInfo, 
-                                       SkCodec::ZeroInitialized zeroInit) {
+                                       const SkImageInfo& dstInfo,
+                                       const SkCodec::Options& options) {
     if (dstInfo.colorType() == kUnknown_SkColorType || kUnknown == sc) {
         return nullptr;
     }
@@ -524,7 +521,7 @@
         return nullptr;
     }
     RowProc proc = nullptr;
-
+    SkCodec::ZeroInitialized zeroInit = options.fZeroInitialized;
     switch (sc) {
         case kBit:
             switch (dstInfo.colorType()) {
@@ -683,28 +680,35 @@
         return nullptr;
     }
 
-    // Store deltaSrc in bytes if it is an even multiple, otherwise use bits
-    int deltaSrc = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc);
+    // Store bpp in bytes if it is an even multiple, otherwise use bits
+    int bpp = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc);
+    
+    int srcOffset = 0;
+    int srcWidth = dstInfo.width();
+    if (options.fSubset) {
+        srcOffset = options.fSubset->left();
+        srcWidth = options.fSubset->width();
+    }
 
-    return new SkSwizzler(proc, ctable, deltaSrc, dstInfo.width());
+    return new SkSwizzler(proc, ctable, srcOffset, srcWidth, bpp);
 }
 
-SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable,
-                       int deltaSrc, int srcWidth)
+SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, int bpp)
     : fRowProc(proc)
     , fColorTable(ctable)
-    , fDeltaSrc(deltaSrc)
+    , fSrcOffset(srcOffset)
+    , fX0(srcOffset)
     , fSrcWidth(srcWidth)
     , fDstWidth(srcWidth)
+    , fBPP(bpp)
     , 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);
+    fX0 = get_start_coord(sampleX) + fSrcOffset;
     fDstWidth = get_scaled_dimension(fSrcWidth, sampleX);
 
     // check that fX0 is less than original width
@@ -714,6 +718,5 @@
 
 SkSwizzler::ResultAlpha SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) {
     SkASSERT(nullptr != dst && nullptr != src);
-    return fRowProc(dst, src, fDstWidth, fDeltaSrc, fSampleX * fDeltaSrc,
-            fX0 * fDeltaSrc, fColorTable);
+    return fRowProc(dst, src, fDstWidth, fBPP, fSampleX * fBPP, fX0 * fBPP, fColorTable);
 }
diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h
index d7f6337..058044e 100644
--- a/src/codec/SkSwizzler.h
+++ b/src/codec/SkSwizzler.h
@@ -121,13 +121,14 @@
      *  @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
+     *  @param options Indicates if dst is zero-initialized. The
      *                         implementation may choose to skip writing zeroes
      *                         if set to kYes_ZeroInitialized.
+     *                 Contains subset information.
      *  @return A new SkSwizzler or nullptr on failure.
      */
     static SkSwizzler* CreateSwizzler(SrcConfig, const SkPMColor* ctable,
-                                      const SkImageInfo& dstInfo, SkCodec::ZeroInitialized);
+                                      const SkImageInfo& dstInfo, const SkCodec::Options&);
 
     /**
      *  Swizzle a line. Generally this will be called height times, once
@@ -173,16 +174,19 @@
 
     const RowProc       fRowProc;
     const SkPMColor*    fColorTable;      // Unowned pointer
-    const int           fDeltaSrc;        // if bitsPerPixel % 8 == 0
-                                          //     deltaSrc is bytesPerPixel
-                                          // else
-                                          //     deltaSrc is bitsPerPixel
+    const int           fSrcOffset;       // Offset of the src in pixels, allows for partial
+                                          // scanline decodes.
+    int                 fX0;              // Start coordinate for the src, may be different than
+                                          // fSrcOffset if we are sampling.
     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
+    const int           fBPP;             // if bitsPerPixel % 8 == 0
+                                          //     fBPP is bytesPerPixel
+                                          // else
+                                          //     fBPP is bitsPerPixel
 
-    SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc, int srcWidth);
+    SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, int bpp);
 
     int onSetSampleX(int) override;
 
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index a0fab0a..3c61b93 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -144,8 +144,8 @@
         return false;
     }
 
-    SkIRect bounds = SkIRect::MakeSize(this->getInfo().dimensions());
-    if (!desiredSubset->intersect(bounds)) {
+    SkIRect dimensions  = SkIRect::MakeSize(this->getInfo().dimensions());
+    if (!dimensions.contains(*desiredSubset)) {
         return false;
     }