Move calls to conversion_possible to SkCodec

Move common code into the base class, so subclasses need not call
conversion_possible.

Use SkEncodedInfo rather than SkImageInfo, and use the proper frame.

API Changes:
- SkAndroidCodec:
  - Add getEncodedInfo(), for SkBitmapRegionCodec
- SkEncodedInfo:
  - Add opaque() helper
- SkBitmapRegionDecoder:
  - Remove unused conversionSupported

(Split off from skia-review.googlesource.com/c/25746)

Bug: skia:5601
Change-Id: If4a40d4b98a3dd0afde2b6058f92315a393a5baf
Reviewed-on: https://skia-review.googlesource.com/34361
Reviewed-by: Mike Klein <mtklein@google.com>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/dm/DM.cpp b/dm/DM.cpp
index afc853d..37ff24d 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -529,6 +529,65 @@
     push_src("image", folder, src);
 }
 
+static void push_brd_src(Path path, CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode,
+        uint32_t sampleSize) {
+    SkString folder("brd_android_codec");
+    switch (mode) {
+        case BRDSrc::kFullImage_Mode:
+            break;
+        case BRDSrc::kDivisor_Mode:
+            folder.append("_divisor");
+            break;
+        default:
+            SkASSERT(false);
+            return;
+    }
+
+    switch (dstColorType) {
+        case CodecSrc::kGetFromCanvas_DstColorType:
+            break;
+        case CodecSrc::kGrayscale_Always_DstColorType:
+            folder.append("_kGray");
+            break;
+        default:
+            SkASSERT(false);
+            return;
+    }
+
+    if (1 != sampleSize) {
+        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
+    }
+
+    BRDSrc* src = new BRDSrc(path, mode, dstColorType, sampleSize);
+    push_src("image", folder, src);
+}
+
+static void push_brd_srcs(Path path, bool gray) {
+    if (gray) {
+        // Only run grayscale to one sampleSize and Mode. Though interesting
+        // to test grayscale, it should not reveal anything across various
+        // sampleSizes and Modes
+        // Arbitrarily choose Mode and sampleSize.
+        push_brd_src(path, CodecSrc::kGrayscale_Always_DstColorType,
+                     BRDSrc::kFullImage_Mode, 2);
+    }
+
+    // Test on a variety of sampleSizes, making sure to include:
+    // - 2, 4, and 8, which are natively supported by jpeg
+    // - multiples of 2 which are not divisible by 4 (analogous for 4)
+    // - larger powers of two, since BRD clients generally use powers of 2
+    // We will only produce output for the larger sizes on large images.
+    const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64 };
+
+    const BRDSrc::Mode modes[] = { BRDSrc::kFullImage_Mode, BRDSrc::kDivisor_Mode, };
+
+    for (uint32_t sampleSize : sampleSizes) {
+        for (BRDSrc::Mode mode : modes) {
+            push_brd_src(path, CodecSrc::kGetFromCanvas_DstColorType, mode, sampleSize);
+        }
+    }
+}
+
 static void push_codec_srcs(Path path) {
     sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
     if (!encoded) {
@@ -641,16 +700,31 @@
         }
     }
 
-    static const char* const rawExts[] = {
-        "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
-        "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",
-    };
+    const char* ext = strrchr(path.c_str(), '.');
+    if (ext) {
+        ext++;
 
-    // There is not currently a reason to test RAW images on image generator.
-    // If we want to enable these tests, we will need to fix skbug.com/5079.
-    for (const char* ext : rawExts) {
-        if (path.endsWith(ext)) {
-            return;
+        static const char* const rawExts[] = {
+            "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
+            "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",
+        };
+        for (const char* rawExt : rawExts) {
+            if (0 == strcmp(rawExt, ext)) {
+                // RAW is not supported by image generator (skbug.com/5079) or BRD.
+                return;
+            }
+        }
+
+        static const char* const brdExts[] = {
+            "jpg", "jpeg", "png", "webp",
+            "JPG", "JPEG", "PNG", "WEBP",
+        };
+        for (const char* brdExt : brdExts) {
+            if (0 == strcmp(brdExt, ext)) {
+                bool gray = codec->getEncodedInfo().color() == SkEncodedInfo::kGray_Color;
+                push_brd_srcs(path, gray);
+                break;
+            }
         }
     }
 
@@ -678,80 +752,6 @@
     }
 }
 
