Add SkColor4f-accepting SkColorFilters::Blend()

Bug: skia:13637
Change-Id: I56fff2859cee120f679a93a2d59925cba8580962
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/566445
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 42e0b09..603b866 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -9,6 +9,8 @@
     varyings using SkSL. Mesh data (vertex and index) can be created on a
     GrDirectContext to avoid re-uploading data per draw. Currently does not
     work with SkPicture or any backend but GPU.
+  * Added SkColorFilters::Blend(const SkColor4f&, sk_sp<SkColorSpace>, SkBlendMode) to
+    complement the existing SkColorFilters::Blend(SkColor, SkBlendMode) factory.
 
 * * *
 
diff --git a/gm/color4f.cpp b/gm/color4f.cpp
index 92ffcbe..d12415e 100644
--- a/gm/color4f.cpp
+++ b/gm/color4f.cpp
@@ -127,3 +127,40 @@
         canvas->translate(0, r.height() * 6 / 5);
     }
 }
+
+DEF_SIMPLE_GM(color4blendcf, canvas, 360, 480) {
+    canvas->translate(10, 10);
+
+    auto srgb = SkColorSpace::MakeSRGB();
+    auto spin = srgb->makeColorSpin(); // RGB -> GBR
+
+    const SkColor4f colors[] {
+        { 1, 0, 0, 1 },
+        { 0, 1, 0, 1 },
+        { 0, 0, 1, 1 },
+        { 0.5, 0.5, 0.5, 1 },
+    };
+
+    SkPaint paint;
+    paint.setColor(SK_ColorWHITE);
+    SkRect r = SkRect::MakeWH(100, 100);
+
+    for (const auto& c4 : colors) {
+        sk_sp<SkColorFilter> filters[] {
+            // Use kModulate and a paint color of white so the final drawn color is color-space
+            // managed 'c4'.
+            SkColorFilters::Blend(c4, nullptr, SkBlendMode::kModulate),
+            SkColorFilters::Blend(c4, srgb, SkBlendMode::kModulate),
+            SkColorFilters::Blend(c4, spin, SkBlendMode::kModulate),
+        };
+
+        canvas->save();
+        for (const auto& f : filters) {
+            paint.setColorFilter(f);
+            canvas->drawRect(r, paint);
+            canvas->translate(r.width() * 6 / 5, 0);
+        }
+        canvas->restore();
+        canvas->translate(0, r.height() * 6 / 5);
+    }
+}
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index ca428cf..695ec97 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -71,7 +71,12 @@
     static sk_sp<SkColorFilter> Compose(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner) {
         return outer ? outer->makeComposed(inner) : inner;
     }
+
+    // Blends between the constant color (src) and input color (dst) based on the SkBlendMode.
+    // If the color space is null, the constant color is assumed to be defined in sRGB.
+    static sk_sp<SkColorFilter> Blend(const SkColor4f& c, sk_sp<SkColorSpace>, SkBlendMode mode);
     static sk_sp<SkColorFilter> Blend(SkColor c, SkBlendMode mode);
+
     static sk_sp<SkColorFilter> Matrix(const SkColorMatrix&);
     static sk_sp<SkColorFilter> Matrix(const float rowMajor[20]);
 
diff --git a/src/core/SkModeColorFilter.cpp b/src/core/SkModeColorFilter.cpp
index 9cb1c52..4ab6b24 100644
--- a/src/core/SkModeColorFilter.cpp
+++ b/src/core/SkModeColorFilter.cpp
@@ -22,16 +22,23 @@
 #include "src/core/SkValidationUtils.h"
 #include "src/core/SkWriteBuffer.h"
 
