[svg] Implement feComposite, basic filter result storage

- Plumbing to store filter results by id and resolve them as inputs
  when referenced
- Added implementation of feComposite filter
- Added call to resolve input in feColorMatrix
- Bugfix to SkSVGFilterType operator==

The tests filters-color-01-b and filters-composite-03-b should now be
passing.

Bug: skia:10841
Change-Id: I2cd099c60ac21710f25184806c5cc537656b42af
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/332723
Commit-Queue: Tyler Denniston <tdenniston@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/modules/svg/include/SkSVGFe.h b/modules/svg/include/SkSVGFe.h
index 7965a39..1031b15 100644
--- a/modules/svg/include/SkSVGFe.h
+++ b/modules/svg/include/SkSVGFe.h
@@ -18,11 +18,15 @@
     ~SkSVGFe() override = default;
 
     static bool IsFilterEffect(const sk_sp<SkSVGNode>& node) {
-        return node->tag() == SkSVGTag::kFeTurbulence || node->tag() == SkSVGTag::kFeColorMatrix;
+        return node->tag() == SkSVGTag::kFeTurbulence || node->tag() == SkSVGTag::kFeColorMatrix ||
+               node->tag() == SkSVGTag::kFeComposite;
     }
 
     sk_sp<SkImageFilter> makeImageFilter(const SkSVGRenderContext& ctx,
-                                         SkSVGFilterContext* fctx) const;
+                                         const SkSVGFilterContext& fctx) const;
+
+    SVG_ATTR(In, SkSVGFeInputType, SkSVGFeInputType(SkSVGFeInputType::Type::kSourceGraphic))
+    SVG_ATTR(Result, SkSVGStringType, SkSVGStringType())
 
 protected:
     explicit SkSVGFe(SkSVGTag t) : INHERITED(t) {}
@@ -30,6 +34,8 @@
     virtual sk_sp<SkImageFilter> onMakeImageFilter(const SkSVGRenderContext&,
                                                    const SkSVGFilterContext&) const = 0;
 
+    bool parseAndSetAttribute(const char*, const char*) override;
+
 private:
     using INHERITED = SkSVGHiddenContainer;
 };
diff --git a/modules/svg/include/SkSVGFeComposite.h b/modules/svg/include/SkSVGFeComposite.h
new file mode 100644
index 0000000..9a6570a
--- /dev/null
+++ b/modules/svg/include/SkSVGFeComposite.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGFeComposite_DEFINED
+#define SkSVGFeComposite_DEFINED
+
+#include "include/core/SkBlendMode.h"
+#include "modules/svg/include/SkSVGFe.h"
+#include "modules/svg/include/SkSVGTypes.h"
+
+class SkSVGFeComposite final : public SkSVGFe {
+public:
+    ~SkSVGFeComposite() override = default;
+    static sk_sp<SkSVGFeComposite> Make() {
+        return sk_sp<SkSVGFeComposite>(new SkSVGFeComposite());
+    }
+
+    SVG_ATTR(In2, SkSVGFeInputType, SkSVGFeInputType())
+    SVG_ATTR(K1, SkSVGNumberType, SkSVGNumberType(0))
+    SVG_ATTR(K2, SkSVGNumberType, SkSVGNumberType(0))
+    SVG_ATTR(K3, SkSVGNumberType, SkSVGNumberType(0))
+    SVG_ATTR(K4, SkSVGNumberType, SkSVGNumberType(0))
+    SVG_ATTR(Operator, SkSVGFeCompositeOperator, SkSVGFeCompositeOperator::kOver)
+
+protected:
+    sk_sp<SkImageFilter> onMakeImageFilter(const SkSVGRenderContext&,
+                                           const SkSVGFilterContext&) const override;
+
+    bool parseAndSetAttribute(const char*, const char*) override;
+
+private:
+    SkSVGFeComposite() : INHERITED(SkSVGTag::kFeComposite) {}
+
+    static SkBlendMode BlendModeForOperator(SkSVGFeCompositeOperator);
+
+    using INHERITED = SkSVGFe;
+};
+
+#endif  // SkSVGFeComposite_DEFINED
diff --git a/modules/svg/include/SkSVGFilterContext.h b/modules/svg/include/SkSVGFilterContext.h
index 9077c63..01a138b 100644
--- a/modules/svg/include/SkSVGFilterContext.h
+++ b/modules/svg/include/SkSVGFilterContext.h
@@ -10,23 +10,31 @@
 
 #include "include/core/SkRect.h"
 #include "include/core/SkRefCnt.h"