-static void push_brd_src(Path path, CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode,
-        uint32_t sampleSize) {
-    SkString folder("brd_android_codec");
-    switch (mode) {
-        case BRDSrc::kFullImage_Mode:
-            break;
-        case BRDSrc::kDivisor_Mode:
-            folder.append("_divisor");
-            break;
-        default:
-            SkASSERT(false);
-            return;
-    }
-
-    switch (dstColorType) {
-        case CodecSrc::kGetFromCanvas_DstColorType:
-            break;
-        case CodecSrc::kGrayscale_Always_DstColorType:
-            folder.append("_kGray");
-            break;
-        default:
-            SkASSERT(false);
-            return;
-    }
-
-    if (1 != sampleSize) {
-        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
-    }
-
-    BRDSrc* src = new BRDSrc(path, mode, dstColorType, sampleSize);
-    push_src("image", folder, src);
-}
-
-static void push_brd_srcs(Path path) {
-    // Only run grayscale to one sampleSize and Mode. Though interesting
-    // to test grayscale, it should not reveal anything across various
-    // sampleSizes and Modes
-    // Arbitrarily choose Mode and sampleSize.
-    push_brd_src(path, CodecSrc::kGrayscale_Always_DstColorType, BRDSrc::kFullImage_Mode, 2);
-
-
-    // Test on a variety of sampleSizes, making sure to include:
-    // - 2, 4, and 8, which are natively supported by jpeg
-    // - multiples of 2 which are not divisible by 4 (analogous for 4)
-    // - larger powers of two, since BRD clients generally use powers of 2
-    // We will only produce output for the larger sizes on large images.
-    const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64 };
-
-    const BRDSrc::Mode modes[] = {
-            BRDSrc::kFullImage_Mode,
-            BRDSrc::kDivisor_Mode,
-    };
-
-    for (uint32_t sampleSize : sampleSizes) {
-        for (BRDSrc::Mode mode : modes) {
-            push_brd_src(path, CodecSrc::kGetFromCanvas_DstColorType, mode, sampleSize);
-        }
-    }
-}
-
-static bool brd_supported(const char* ext) {
-    static const char* const exts[] = {
-        "jpg", "jpeg", "png", "webp",
-        "JPG", "JPEG", "PNG", "WEBP",
-    };
-
-    for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
-        if (0 == strcmp(exts[i], ext)) {
-            return true;
-        }
-    }
-    return false;
-}
-
 template <typename T>
 void gather_file_srcs(const SkCommandLineFlags::StringArray& flags, const char* ext) {
     for (int i = 0; i < flags.count(); i++) {
@@ -785,14 +785,6 @@
 
     for (auto image : images) {
         push_codec_srcs(image);
-        if (FLAGS_simpleCodec) {
-            continue;
-        }
-
-        const char* ext = strrchr(image.c_str(), '.');
-        if (ext && brd_supported(ext+1)) {
-            push_brd_srcs(image);
-        }
     }
 
     SkTArray<SkString> colorImages;
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index e255dd3..a862969 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -152,10 +152,6 @@
         return Error::Nonfatal(SkStringPrintf("Could not create brd for %s.", fPath.c_str()));
     }
 
-    if (!brd->conversionSupported(colorType)) {
-        return Error::Nonfatal("Cannot convert to color type.");
-    }
-
     const uint32_t width = brd->width();
     const uint32_t height = brd->height();
     // Visually inspecting very small output images is not necessary.
diff --git a/include/android/SkBitmapRegionDecoder.h b/include/android/SkBitmapRegionDecoder.h
index df0c370..c41d112 100644
--- a/include/android/SkBitmapRegionDecoder.h
+++ b/include/android/SkBitmapRegionDecoder.h
@@ -63,11 +63,6 @@
                               const SkIRect& desiredSubset, int sampleSize,
                               SkColorType colorType, bool requireUnpremul,
                               sk_sp<SkColorSpace> prefColorSpace = nullptr) = 0;