+static SkColor4f map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
+    SkColor4f color = c;
+    SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType,
+                           dst, kUnpremul_SkAlphaType).apply(color.vec());
+    return color;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkModeColorFilter::SkModeColorFilter(SkColor color, SkBlendMode mode) {
-    fColor = color;
-    fMode = mode;
-}
+SkModeColorFilter::SkModeColorFilter(const SkColor4f& color,
+                                     SkBlendMode mode)
+        : fColor(color)
+        , fMode(mode) {}
 
 bool SkModeColorFilter::onAsAColorMode(SkColor* color, SkBlendMode* mode) const {
     if (color) {
-        *color = fColor;
+        *color = fColor.toSkColor();
     }
     if (mode) {
         *mode = fMode;
@@ -51,21 +58,28 @@
 }
 
 void SkModeColorFilter::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeColor(fColor);
-    buffer.writeUInt((int)fMode);
+    buffer.writeColor4f(fColor);
+    buffer.writeUInt((int) fMode);
 }
 
 sk_sp<SkFlattenable> SkModeColorFilter::CreateProc(SkReadBuffer& buffer) {
-    SkColor color = buffer.readColor();
-    SkBlendMode mode = (SkBlendMode)buffer.readUInt();
-    return SkColorFilters::Blend(color, mode);
+    if (buffer.isVersionLT(SkPicturePriv::kBlend4fColorFilter)) {
+        // Color is 8-bit, sRGB
+        SkColor color = buffer.readColor();
+        SkBlendMode mode = (SkBlendMode)buffer.readUInt();
+        return SkColorFilters::Blend(SkColor4f::FromColor(color), /*sRGB*/nullptr, mode);
+    } else {
+        // Color is 32-bit, sRGB
+        SkColor4f color;
+        buffer.readColor4f(&color);
+        SkBlendMode mode = (SkBlendMode)buffer.readUInt();
+        return SkColorFilters::Blend(color, /*sRGB*/nullptr, mode);
+    }
 }
 
 bool SkModeColorFilter::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
     rec.fPipeline->append(SkRasterPipeline::move_src_dst);
-    SkColor4f color = SkColor4f::FromColor(fColor);
-    SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
-                           rec.fDstCS,          kUnpremul_SkAlphaType).apply(color.vec());
+    SkColor4f color = map_color(fColor, sk_srgb_singleton(), rec.fDstCS);
     rec.fPipeline->append_constant_color(rec.fAlloc, color.premul().vec());
     SkBlendMode_AppendStages(fMode, rec.fPipeline);
     return true;