+#include "include/core/SkString.h"
 #include "include/private/SkTHash.h"
+#include "modules/svg/include/SkSVGTypes.h"
 
 class SkImageFilter;
-class SkPicture;
-class SkString;
+class SkSVGFeInputType;
+class SkSVGRenderContext;
 
 class SkSVGFilterContext {
 public:
     SkSVGFilterContext(const SkRect& filterEffectsRegion)
             : fFilterEffectsRegion(filterEffectsRegion) {}
 
-    sk_sp<SkImageFilter> findResultById(const SkString& id) const;
-
     const SkRect& filterEffectsRegion() const { return fFilterEffectsRegion; }
 
+    void registerResult(const SkSVGStringType&, const sk_sp<SkImageFilter>&);
+
+    sk_sp<SkImageFilter> resolveInput(const SkSVGRenderContext&, const SkSVGFeInputType&) const;
+
 private:
+    sk_sp<SkImageFilter> findResultById(const SkSVGStringType&) const;
+
     SkRect fFilterEffectsRegion;
+
+    SkTHashMap<SkSVGStringType, sk_sp<SkImageFilter>> fResults;
 };
 
 #endif  // SkSVGFilterContext_DEFINED
diff --git a/modules/svg/include/SkSVGNode.h b/modules/svg/include/SkSVGNode.h
index 26a9dd8..8b731a5 100644
--- a/modules/svg/include/SkSVGNode.h
+++ b/modules/svg/include/SkSVGNode.h
@@ -26,6 +26,7 @@
     kDefs,
     kEllipse,
     kFeColorMatrix,
+    kFeComposite,
     kFeTurbulence,
     kFilter,
     kG,
diff --git a/modules/svg/include/SkSVGTypes.h b/modules/svg/include/SkSVGTypes.h
index 3985aa1..e4f0fb7 100644
--- a/modules/svg/include/SkSVGTypes.h
+++ b/modules/svg/include/SkSVGTypes.h
@@ -501,7 +501,9 @@
     explicit SkSVGFilterType(Type t) : fType(t) {}
     explicit SkSVGFilterType(const SkString& iri) : fType(Type::kIRI), fIRI(iri) {}
 
-    bool operator==(const SkSVGFilterType& other) const { return fType == other.fType; }
+    bool operator==(const SkSVGFilterType& other) const {
+        return fType == other.fType && fIRI == other.fIRI;
+    }
     bool operator!=(const SkSVGFilterType& other) const { return !(*this == other); }
 
     const SkString& iri() const {
@@ -516,6 +518,40 @@
     SkString fIRI;
 };
 
+class SkSVGFeInputType {
+public:
+    enum class Type {
+        kSourceGraphic,
+        kSourceAlpha,
+        kBackgroundImage,
+        kBackgroundAlpha,
+        kFillPaint,
+        kStrokePaint,
+        kFilterPrimitiveReference,
+    };
+
+    SkSVGFeInputType() : fType(Type::kSourceGraphic) {}
+    explicit SkSVGFeInputType(Type t) : fType(t) {}
+    explicit SkSVGFeInputType(const SkSVGStringType& id)
+            : fType(Type::kFilterPrimitiveReference), fId(id) {}
+
+    bool operator==(const SkSVGFeInputType& other) const {
+        return fType == other.fType && fId == other.fId;
+    }
+    bool operator!=(const SkSVGFeInputType& other) const { return !(*this == other); }
+
+    const SkString& id() const {
+        SkASSERT(fType == Type::kFilterPrimitiveReference);
+        return fId;
+    }
+
+    Type type() const { return fType; }
+
+private:
+    Type fType;
+    SkString fId;
+};
+
 enum class SkSVGFeColorMatrixType {
     kMatrix,
     kSaturate,
@@ -525,6 +561,15 @@
 
 using SkSVGFeColorMatrixValues = SkTDArray<SkSVGNumberType>;
 
+enum class SkSVGFeCompositeOperator {
+    kOver,
+    kIn,
+    kOut,
+    kAtop,
+    kXor,
+    kArithmetic,
+};
+
 class SkSVGFeTurbulenceBaseFrequency {
 public:
     SkSVGFeTurbulenceBaseFrequency() : fFreqX(0), fFreqY(0) {}
diff --git a/modules/svg/src/SkSVGAttributeParser.cpp b/modules/svg/src/SkSVGAttributeParser.cpp
index 0f74906..361dcae 100644
--- a/modules/svg/src/SkSVGAttributeParser.cpp
+++ b/modules/svg/src/SkSVGAttributeParser.cpp
@@ -269,6 +269,16 @@
     }, iri);
 }
 
