Add color xform support to SkWebpCodec

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2294993002

Committed: https://skia.googlesource.com/skia/+/828ed1777da74692d0c8a5834017929f5aedcc6b
Review-Url: https://codereview.chromium.org/2294993002
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index c12b1df..0c3aa40 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkCodecPriv.h"
+#include "SkColorSpaceXform.h"
 #include "SkWebpCodec.h"
 #include "SkStreamPriv.h"
 #include "SkTemplates.h"
@@ -143,20 +144,20 @@
                            streamDeleter.release(), demux.release(), std::move(data));
 }
 
-// This version is slightly different from SkCodecPriv's version of conversion_possible. It
-// supports both byte orders for 8888.
-static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
+static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src,
+                                     SkColorSpaceXform* colorXform) {
     if (!valid_alpha(dst.alphaType(), src.alphaType())) {
         return false;
     }
 
     switch (dst.colorType()) {
-        // Both byte orders are supported.
+        case kRGBA_F16_SkColorType:
+            return nullptr != colorXform;
         case kBGRA_8888_SkColorType:
         case kRGBA_8888_SkColorType:
             return true;
         case kRGB_565_SkColorType:
-            return src.alphaType() == kOpaque_SkAlphaType;
+            return nullptr == colorXform && src.alphaType() == kOpaque_SkAlphaType;
         default:
             return false;
     }
@@ -210,8 +211,15 @@
 
 SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
                                          const Options& options, SkPMColor*, int*,
-                                         int* rowsDecoded) {
-    if (!webp_conversion_possible(dstInfo, this->getInfo())) {
+                                         int* rowsDecodedPtr) {
+
+    std::unique_ptr<SkColorSpaceXform> colorXform = nullptr;
+    if (needs_color_xform(dstInfo, this->getInfo())) {
+        colorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpace()),
+                                            sk_ref_sp(dstInfo.colorSpace()));
+    }
+
+    if (!webp_conversion_possible(dstInfo, this->getInfo(), colorXform.get())) {
         return kInvalidConversion;
     }
 
@@ -269,13 +277,28 @@
         config.options.scaled_height = dstDimensions.height();
     }
 
-    config.output.colorspace = webp_decode_mode(dstInfo.colorType(),
-            dstInfo.alphaType() == kPremul_SkAlphaType);
-    config.output.u.RGBA.rgba = (uint8_t*) dst;
-    config.output.u.RGBA.stride = (int) rowBytes;
-    config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes);
+    // FIXME (msarett):
+    // Lossless webp is encoded as BGRA.  In that case, it would be more efficient to
+    // to decode BGRA and apply the color xform to a BGRA buffer.
+    config.output.colorspace = colorXform ? MODE_RGBA :
+            webp_decode_mode(dstInfo.colorType(), dstInfo.alphaType() == kPremul_SkAlphaType);
     config.output.is_external_memory = 1;
 
+    // We will decode the entire image and then perform the color transform.  libwebp
+    // does not provide a row-by-row API.  This is a shame particularly in the F16 case,
+    // where we need to allocate an extra image-sized buffer.
+    SkAutoTMalloc<uint32_t> pixels;
+    if (kRGBA_F16_SkColorType == dstInfo.colorType()) {
+        pixels.reset(dstDimensions.width() * dstDimensions.height());
+        config.output.u.RGBA.rgba = (uint8_t*) pixels.get();
+        config.output.u.RGBA.stride = (int) dstDimensions.width() * sizeof(uint32_t);
+        config.output.u.RGBA.size = config.output.u.RGBA.stride * dstDimensions.height();
+    } else {
+        config.output.u.RGBA.rgba = (uint8_t*) dst;
+        config.output.u.RGBA.stride = (int) rowBytes;
+        config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes);
+    }
+
     WebPIterator frame;
     SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
     // If this succeeded in NewFromStream(), it should succeed again here.
@@ -286,15 +309,36 @@
         return kInvalidInput;
     }
 
+    int rowsDecoded;
+    SkCodec::Result result;
     switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) {
         case VP8_STATUS_OK:
-            return kSuccess;
+            rowsDecoded = dstInfo.height();
+            result = kSuccess;
+            break;
         case VP8_STATUS_SUSPENDED:
-            WebPIDecGetRGB(idec, rowsDecoded, nullptr, nullptr, nullptr);
-            return kIncompleteInput;
+            WebPIDecGetRGB(idec, rowsDecodedPtr, nullptr, nullptr, nullptr);
+            rowsDecoded = *rowsDecodedPtr;
+            result = kIncompleteInput;
+            break;
         default:
             return kInvalidInput;
     }
+
+    if (colorXform) {
+        SkAlphaType xformAlphaType = select_alpha_xform(dstInfo.alphaType(),
+                                                        this->getInfo().alphaType());
+
+        uint32_t* src = (uint32_t*) config.output.u.RGBA.rgba;
+        size_t srcRowBytes = config.output.u.RGBA.stride;
+        for (int y = 0; y < rowsDecoded; y++) {
+            colorXform->apply(dst, src, dstInfo.width(), dstInfo.colorType(), xformAlphaType);
+            dst = SkTAddOffset<void>(dst, rowBytes);
+            src = SkTAddOffset<uint32_t>(src, srcRowBytes);
+        }
+    }
+
+    return result;
 }
 
 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,