@@ -74,9 +88,7 @@
 skvm::Color SkModeColorFilter::onProgram(skvm::Builder* p, skvm::Color c,
                                          const SkColorInfo& dstInfo,
                                          skvm::Uniforms* uniforms, SkArenaAlloc*) const {
-    SkColor4f color = SkColor4f::FromColor(fColor);
-    SkColorSpaceXformSteps( sk_srgb_singleton(), kUnpremul_SkAlphaType,
-                           dstInfo.colorSpace(), kPremul_SkAlphaType).apply(color.vec());
+    SkColor4f color = map_color(fColor, sk_srgb_singleton(), dstInfo.colorSpace());
     skvm::Color dst = c,
                 src = p->uniformColor(color, uniforms);
     return p->blend(fMode, src,dst);
@@ -85,6 +97,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 #if SK_SUPPORT_GPU
 #include "src/gpu/Blend.h"
+#include "src/gpu/ganesh/GrColorInfo.h"
 #include "src/gpu/ganesh/GrFragmentProcessor.h"
 #include "src/gpu/ganesh/SkGr.h"
 #include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
@@ -101,7 +114,9 @@
 
     SkDEBUGCODE(const bool fpHasConstIO = !inputFP || inputFP->hasConstantOutputForConstantInput();)
 
-    auto colorFP = GrFragmentProcessor::MakeColor(SkColorToPMColor4f(fColor, dstColorInfo));
+    SkColor4f color = map_color(fColor, sk_srgb_singleton(), dstColorInfo.colorSpace());
+
+    auto colorFP = GrFragmentProcessor::MakeColor(color.premul());
     auto xferFP = GrBlendFragmentProcessor::Make(std::move(colorFP), std::move(inputFP), fMode);
 
     if (xferFP == nullptr) {
@@ -131,7 +146,9 @@
 void SkModeColorFilter::addToKey(const SkKeyContext& keyContext,
                                  SkPaintParamsKeyBuilder* builder,
                                  SkPipelineDataGatherer* gatherer) const {
-    BlendColorFilterBlock::BlendColorFilterData data(fMode, SkColor4f::FromColor(fColor).premul());
+    // TODO: Take into account the render target color space once graphite has color management.
+    SkColor4f color = map_color(fColor, sk_srgb_singleton(), nullptr);
+    BlendColorFilterBlock::BlendColorFilterData data(fMode, color.premul());
 
     BlendColorFilterBlock::BeginBlock(keyContext, builder, gatherer, data);
     builder->endBlock();
@@ -141,38 +158,45 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-sk_sp<SkColorFilter> SkColorFilters::Blend(SkColor color, SkBlendMode mode) {
+sk_sp<SkColorFilter> SkColorFilters::Blend(const SkColor4f& color,
+                                           sk_sp<SkColorSpace> colorSpace,
+                                           SkBlendMode mode) {
     if (!SkIsValidMode(mode)) {
         return nullptr;
     }
 
-    unsigned alpha = SkColorGetA(color);
+    // First map to sRGB to simplify storage in the actual SkColorFilter instance
+    SkColor4f srgb = map_color(color, colorSpace.get(), sk_srgb_singleton());
 
-    // first collapse some modes if possible
-
+    // Next collapse some modes if possible
+    float alpha = srgb.fA;
     if (SkBlendMode::kClear == mode) {
-        color = 0;
+        srgb = SkColors::kTransparent;
         mode = SkBlendMode::kSrc;
     } else if (SkBlendMode::kSrcOver == mode) {
-        if (0 == alpha) {
+        if (0.f == alpha) {
             mode = SkBlendMode::kDst;
-        } else if (255 == alpha) {
+        } else if (1.f == alpha) {
             mode = SkBlendMode::kSrc;
         }
         // else just stay srcover
     }
 
-    // weed out combinations that are noops, and just return null
+    // Finally weed out combinations that are noops, and just return null
     if (SkBlendMode::kDst == mode ||
-        (0 == alpha && (SkBlendMode::kSrcOver == mode ||
-                        SkBlendMode::kDstOver == mode ||
-                        SkBlendMode::kDstOut == mode ||
-                        SkBlendMode::kSrcATop == mode ||
-                        SkBlendMode::kXor == mode ||
-                        SkBlendMode::kDarken == mode)) ||
-            (0xFF == alpha && SkBlendMode::kDstIn == mode)) {
+        (0.f == alpha && (SkBlendMode::kSrcOver == mode ||
+                          SkBlendMode::kDstOver == mode ||
+                          SkBlendMode::kDstOut == mode ||
+                          SkBlendMode::kSrcATop == mode ||
+                          SkBlendMode::kXor == mode ||
+                          SkBlendMode::kDarken == mode)) ||
+            (1.f == alpha && SkBlendMode::kDstIn == mode)) {
         return nullptr;
     }
 
-    return SkModeColorFilter::Make(color, mode);
+    return sk_sp<SkColorFilter>(new SkModeColorFilter(srgb, mode));
+}
+
+sk_sp<SkColorFilter> SkColorFilters::Blend(SkColor color, SkBlendMode mode) {
+    return Blend(SkColor4f::FromColor(color), /*sRGB*/nullptr, mode);
 }
diff --git a/src/core/SkModeColorFilter.h b/src/core/SkModeColorFilter.h
index f785e02..7ef19a9 100644
--- a/src/core/SkModeColorFilter.h
+++ b/src/core/SkModeColorFilter.h
@@ -12,9 +12,7 @@
 
 class SkModeColorFilter : public SkColorFilterBase {
 public:
-    static sk_sp<SkColorFilter> Make(SkColor color, SkBlendMode mode) {
-        return sk_sp<SkColorFilter>(new SkModeColorFilter(color, mode));
-    }
+    SkModeColorFilter(const SkColor4f& color, SkBlendMode mode);
 
     bool onIsAlphaUnchanged() const override;
 
@@ -32,7 +30,6 @@
     SK_FLATTENABLE_HOOKS(SkModeColorFilter)
 
 protected:
-    SkModeColorFilter(SkColor color, SkBlendMode mode);
 
     void flatten(SkWriteBuffer&) const override;
     bool onAsAColorMode(SkColor*, SkBlendMode*) const override;
@@ -42,7 +39,7 @@
                           const SkColorInfo&, skvm::Uniforms*, SkArenaAlloc*) const override;
 
 private:
-    SkColor     fColor;
+    SkColor4f   fColor; // always stored in sRGB
     SkBlendMode fMode;
 
     friend class SkColorFilter;
diff --git a/src/core/SkPicturePriv.h b/src/core/SkPicturePriv.h
index 3d3c934..087711f 100644
--- a/src/core/SkPicturePriv.h
+++ b/src/core/SkPicturePriv.h
@@ -117,6 +117,7 @@
         kBackdropScaleFactor                = 90,
         kRawImageShaders                    = 91,
         kAnisotropicFilter                  = 92,
+        kBlend4fColorFilter                 = 93,
 
         // Only SKPs within the min/current picture version range (inclusive) can be read.
         //
@@ -142,7 +143,7 @@
         // Contact the Infra Gardener (or directly ping rmistry@) if the above steps do not work
         // for you.
         kMin_Version     = kPictureShaderFilterParam_Version,
-        kCurrent_Version = kAnisotropicFilter
+        kCurrent_Version = kBlend4fColorFilter
     };
 };