+template <>
+bool SkSVGAttributeParser::parse(SkSVGStringType* result) {
+    if (this->parseEOSToken()) {
+        return false;
+    }
+    *result = SkSVGStringType(fCurPos);
+    fCurPos += result->size();
+    return this->parseEOSToken();
+}
+
 // https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
 bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
     // consume WS
diff --git a/modules/svg/src/SkSVGDOM.cpp b/modules/svg/src/SkSVGDOM.cpp
index 342b861..790d2f4 100644
--- a/modules/svg/src/SkSVGDOM.cpp
+++ b/modules/svg/src/SkSVGDOM.cpp
@@ -16,6 +16,7 @@
 #include "modules/svg/include/SkSVGDefs.h"
 #include "modules/svg/include/SkSVGEllipse.h"
 #include "modules/svg/include/SkSVGFeColorMatrix.h"
+#include "modules/svg/include/SkSVGFeComposite.h"
 #include "modules/svg/include/SkSVGFeTurbulence.h"
 #include "modules/svg/include/SkSVGFilter.h"
 #include "modules/svg/include/SkSVGG.h"
@@ -333,6 +334,7 @@
     { "defs"          , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make();           }},
     { "ellipse"       , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make();        }},
     { "feColorMatrix" , []() -> sk_sp<SkSVGNode> { return SkSVGFeColorMatrix::Make();  }},
+    { "feComposite"   , []() -> sk_sp<SkSVGNode> { return SkSVGFeComposite::Make();    }},
     { "feTurbulence"  , []() -> sk_sp<SkSVGNode> { return SkSVGFeTurbulence::Make();   }},
     { "filter"        , []() -> sk_sp<SkSVGNode> { return SkSVGFilter::Make();         }},
     { "g"             , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();              }},
diff --git a/modules/svg/src/SkSVGFe.cpp b/modules/svg/src/SkSVGFe.cpp
index 058c05c..78ed49f 100644
--- a/modules/svg/src/SkSVGFe.cpp
+++ b/modules/svg/src/SkSVGFe.cpp
@@ -6,10 +6,40 @@
  */
 
 #include "include/effects/SkImageFilters.h"
+#include "modules/svg/include/SkSVGAttributeParser.h"
 #include "modules/svg/include/SkSVGFe.h"
-#include "modules/svg/include/SkSVGFilterContext.h"
 
 sk_sp<SkImageFilter> SkSVGFe::makeImageFilter(const SkSVGRenderContext& ctx,
-                                              SkSVGFilterContext* fctx) const {
-    return this->onMakeImageFilter(ctx, *fctx);
+                                              const SkSVGFilterContext& fctx) const {
+    return this->onMakeImageFilter(ctx, fctx);
+}
+
+bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
+    return INHERITED::parseAndSetAttribute(name, value) ||
+           this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
+           this->setResult(SkSVGAttributeParser::parse<SkSVGStringType>("result", name, value));
+}
+
+template <> bool SkSVGAttributeParser::parse(SkSVGFeInputType* type) {
+    static constexpr std::tuple<const char*, SkSVGFeInputType::Type> gTypeMap[] = {
+            {"SourceGraphic", SkSVGFeInputType::Type::kSourceGraphic},
+            {"SourceAlpha", SkSVGFeInputType::Type::kSourceAlpha},
+            {"BackgroundImage", SkSVGFeInputType::Type::kBackgroundImage},
+            {"BackgroundAlpha", SkSVGFeInputType::Type::kBackgroundAlpha},
+            {"FillPaint", SkSVGFeInputType::Type::kFillPaint},
+            {"StrokePaint", SkSVGFeInputType::Type::kStrokePaint},
+    };
+
+    SkSVGStringType resultId;
+    SkSVGFeInputType::Type t;
+    bool parsedValue = false;
+    if (this->parseEnumMap(gTypeMap, &t)) {
+        *type = SkSVGFeInputType(t);
+        parsedValue = true;
+    } else if (parse(&resultId)) {
+        *type = SkSVGFeInputType(resultId);
+        parsedValue = true;
+    }
+
+    return parsedValue && this->parseEOSToken();
 }
