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/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) {