-    /*
-     * @param  Requested destination color type
-     * @return true if we support the requested color type and false otherwise
-     */
-    virtual bool conversionSupported(SkColorType colorType) = 0;
 
     virtual SkEncodedImageFormat getEncodedFormat() = 0;
 
diff --git a/include/codec/SkAndroidCodec.h b/include/codec/SkAndroidCodec.h
index 3571108..cc93bf1 100644
--- a/include/codec/SkAndroidCodec.h
+++ b/include/codec/SkAndroidCodec.h
@@ -41,8 +41,9 @@
      */
     static std::unique_ptr<SkAndroidCodec> MakeFromData(sk_sp<SkData>, SkPngChunkReader* = nullptr);
 
-    virtual ~SkAndroidCodec() {}
+    virtual ~SkAndroidCodec();
 
+    const SkEncodedInfo& getEncodedInfo() const;
 
     const SkImageInfo& getInfo() const { return fInfo; }
 
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index fb4f165..8ec44c8 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -846,6 +846,13 @@
     bool                               fStartedIncrementalDecode;
 
     /**
+     *  Return whether {srcColor, srcIsOpaque, srcCS} can convert to dst.
+     *
+     *  Will be called for the appropriate frame, prior to initializing the colorXform.
+     */
+    virtual bool conversionSupported(const SkImageInfo& dst, SkEncodedInfo::Color srcColor,
+                                     bool srcIsOpaque, const SkColorSpace* srcCS) const;
+    /**
      *  Return whether these dimensions are supported as a scale.
      *
      *  The codec may choose to cache the information about scale and subset.
diff --git a/include/codec/SkEncodedInfo.h b/include/codec/SkEncodedInfo.h
index 3b1ce48..dce88ca 100644
--- a/include/codec/SkEncodedInfo.h
+++ b/include/codec/SkEncodedInfo.h
@@ -156,6 +156,8 @@
 
     Color color() const { return fColor; }
     Alpha alpha() const { return fAlpha; }
+    bool opaque() const { return fAlpha == kOpaque_Alpha; }
+
     uint8_t bitsPerComponent() const { return fBitsPerComponent; }
 
     uint8_t bitsPerPixel() const {
diff --git a/src/android/SkBitmapRegionCodec.cpp b/src/android/SkBitmapRegionCodec.cpp
index e2ab423..f77c702 100644
--- a/src/android/SkBitmapRegionCodec.cpp
+++ b/src/android/SkBitmapRegionCodec.cpp
@@ -116,8 +116,3 @@
 
     return true;
 }
-
-bool SkBitmapRegionCodec::conversionSupported(SkColorType colorType) {
-    SkImageInfo dstInfo = fCodec->getInfo().makeColorType(colorType);
-    return conversion_possible(dstInfo, fCodec->getInfo());
-}
diff --git a/src/android/SkBitmapRegionCodec.h b/src/android/SkBitmapRegionCodec.h
index a7cfe33..94194c0 100644
--- a/src/android/SkBitmapRegionCodec.h
+++ b/src/android/SkBitmapRegionCodec.h
@@ -28,8 +28,6 @@
                       SkColorType colorType, bool requireUnpremul,
                       sk_sp<SkColorSpace> prefColorSpace) override;
 
-    bool conversionSupported(SkColorType colorType) override;
-
     SkEncodedImageFormat getEncodedFormat() override { return fCodec->getEncodedFormat(); }
 
     SkColorType computeOutputColorType(SkColorType requestedColorType) override {
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 3a57352..d004eb8 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -66,6 +66,12 @@
     , fCodec(codec)
 {}
 
+SkAndroidCodec::~SkAndroidCodec() {}
+
+const SkEncodedInfo& SkAndroidCodec::getEncodedInfo() const {
+    return fCodec->getEncodedInfo();
+}
+
 std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<SkStream> stream, SkPngChunkReader* chunkReader) {
     auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader);
     if (nullptr == codec) {
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 18f8768..d8167ef 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -620,9 +620,7 @@
 
 SkCodec::Result SkBmpCodec::prepareToDecode(const SkImageInfo& dstInfo,
         const SkCodec::Options& options) {
-    if (!conversion_possible(dstInfo, this->getInfo()) ||
-        !this->initializeColorXform(dstInfo, options.fPremulBehavior))
-    {
+    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
         return kInvalidConversion;
     }
 
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 2b61082..84af227 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -155,6 +155,29 @@
 
 SkCodec::~SkCodec() {}
 
+bool SkCodec::conversionSupported(const SkImageInfo& dst, SkEncodedInfo::Color srcColor,
+                                  bool srcIsOpaque, const SkColorSpace* srcCS) const {
+    if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
+        return false;
+    }
+
+    switch (dst.colorType()) {
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            return true;
+        case kRGBA_F16_SkColorType:
+            return dst.colorSpace() && dst.colorSpace()->gammaIsLinear();
+        case kRGB_565_SkColorType:
+            return srcIsOpaque;
+        case kGray_8_SkColorType:
+            return SkEncodedInfo::kGray_Color == srcColor && srcIsOpaque &&
+                   !needs_color_xform(dst, srcCS, false);
+        default:
+            return false;
+    }
+
+}
+
 bool SkCodec::rewindIfNeeded() {
     // Store the value of fNeedsRewind so we can update it. Next read will
     // require a rewind.
@@ -194,6 +217,10 @@
                                           const Options& options) {
     const int index = options.fFrameIndex;
     if (0 == index) {
+        if (!this->conversionSupported(info, fEncodedInfo.color(), fEncodedInfo.opaque(),
+                                      fSrcInfo.colorSpace())) {
+            return kInvalidConversion;
+        }
         return kSuccess;
     }
 
@@ -213,6 +240,11 @@
     const auto* frame = frameHolder->getFrame(index);
     SkASSERT(frame);
 
+    if (!this->conversionSupported(info, fEncodedInfo.color(), !frame->hasAlpha(),
+                                   fSrcInfo.colorSpace())) {
+        return kInvalidConversion;
+    }
+
     const int requiredFrame = frame->getRequiredFrame();
     if (requiredFrame == kNone) {
         return kSuccess;
@@ -278,10 +310,6 @@
     if (nullptr == options) {
         options = &optsStorage;
     } else {
-        const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes, *options);
-        if (frameIndexResult != kSuccess) {
-            return frameIndexResult;
-        }
         if (options->fSubset) {
             SkIRect subset(*options->fSubset);
             if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
@@ -292,6 +320,12 @@
         }
     }
 
+    const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
+                                                           *options);
+    if (frameIndexResult != kSuccess) {
+        return frameIndexResult;
+    }
+
     // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
     // because it supports arbitrary scaling/subset combinations.
     if (!this->dimensionsSupported(info.dimensions())) {
@@ -350,10 +384,6 @@
     if (nullptr == options) {
         options = &optsStorage;
     } else {
-        const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes, *options);
-        if (frameIndexResult != kSuccess) {
-            return frameIndexResult;
-        }
         if (options->fSubset) {
             SkIRect size = SkIRect::MakeSize(info.dimensions());
             if (!size.contains(*options->fSubset)) {
@@ -368,6 +398,12 @@
         }
     }
 
+    const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
+                                                           *options);
+    if (frameIndexResult != kSuccess) {
+        return frameIndexResult;
+    }
+
     if (!this->dimensionsSupported(info.dimensions())) {
         return kInvalidScale;
     }
@@ -418,6 +454,18 @@
         }
     }
 
+    // Scanline decoding only supports decoding the first frame.
+    if (options->fFrameIndex != 0) {
+        return kUnimplemented;
+    }
+
+    // The void* dst and rowbytes in handleFrameIndex or only used for decoding prior
+    // frames, which is not supported here anyway, so it is safe to pass nullptr/0.
+    const Result frameIndexResult = this->handleFrameIndex(info, nullptr, 0, *options);
+    if (frameIndexResult != kSuccess) {
+        return frameIndexResult;
+    }
+
     // FIXME: Support subsets somehow?
     if (!this->dimensionsSupported(info.dimensions())) {
         return kInvalidScale;
@@ -567,7 +615,7 @@
     fXformOnDecode = false;
     bool needsColorCorrectPremul = needs_premul(dstInfo, fEncodedInfo) &&
                                    SkTransferFunctionBehavior::kRespect == premulBehavior;
-    if (needs_color_xform(dstInfo, fSrcInfo, needsColorCorrectPremul)) {
+    if (needs_color_xform(dstInfo, fSrcInfo.colorSpace(), needsColorCorrectPremul)) {
         fColorXform = SkColorSpaceXform_Base::New(fSrcInfo.colorSpace(), dstInfo.colorSpace(),
                                                   premulBehavior);
         if (!fColorXform) {
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index cb7cd94..a737fb7 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -82,31 +82,20 @@
     return ((srcCoord - startCoord) % sampleFactor) == 0;
 }
 
-static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
+static inline bool valid_alpha(SkAlphaType dstAlpha, bool srcIsOpaque) {
     if (kUnknown_SkAlphaType == dstAlpha) {
         return false;
     }
 
-    if (srcAlpha != dstAlpha) {
-        if (kOpaque_SkAlphaType == srcAlpha) {
-            // If the source is opaque, we can support any.
+    if (srcIsOpaque) {
+        if (kOpaque_SkAlphaType != dstAlpha) {
             SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
                           "- it is being decoded as non-opaque, which will draw slower\n");
-            return true;
         }
-
-        // The source is not opaque
-        switch (dstAlpha) {
-            case kPremul_SkAlphaType:
-            case kUnpremul_SkAlphaType:
-                // The source is not opaque, so either of these is okay
-                break;
-            default:
-                // We cannot decode a non-opaque image to opaque (or unknown)
-                return false;
-        }
+        return true;
     }
-    return true;
+
+    return dstAlpha != kOpaque_SkAlphaType;
 }
 
 /*
@@ -287,7 +276,7 @@
            SkEncodedInfo::kUnpremul_Alpha == encodedInfo.alpha();
 }
 
-static inline bool needs_color_xform(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
+static inline bool needs_color_xform(const SkImageInfo& dstInfo, const SkColorSpace* srcCS,
                                      bool needsColorCorrectPremul) {
     // We never perform a color xform in legacy mode.
     if (!dstInfo.colorSpace()) {
@@ -298,7 +287,7 @@
     bool isF16 = kRGBA_F16_SkColorType == dstInfo.colorType();
 
     // Need a color xform when dst space does not match the src.
-    bool srcDstNotEqual = !SkColorSpace::Equals(srcInfo.colorSpace(), dstInfo.colorSpace());
+    bool srcDstNotEqual = !SkColorSpace::Equals(srcCS, dstInfo.colorSpace());
 
     return needsColorCorrectPremul || isF16 || srcDstNotEqual;
 }
@@ -307,38 +296,4 @@
     return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType;
 }
 
-/*
- * Alpha Type Conversions
- * - kOpaque to kOpaque, kUnpremul, kPremul is valid
- * - kUnpremul to kUnpremul, kPremul is valid
- *
- * Color Type Conversions
- * - Always support kRGBA_8888, kBGRA_8888
- * - Support kRGBA_F16 when there is a linear dst color space
- * - Support k565 if kOpaque and color correction is not required
- * - Support k565 if it matches the src, kOpaque, and color correction is not required
- */
-static inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
-    // Ensure the alpha type is valid.
-    if (!valid_alpha(dst.alphaType(), src.alphaType())) {
-        return false;
-    }
-
-    // Check for supported color types.
-    switch (dst.colorType()) {
-        case kRGBA_8888_SkColorType:
-        case kBGRA_8888_SkColorType:
-            return true;
-        case kRGBA_F16_SkColorType:
-            return dst.colorSpace() && dst.colorSpace()->gammaIsLinear();
-        case kRGB_565_SkColorType:
-            return kOpaque_SkAlphaType == src.alphaType();
-        case kGray_8_SkColorType:
-            return kGray_8_SkColorType == src.colorType() &&
-                   kOpaque_SkAlphaType == src.alphaType() && !needs_color_xform(dst, src, false);
-        default:
-            return false;
-    }
-}
-
 #endif // SkCodecPriv_DEFINED
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index d54cbd5..4bd0e17 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -228,9 +228,7 @@
 
     const auto at = frame->hasAlpha() ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
     const auto srcInfo = this->getInfo().makeAlphaType(at);