diff --git a/modules/svg/src/SkSVGFeColorMatrix.cpp b/modules/svg/src/SkSVGFeColorMatrix.cpp
index 4458f38..db1e3d5 100644
--- a/modules/svg/src/SkSVGFeColorMatrix.cpp
+++ b/modules/svg/src/SkSVGFeColorMatrix.cpp
@@ -91,10 +91,9 @@
 
 sk_sp<SkImageFilter> SkSVGFeColorMatrix::onMakeImageFilter(const SkSVGRenderContext& ctx,
                                                            const SkSVGFilterContext& fctx) const {
-    // TODO: "in" param should supply filter source
-    const sk_sp<SkImageFilter> input = nullptr;
-    return SkImageFilters::ColorFilter(
-            SkColorFilters::Matrix(makeMatrixForType()), input, fctx.filterEffectsRegion());
+    return SkImageFilters::ColorFilter(SkColorFilters::Matrix(makeMatrixForType()),
+                                       fctx.resolveInput(ctx, this->getIn()),
+                                       fctx.filterEffectsRegion());
 }
 
 template <> bool SkSVGAttributeParser::parse(SkSVGFeColorMatrixValues* values) {
diff --git a/modules/svg/src/SkSVGFeComposite.cpp b/modules/svg/src/SkSVGFeComposite.cpp
new file mode 100644
index 0000000..488574f
--- /dev/null
+++ b/modules/svg/src/SkSVGFeComposite.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/effects/SkImageFilters.h"
+#include "modules/svg/include/SkSVGAttributeParser.h"
+#include "modules/svg/include/SkSVGFeComposite.h"
+#include "modules/svg/include/SkSVGFilterContext.h"
+#include "modules/svg/include/SkSVGRenderContext.h"
+
+bool SkSVGFeComposite::parseAndSetAttribute(const char* name, const char* value) {
+    return INHERITED::parseAndSetAttribute(name, value) ||
+           // SkSVGFeInputType parsing defined in SkSVGFe.cpp:
+           this->setIn2(SkSVGAttributeParser::parse<SkSVGFeInputType>("in2", name, value)) ||
+           this->setK1(SkSVGAttributeParser::parse<SkSVGNumberType>("k1", name, value)) ||
+           this->setK2(SkSVGAttributeParser::parse<SkSVGNumberType>("k2", name, value)) ||
+           this->setK3(SkSVGAttributeParser::parse<SkSVGNumberType>("k3", name, value)) ||
+           this->setK4(SkSVGAttributeParser::parse<SkSVGNumberType>("k4", name, value)) ||
+           this->setOperator(
+                   SkSVGAttributeParser::parse<SkSVGFeCompositeOperator>("operator", name, value));
+}
+
+SkBlendMode SkSVGFeComposite::BlendModeForOperator(SkSVGFeCompositeOperator op) {
+    switch (op) {
+        case SkSVGFeCompositeOperator::kOver:
+            return SkBlendMode::kSrcOver;
+        case SkSVGFeCompositeOperator::kIn:
+            return SkBlendMode::kSrcIn;
+        case SkSVGFeCompositeOperator::kOut:
+            return SkBlendMode::kSrcOut;
+        case SkSVGFeCompositeOperator::kAtop:
+            return SkBlendMode::kSrcATop;
+        case SkSVGFeCompositeOperator::kXor:
+            return SkBlendMode::kXor;
+        case SkSVGFeCompositeOperator::kArithmetic:
+            // Arithmetic is not handled with a blend
+            SkASSERT(false);
+            return SkBlendMode::kSrcOver;
+    }
+
+    SkUNREACHABLE;
+}
+
+sk_sp<SkImageFilter> SkSVGFeComposite::onMakeImageFilter(const SkSVGRenderContext& ctx,
+                                                         const SkSVGFilterContext& fctx) const {
+    const SkRect cropRect = fctx.filterEffectsRegion();
+    const sk_sp<SkImageFilter> background = fctx.resolveInput(ctx, fIn2);
+    const sk_sp<SkImageFilter> foreground = fctx.resolveInput(ctx, this->getIn());
+    if (fOperator == SkSVGFeCompositeOperator::kArithmetic) {
+        constexpr bool enforcePMColor = true;
+        return SkImageFilters::Arithmetic(
+                fK1, fK2, fK3, fK4, enforcePMColor, background, foreground, cropRect);
+    } else {
+        return SkImageFilters::Blend(
+                BlendModeForOperator(fOperator), background, foreground, cropRect);
+    }
+}
+
+template <> bool SkSVGAttributeParser::parse(SkSVGFeCompositeOperator* op) {
+    static constexpr std::tuple<const char*, SkSVGFeCompositeOperator> gOpMap[] = {
+            {"over", SkSVGFeCompositeOperator::kOver},
+            {"in", SkSVGFeCompositeOperator::kIn},
+            {"out", SkSVGFeCompositeOperator::kOut},
+            {"atop", SkSVGFeCompositeOperator::kAtop},
+            {"xor", SkSVGFeCompositeOperator::kXor},
+            {"arithmetic", SkSVGFeCompositeOperator::kArithmetic},
+    };
+
+    return this->parseEnumMap(gOpMap, op) && this->parseEOSToken();
+}
diff --git a/modules/svg/src/SkSVGFilter.cpp b/modules/svg/src/SkSVGFilter.cpp
index 5dc97520..f1f57ee 100644
--- a/modules/svg/src/SkSVGFilter.cpp
+++ b/modules/svg/src/SkSVGFilter.cpp
@@ -72,10 +72,13 @@
         }
 
         const auto& feNode = static_cast<const SkSVGFe&>(*child);
