Make xformer class for SkCSXCanvas, use for draw loopers

Fixes draw looper gms in gbr-8888 config.

Bug: skia:
Change-Id: I0a9306cc0523c82f2ad9516752d79c1d86b1e295
Reviewed-on: https://skia-review.googlesource.com/10644
Commit-Queue: Matt Sarett <msarett@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
diff --git a/src/core/SkColorSpaceXformer.cpp b/src/core/SkColorSpaceXformer.cpp
new file mode 100644
index 0000000..e983756
--- /dev/null
+++ b/src/core/SkColorSpaceXformer.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorFilter.h"
+#include "SkColorSpaceXformer.h"
+#include "SkColorSpaceXform_Base.h"
+#include "SkDrawLooper.h"
+#include "SkGradientShader.h"
+#include "SkImage_Base.h"
+#include "SkMakeUnique.h"
+
+std::unique_ptr<SkColorSpaceXformer> SkColorSpaceXformer::Make(sk_sp<SkColorSpace> dst) {
+    std::unique_ptr<SkColorSpaceXform> fromSRGB = SkColorSpaceXform_Base::New(
+            SkColorSpace::MakeSRGB().get(), dst.get(), SkTransferFunctionBehavior::kIgnore);
+    if (!fromSRGB) {
+        return nullptr;
+    }
+
+    auto xformer = std::unique_ptr<SkColorSpaceXformer>(new SkColorSpaceXformer());
+    xformer->fDst      = std::move(dst);
+    xformer->fFromSRGB = std::move(fromSRGB);
+    return xformer;
+}
+
+sk_sp<SkImage> SkColorSpaceXformer::apply(const SkImage* src) {
+    return as_IB(src)->makeColorSpace(fDst);
+}
+
+void SkColorSpaceXformer::apply(SkColor* xformed, const SkColor* srgb, int n) {
+    SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, xformed,
+                                    SkColorSpaceXform::kBGRA_8888_ColorFormat, srgb,
+                                    n, kUnpremul_SkAlphaType));
+}
+
+SkColor SkColorSpaceXformer::apply(SkColor srgb) {
+    SkColor xformed;
+    this->apply(&xformed, &srgb, 1);
+    return xformed;
+}
+
+// TODO: Is this introspection going to be enough, or do we need a new SkShader method?
+sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) {
+    SkColor color;
+    if (shader->isConstant() && shader->asLuminanceColor(&color)) {
+        return SkShader::MakeColorShader(this->apply(color));
+    }
+
+    SkShader::TileMode xy[2];
+    SkMatrix local;
+    if (auto img = shader->isAImage(&local, xy)) {
+        return this->apply(img)->makeShader(xy[0], xy[1], &local);
+    }
+
+    SkShader::ComposeRec compose;
+    if (shader->asACompose(&compose)) {
+        auto A = this->apply(compose.fShaderA),
+             B = this->apply(compose.fShaderB);
+        if (A && B) {
+            return SkShader::MakeComposeShader(std::move(A), std::move(B), compose.fBlendMode);
+        }
+    }
+
+    SkShader::GradientInfo gradient;
+    sk_bzero(&gradient, sizeof(gradient));
+    if (auto type = shader->asAGradient(&gradient)) {
+        SkSTArray<8, SkColor>  colors(gradient.fColorCount);
+        SkSTArray<8, SkScalar>    pos(gradient.fColorCount);
+
+        gradient.fColors       = colors.begin();
+        gradient.fColorOffsets =    pos.begin();
+        shader->asAGradient(&gradient);
+
+        SkSTArray<8, SkColor> xformed(gradient.fColorCount);
+        this->apply(xformed.begin(), gradient.fColors, gradient.fColorCount);
+
+        switch (type) {
+            case SkShader::kNone_GradientType:
+            case SkShader::kColor_GradientType:
+                SkASSERT(false);  // Should be unreachable.
+                break;
+
+            case SkShader::kLinear_GradientType:
+                return SkGradientShader::MakeLinear(gradient.fPoint,
+                                                    xformed.begin(),
+                                                    gradient.fColorOffsets,
+                                                    gradient.fColorCount,
+                                                    gradient.fTileMode,
+                                                    gradient.fGradientFlags,
+                                                    &shader->getLocalMatrix());
+            case SkShader::kRadial_GradientType:
+                return SkGradientShader::MakeRadial(gradient.fPoint[0],
+                                                    gradient.fRadius[0],
+                                                    xformed.begin(),
+                                                    gradient.fColorOffsets,
+                                                    gradient.fColorCount,
+                                                    gradient.fTileMode,
+                                                    gradient.fGradientFlags,
+                                                    &shader->getLocalMatrix());
+            case SkShader::kSweep_GradientType:
+                return SkGradientShader::MakeSweep(gradient.fPoint[0].fX,
+                                                   gradient.fPoint[0].fY,
+                                                   xformed.begin(),
+                                                   gradient.fColorOffsets,
+                                                   gradient.fColorCount,
+                                                   gradient.fGradientFlags,
+                                                   &shader->getLocalMatrix());
+            case SkShader::kConical_GradientType:
+                return SkGradientShader::MakeTwoPointConical(gradient.fPoint[0],
+                                                             gradient.fRadius[0],
+                                                             gradient.fPoint[1],
+                                                             gradient.fRadius[1],
+                                                             xformed.begin(),
+                                                             gradient.fColorOffsets,
+                                                             gradient.fColorCount,
+                                                             gradient.fTileMode,
+                                                             gradient.fGradientFlags,
+                                                             &shader->getLocalMatrix());
+        }
+    }
+
+    return nullptr;
+}
+
+const SkPaint& SkColorSpaceXformer::apply(const SkPaint& src) {
+    const SkPaint* result = &src;
+    auto get_dst = [&] {
+        if (result == &src) {
+            fDstPaint = src;
+            result = &fDstPaint;
+        }
+        return &fDstPaint;
+    };
+
+    // All SkColorSpaces have the same black point.
+    if (src.getColor() & 0xffffff) {
+        get_dst()->setColor(this->apply(src.getColor()));
+    }
+
+    if (auto shader = src.getShader()) {
+        if (auto replacement = this->apply(shader)) {
+            get_dst()->setShader(std::move(replacement));
+        }
+    }
+
+    // As far as I know, SkModeColorFilter is the only color filter that holds a color.
+    if (auto cf = src.getColorFilter()) {
+        SkColor color;
+        SkBlendMode mode;
+        if (cf->asColorMode(&color, &mode)) {
+            get_dst()->setColorFilter(SkColorFilter::MakeModeFilter(this->apply(color), mode));
+        }
+    }
+
+    if (auto looper = src.getDrawLooper()) {
+        get_dst()->setDrawLooper(looper->makeColorSpace(this));
+    }
+
+    // TODO:
+    //    - image filters?
+    return *result;
+}
+
+const SkPaint* SkColorSpaceXformer::apply(const SkPaint* src) {
+    return src ? &this->apply(*src) : nullptr;
+}