-    if (!conversion_possible(dstInfo, srcInfo) ||
-        !this->initializeColorXform(dstInfo, opts.fPremulBehavior))
-    {
+    if (!this->initializeColorXform(dstInfo, opts.fPremulBehavior)) {
         return gif_error("Cannot convert input type to output type.\n", kInvalidConversion);
     }
 
diff --git a/src/codec/SkIcoCodec.h b/src/codec/SkIcoCodec.h
index aa19c9b..62de20f 100644
--- a/src/codec/SkIcoCodec.h
+++ b/src/codec/SkIcoCodec.h
@@ -48,6 +48,12 @@
 
     SkScanlineOrder onGetScanlineOrder() const override;
 
+    bool conversionSupported(const SkImageInfo&, SkEncodedInfo::Color, bool,
+                             const SkColorSpace*) const override {
+        // This will be checked by the embedded codec.
+        return true;
+    }
+
 private:
 
     Result onStartScanlineDecode(const SkImageInfo& dstInfo,
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 1409182..fd8ee63 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -58,6 +58,12 @@
 
     bool onDimensionsSupported(const SkISize&) override;
 
+    bool conversionSupported(const SkImageInfo&, SkEncodedInfo::Color, bool,
+                             const SkColorSpace*) const override {
+        // This class checks for conversion after creating colorXform.
+        return true;
+    }
+
 private:
 
     /*
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
index 13e45ab..c5dae3e 100644
--- a/src/codec/SkPngCodec.cpp
+++ b/src/codec/SkPngCodec.cpp
@@ -1086,10 +1086,6 @@
 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
                                         size_t rowBytes, const Options& options,
                                         int* rowsDecoded) {
-    if (!conversion_possible(dstInfo, this->getInfo())) {
-        return kInvalidConversion;
-    }
-
     Result result = this->initializeXforms(dstInfo, options);
     if (kSuccess != result) {
         return result;
@@ -1106,10 +1102,6 @@
 
 SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
         void* dst, size_t rowBytes, const SkCodec::Options& options) {
-    if (!conversion_possible(dstInfo, this->getInfo())) {
-        return kInvalidConversion;
-    }
-
     Result result = this->initializeXforms(dstInfo, options);
     if (kSuccess != result) {
         return result;
diff --git a/src/codec/SkRawCodec.cpp b/src/codec/SkRawCodec.cpp
index cf41d17..93c6db5 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -696,9 +696,7 @@
 SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
                                         size_t dstRowBytes, const Options& options,
                                         int* rowsDecoded) {
-    if (!conversion_possible(dstInfo, this->getInfo()) ||
-        !this->initializeColorXform(dstInfo, options.fPremulBehavior))
-    {
+    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
         SkCodecPrintf("Error: cannot convert input type to output type.\n");
         return kInvalidConversion;
     }
diff --git a/src/codec/SkWbmpCodec.cpp b/src/codec/SkWbmpCodec.cpp
index 934d7d5..bc796c4 100644
--- a/src/codec/SkWbmpCodec.cpp
+++ b/src/codec/SkWbmpCodec.cpp
@@ -108,6 +108,11 @@
     return SkEncodedImageFormat::kWBMP;
 }
 
+bool SkWbmpCodec::conversionSupported(const SkImageInfo& dst, SkEncodedInfo::Color srcColor,
+                                      bool srcIsOpaque, const SkColorSpace* srcCS) const {
+    return valid_color_type(dst) && valid_alpha(dst.alphaType(), srcIsOpaque);
+}
+
 SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
                                          void* dst,
                                          size_t rowBytes,
@@ -118,10 +123,6 @@
         return kUnimplemented;
     }
 
-    if (!valid_color_type(info) || !valid_alpha(info.alphaType(), this->getInfo().alphaType())) {
-        return kInvalidConversion;
-    }
-
     // Initialize the swizzler
     std::unique_ptr<SkSwizzler> swizzler(this->initializeSwizzler(info, options));
     SkASSERT(swizzler);
@@ -186,12 +187,6 @@
         return kUnimplemented;
     }
 
-    if (!valid_color_type(dstInfo) ||
-        !valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType()))
-    {
-        return kInvalidConversion;
-    }
-
     // Initialize the swizzler
     fSwizzler.reset(this->initializeSwizzler(dstInfo, options));
     SkASSERT(fSwizzler);
diff --git a/src/codec/SkWbmpCodec.h b/src/codec/SkWbmpCodec.h
index 0328771..f26967f 100644
--- a/src/codec/SkWbmpCodec.h
+++ b/src/codec/SkWbmpCodec.h
@@ -28,6 +28,8 @@
     Result onGetPixels(const SkImageInfo&, void*, size_t,
                        const Options&, int*) override;
     bool onRewind() override;
+    bool conversionSupported(const SkImageInfo& dst, SkEncodedInfo::Color srcColor,
+                             bool srcIsOpaque, const SkColorSpace* srcCS) const override;
 private:
     /*
      * Returns a swizzler on success, nullptr on failure
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index 94fa175..38feb82 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -398,23 +398,14 @@
 
 SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
                                          const Options& options, int* rowsDecodedPtr) {
+    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
+        return kInvalidConversion;
+    }
+
     const int index = options.fFrameIndex;
     SkASSERT(0 == index || index < fFrameHolder.size());
 
     const auto& srcInfo = this->getInfo();
-    {
-        auto info = srcInfo;
-        if (index > 0) {
-            auto alphaType = alpha_type(fFrameHolder.frame(index)->hasAlpha());
-            info = info.makeAlphaType(alphaType);
-        }
-        if (!conversion_possible(dstInfo, info) ||
-            !this->initializeColorXform(dstInfo, options.fPremulBehavior))
-        {
-            return kInvalidConversion;
-        }
-    }
-
     SkASSERT(0 == index || (!options.fSubset && dstInfo.dimensions() == srcInfo.dimensions()));
 
     WebPDecoderConfig config;
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index 4e68cd1..6ffc448 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -1138,14 +1138,16 @@
     if (supportsScanlineDecoder) {
         REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
     } else {
-        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
+        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
+                        || SkCodec::kInvalidConversion == result);
     }
 
     result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
     if (supportsIncrementalDecoder) {
         REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
     } else {
-        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
+        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
+                        || SkCodec::kInvalidConversion == result);
     }
 
     SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(infoF16.colorSpace())->type());