-        sk_sp<SkImageFilter> imageFilter = feNode.makeImageFilter(ctx, &fctx);
-        if (imageFilter) {
-            // TODO: there are specific composition rules that need to be followed
-            filter = SkImageFilters::Compose(imageFilter, filter);
+        const auto& feResultType = feNode.getResult();
+
+        // TODO: there are specific composition rules that need to be followed
+        filter = feNode.makeImageFilter(ctx, fctx);
+
+        if (!feResultType.isEmpty()) {
+            fctx.registerResult(feResultType, filter);
         }
     }
 
diff --git a/modules/svg/src/SkSVGFilterContext.cpp b/modules/svg/src/SkSVGFilterContext.cpp
index 743272a..ea9683b 100644
--- a/modules/svg/src/SkSVGFilterContext.cpp
+++ b/modules/svg/src/SkSVGFilterContext.cpp
@@ -7,7 +7,41 @@
 
 #include "include/effects/SkImageFilters.h"
 #include "modules/svg/include/SkSVGFilterContext.h"
+#include "modules/svg/include/SkSVGNode.h"
+#include "modules/svg/include/SkSVGRenderContext.h"
+#include "modules/svg/include/SkSVGTypes.h"
 
-sk_sp<SkImageFilter> SkSVGFilterContext::findResultById(const SkString& id) const {
-    return nullptr;
+sk_sp<SkImageFilter> SkSVGFilterContext::findResultById(const SkSVGStringType& id) const {
+    const sk_sp<SkImageFilter>* res = fResults.find(id);
+    return res ? *res : nullptr;
+}
+
+void SkSVGFilterContext::registerResult(const SkSVGStringType& id,
+                                        const sk_sp<SkImageFilter>& result) {
+    SkASSERT(!id.isEmpty());
+    fResults[id] = result;
+}
+
+sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext& ctx,
+                                                      const SkSVGFeInputType& inputType) const {
+    switch (inputType.type()) {
+        case SkSVGFeInputType::Type::kSourceGraphic:
+            return nullptr;
+        case SkSVGFeInputType::Type::kFillPaint:
+            return SkImageFilters::Paint(*ctx.fillPaint());
+        case SkSVGFeInputType::Type::kStrokePaint: {
+            // The paint filter doesn't handle stroke paints properly, so convert to fill for
+            // simplicity.
+            // TODO: Paint filter is deprecated, but the replacement (SkShaders::*())
+            //       requires some extra work to handle all paint features (gradients, etc).
+            SkPaint p = *ctx.strokePaint();
+            p.setStyle(SkPaint::kFill_Style);
+            return SkImageFilters::Paint(p);
+        }
+        case SkSVGFeInputType::Type::kFilterPrimitiveReference:
+            return findResultById(inputType.id());
+        default:
+            SkDebugf("unhandled filter input type %d\n", inputType.type());
+            return nullptr;
+    }
 }
diff --git a/modules/svg/svg.gni b/modules/svg/svg.gni
index 14d7622..bd4a305 100644
--- a/modules/svg/svg.gni
+++ b/modules/svg/svg.gni
@@ -18,6 +18,7 @@
   "$_include/SkSVGEllipse.h",
   "$_include/SkSVGFe.h",
   "$_include/SkSVGFeColorMatrix.h",
+  "$_include/SkSVGFeComposite.h",
   "$_include/SkSVGFeTurbulence.h",
   "$_include/SkSVGFilter.h",
   "$_include/SkSVGFilterContext.h",
@@ -54,6 +55,7 @@
   "$_src/SkSVGEllipse.cpp",
   "$_src/SkSVGFe.cpp",
   "$_src/SkSVGFeColorMatrix.cpp",
+  "$_src/SkSVGFeComposite.cpp",
   "$_src/SkSVGFeTurbulence.cpp",
   "$_src/SkSVGFilter.cpp",
   "$_src/SkSVGFilterContext.cpp",