[svg] Add plumbing for color-interpolation-filters property

The default colorspace for filter effects is linear RGB, as specified in
https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties.
Currently we perform all filtering in the destination colorspace. This
CL adds the new presentation attribute with the default setting
(according to the spec) of linear RGB.

This CL does not actually implement any colorspace transformations for
filters.

Bug: skia:10841
Change-Id: Id778ad3fa5cb6e0aed756584a50880edd9d82e2b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/352738
Commit-Queue: Tyler Denniston <tdenniston@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/modules/svg/include/SkSVGAttribute.h b/modules/svg/include/SkSVGAttribute.h
index 9c2dfda..3580180 100644
--- a/modules/svg/include/SkSVGAttribute.h
+++ b/modules/svg/include/SkSVGAttribute.h
@@ -16,6 +16,7 @@
 enum class SkSVGAttribute {
     kClipRule,
     kColor,
+    kColorInterpolationFilters,
     kCx, // <circle>, <ellipse>, <radialGradient>: center x position
     kCy, // <circle>, <ellipse>, <radialGradient>: center y position
     kD,
@@ -89,6 +90,7 @@
     SkSVGProperty<SkSVGVisibility, true> fVisibility;
 
     SkSVGProperty<SkSVGColorType , true> fColor;
+    SkSVGProperty<SkSVGColorspace, true> fColorInterpolationFilters;
 
     SkSVGProperty<SkSVGFontFamily, true> fFontFamily;
     SkSVGProperty<SkSVGFontStyle , true> fFontStyle;
diff --git a/modules/svg/include/SkSVGFe.h b/modules/svg/include/SkSVGFe.h
index d21645c..92bd9d3 100644
--- a/modules/svg/include/SkSVGFe.h
+++ b/modules/svg/include/SkSVGFe.h
@@ -29,6 +29,16 @@
     // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion
     SkRect resolveFilterSubregion(const SkSVGRenderContext&, const SkSVGFilterContext&) const;
 
+    /**
+     * Resolves the colorspace within which this filter effect should be applied.
+     * Spec: https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties
+     * 'color-interpolation-filters' property.
+     */
+    SkSVGColorspace resolveColorspace(const SkSVGRenderContext&) const;
+
+    /** Propagates any inherited presentation attributes in the given context. */
+    void applyProperties(SkSVGRenderContext*) const;
+
     SVG_ATTR(In, SkSVGFeInputType, SkSVGFeInputType(SkSVGFeInputType::Type::kSourceGraphic))
     SVG_ATTR(Result, SkSVGStringType, SkSVGStringType())
     SVG_OPTIONAL_ATTR(X, SkSVGLength)
diff --git a/modules/svg/include/SkSVGNode.h b/modules/svg/include/SkSVGNode.h
index b24ce52..36b27ee 100644
--- a/modules/svg/include/SkSVGNode.h
+++ b/modules/svg/include/SkSVGNode.h
@@ -101,35 +101,36 @@
     virtual bool parseAndSetAttribute(const char* name, const char* value);
 
     // inherited
-    SVG_PRES_ATTR(ClipRule        , SkSVGFillRule  , true)
-    SVG_PRES_ATTR(Color           , SkSVGColorType , true)
-    SVG_PRES_ATTR(FillRule        , SkSVGFillRule  , true)
-    SVG_PRES_ATTR(Fill            , SkSVGPaint     , true)
-    SVG_PRES_ATTR(FillOpacity     , SkSVGNumberType, true)
-    SVG_PRES_ATTR(FontFamily      , SkSVGFontFamily, true)
-    SVG_PRES_ATTR(FontSize        , SkSVGFontSize  , true)
-    SVG_PRES_ATTR(FontStyle       , SkSVGFontStyle , true)
-    SVG_PRES_ATTR(FontWeight      , SkSVGFontWeight, true)
-    SVG_PRES_ATTR(Stroke          , SkSVGPaint     , true)
-    SVG_PRES_ATTR(StrokeDashArray , SkSVGDashArray , true)
-    SVG_PRES_ATTR(StrokeDashOffset, SkSVGLength    , true)
-    SVG_PRES_ATTR(StrokeLineCap   , SkSVGLineCap   , true)
-    SVG_PRES_ATTR(StrokeLineJoin  , SkSVGLineJoin  , true)
-    SVG_PRES_ATTR(StrokeMiterLimit, SkSVGNumberType, true)
-    SVG_PRES_ATTR(StrokeOpacity   , SkSVGNumberType, true)
-    SVG_PRES_ATTR(StrokeWidth     , SkSVGLength    , true)
-    SVG_PRES_ATTR(TextAnchor      , SkSVGTextAnchor, true)
-    SVG_PRES_ATTR(Visibility      , SkSVGVisibility, true)
+    SVG_PRES_ATTR(ClipRule                 , SkSVGFillRule  , true)
+    SVG_PRES_ATTR(Color                    , SkSVGColorType , true)
+    SVG_PRES_ATTR(ColorInterpolationFilters, SkSVGColorspace, true)
+    SVG_PRES_ATTR(FillRule                 , SkSVGFillRule  , true)
+    SVG_PRES_ATTR(Fill                     , SkSVGPaint     , true)
+    SVG_PRES_ATTR(FillOpacity              , SkSVGNumberType, true)
+    SVG_PRES_ATTR(FontFamily               , SkSVGFontFamily, true)
+    SVG_PRES_ATTR(FontSize                 , SkSVGFontSize  , true)
+    SVG_PRES_ATTR(FontStyle                , SkSVGFontStyle , true)
+    SVG_PRES_ATTR(FontWeight               , SkSVGFontWeight, true)
+    SVG_PRES_ATTR(Stroke                   , SkSVGPaint     , true)
+    SVG_PRES_ATTR(StrokeDashArray          , SkSVGDashArray , true)
+    SVG_PRES_ATTR(StrokeDashOffset         , SkSVGLength    , true)
+    SVG_PRES_ATTR(StrokeLineCap            , SkSVGLineCap   , true)
+    SVG_PRES_ATTR(StrokeLineJoin           , SkSVGLineJoin  , true)
+    SVG_PRES_ATTR(StrokeMiterLimit         , SkSVGNumberType, true)
+    SVG_PRES_ATTR(StrokeOpacity            , SkSVGNumberType, true)
+    SVG_PRES_ATTR(StrokeWidth              , SkSVGLength    , true)
+    SVG_PRES_ATTR(TextAnchor               , SkSVGTextAnchor, true)
+    SVG_PRES_ATTR(Visibility               , SkSVGVisibility, true)
 
     // not inherited
-    SVG_PRES_ATTR(ClipPath        , SkSVGFuncIRI   , false)
-    SVG_PRES_ATTR(Mask            , SkSVGFuncIRI   , false)
-    SVG_PRES_ATTR(Filter          , SkSVGFuncIRI   , false)
-    SVG_PRES_ATTR(Opacity         , SkSVGNumberType, false)
-    SVG_PRES_ATTR(StopColor       , SkSVGColor     , false)
-    SVG_PRES_ATTR(StopOpacity     , SkSVGNumberType, false)
-    SVG_PRES_ATTR(FloodColor      , SkSVGColor     , false)
-    SVG_PRES_ATTR(FloodOpacity    , SkSVGNumberType, false)
+    SVG_PRES_ATTR(ClipPath                 , SkSVGFuncIRI   , false)
+    SVG_PRES_ATTR(Mask                     , SkSVGFuncIRI   , false)
+    SVG_PRES_ATTR(Filter                   , SkSVGFuncIRI   , false)
+    SVG_PRES_ATTR(Opacity                  , SkSVGNumberType, false)
+    SVG_PRES_ATTR(StopColor                , SkSVGColor     , false)
+    SVG_PRES_ATTR(StopOpacity              , SkSVGNumberType, false)
+    SVG_PRES_ATTR(FloodColor               , SkSVGColor     , false)
+    SVG_PRES_ATTR(FloodOpacity             , SkSVGNumberType, false)
 
 protected:
     SkSVGNode(SkSVGTag);
diff --git a/modules/svg/include/SkSVGTypes.h b/modules/svg/include/SkSVGTypes.h
index ae5b858..b0c7b67 100644
--- a/modules/svg/include/SkSVGTypes.h
+++ b/modules/svg/include/SkSVGTypes.h
@@ -650,4 +650,10 @@
     kPreserve,
 };
 
+enum class SkSVGColorspace {
+    kAuto,
+    kSRGB,
+    kLinearRGB,
+};
+
 #endif // SkSVGTypes_DEFINED
diff --git a/modules/svg/src/SkSVGAttribute.cpp b/modules/svg/src/SkSVGAttribute.cpp
index 843437c..a7d922d 100644
--- a/modules/svg/src/SkSVGAttribute.cpp
+++ b/modules/svg/src/SkSVGAttribute.cpp
@@ -27,6 +27,7 @@
     result.fVisibility.set(SkSVGVisibility(SkSVGVisibility::Type::kVisible));
 
     result.fColor.set(SkSVGColorType(SK_ColorBLACK));
+    result.fColorInterpolationFilters.set(SkSVGColorspace::kLinearRGB);
 
     result.fFontFamily.init("Sans");
     result.fFontStyle.init(SkSVGFontStyle::Type::kNormal);
diff --git a/modules/svg/src/SkSVGAttributeParser.cpp b/modules/svg/src/SkSVGAttributeParser.cpp
index 6b70696..3583df3 100644
--- a/modules/svg/src/SkSVGAttributeParser.cpp
+++ b/modules/svg/src/SkSVGAttributeParser.cpp
@@ -927,3 +927,14 @@
 bool SkSVGAttributeParser::parse(std::vector<SkSVGNumberType>* numbers) {
     return this->parseList(numbers);
 }
+
+template <>
+bool SkSVGAttributeParser::parse(SkSVGColorspace* colorspace) {
+    static constexpr std::tuple<const char*, SkSVGColorspace> gColorspaceMap[] = {
+        { "auto"     , SkSVGColorspace::kAuto      },
+        { "sRGB"     , SkSVGColorspace::kSRGB      },
+        { "linearRGB", SkSVGColorspace::kLinearRGB },
+    };
+
+    return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken();
+}
diff --git a/modules/svg/src/SkSVGFe.cpp b/modules/svg/src/SkSVGFe.cpp
index 8032815..66a8592 100644
--- a/modules/svg/src/SkSVGFe.cpp
+++ b/modules/svg/src/SkSVGFe.cpp
@@ -83,6 +83,12 @@
     return subregion;
 }
 
+SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx) const {
+    return *ctx.presentationContext().fInherited.fColorInterpolationFilters;
+}
+
+void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); }
+
 bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
     return INHERITED::parseAndSetAttribute(name, value) ||
            this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
diff --git a/modules/svg/src/SkSVGFilter.cpp b/modules/svg/src/SkSVGFilter.cpp
index 3e90ee2..917c628 100644
--- a/modules/svg/src/SkSVGFilter.cpp
+++ b/modules/svg/src/SkSVGFilter.cpp
@@ -54,11 +54,20 @@
         const auto& feNode = static_cast<const SkSVGFe&>(*child);
         const auto& feResultType = feNode.getResult();
 
+        // Propagate any inherited properties that may impact filter effect behavior (e.g.
+        // color-interpolation-filters). We call this explicitly here because the SkSVGFe
+        // nodes do not participate in the normal onRender path, which is when property
+        // propagation currently occurs.
+        SkSVGRenderContext localCtx(ctx);
+        feNode.applyProperties(&localCtx);
+
         // TODO: there are specific composition rules that need to be followed
-        filter = feNode.makeImageFilter(ctx, fctx);
+        // TODO: perform colorspace conversions depending on 'color-interpolation-filters' setting
+        // of the current node and its inputs.
+        filter = feNode.makeImageFilter(localCtx, fctx);
 
         if (!feResultType.isEmpty()) {
-            fctx.registerResult(feResultType, filter, feNode.resolveFilterSubregion(ctx, fctx));
+            fctx.registerResult(feResultType, filter, feNode.resolveFilterSubregion(localCtx, fctx));
         }
     }
 
diff --git a/modules/svg/src/SkSVGNode.cpp b/modules/svg/src/SkSVGNode.cpp
index 14b08ac..f87160c 100644
--- a/modules/svg/src/SkSVGNode.cpp
+++ b/modules/svg/src/SkSVGNode.cpp
@@ -88,33 +88,34 @@
             SkSVGAttributeParser::parseProperty<decltype(fPresentationAttributes.f##attrName)>( \
                     svgName, n, v))
 
-    return PARSE_AND_SET("clip-path"        , ClipPath)
-        || PARSE_AND_SET("clip-rule"        , ClipRule)
-        || PARSE_AND_SET("color"            , Color)
-        || PARSE_AND_SET("fill"             , Fill)
-        || PARSE_AND_SET("fill-opacity"     , FillOpacity)
-        || PARSE_AND_SET("fill-rule"        , FillRule)
-        || PARSE_AND_SET("filter"           , Filter)
-        || PARSE_AND_SET("flood-color"      , FloodColor)
-        || PARSE_AND_SET("flood-opacity"    , FloodOpacity)
-        || PARSE_AND_SET("font-family"      , FontFamily)
-        || PARSE_AND_SET("font-size"        , FontSize)
-        || PARSE_AND_SET("font-style"       , FontStyle)
-        || PARSE_AND_SET("font-weight"      , FontWeight)
-        || PARSE_AND_SET("mask"             , Mask)
-        || PARSE_AND_SET("opacity"          , Opacity)
-        || PARSE_AND_SET("stop-color"       , StopColor)
-        || PARSE_AND_SET("stop-opacity"     , StopOpacity)
-        || PARSE_AND_SET("stroke"           , Stroke)
-        || PARSE_AND_SET("stroke-dasharray" , StrokeDashArray)
-        || PARSE_AND_SET("stroke-dashoffset", StrokeDashOffset)
-        || PARSE_AND_SET("stroke-linecap"   , StrokeLineCap)
-        || PARSE_AND_SET("stroke-linejoin"  , StrokeLineJoin)
-        || PARSE_AND_SET("stroke-miterlimit", StrokeMiterLimit)
-        || PARSE_AND_SET("stroke-opacity"   , StrokeOpacity)
-        || PARSE_AND_SET("stroke-width"     , StrokeWidth)
-        || PARSE_AND_SET("text-anchor"      , TextAnchor)
-        || PARSE_AND_SET("visibility"       , Visibility);
+    return PARSE_AND_SET(   "clip-path"                  , ClipPath)
+           || PARSE_AND_SET("clip-rule"                  , ClipRule)
+           || PARSE_AND_SET("color"                      , Color)
+           || PARSE_AND_SET("color-interpolation-filters", ColorInterpolationFilters)
+           || PARSE_AND_SET("fill"                       , Fill)
+           || PARSE_AND_SET("fill-opacity"               , FillOpacity)
+           || PARSE_AND_SET("fill-rule"                  , FillRule)
+           || PARSE_AND_SET("filter"                     , Filter)
+           || PARSE_AND_SET("flood-color"                , FloodColor)
+           || PARSE_AND_SET("flood-opacity"              , FloodOpacity)
+           || PARSE_AND_SET("font-family"                , FontFamily)
+           || PARSE_AND_SET("font-size"                  , FontSize)
+           || PARSE_AND_SET("font-style"                 , FontStyle)
+           || PARSE_AND_SET("font-weight"                , FontWeight)
+           || PARSE_AND_SET("mask"                       , Mask)
+           || PARSE_AND_SET("opacity"                    , Opacity)
+           || PARSE_AND_SET("stop-color"                 , StopColor)
+           || PARSE_AND_SET("stop-opacity"               , StopOpacity)
+           || PARSE_AND_SET("stroke"                     , Stroke)
+           || PARSE_AND_SET("stroke-dasharray"           , StrokeDashArray)
+           || PARSE_AND_SET("stroke-dashoffset"          , StrokeDashOffset)
+           || PARSE_AND_SET("stroke-linecap"             , StrokeLineCap)
+           || PARSE_AND_SET("stroke-linejoin"            , StrokeLineJoin)
+           || PARSE_AND_SET("stroke-miterlimit"          , StrokeMiterLimit)
+           || PARSE_AND_SET("stroke-opacity"             , StrokeOpacity)
+           || PARSE_AND_SET("stroke-width"               , StrokeWidth)
+           || PARSE_AND_SET("text-anchor"                , TextAnchor)
+           || PARSE_AND_SET("visibility"                 , Visibility);
 
 #undef PARSE_AND_SET
 }
diff --git a/modules/svg/src/SkSVGRenderContext.cpp b/modules/svg/src/SkSVGRenderContext.cpp
index 875ed32..d1fb75c 100644
--- a/modules/svg/src/SkSVGRenderContext.cpp
+++ b/modules/svg/src/SkSVGRenderContext.cpp
@@ -262,6 +262,13 @@
 }
 
 template <>
+void commitToPaint<SkSVGAttribute::kColorInterpolationFilters>(const SkSVGPresentationAttributes&,
+                                                               const SkSVGRenderContext&,
+                                                               SkSVGPresentationContext*) {
+    // Not part of the SkPaint state; applied at render time.
+}
+
+template <>
 void commitToPaint<SkSVGAttribute::kFontFamily>(const SkSVGPresentationAttributes&,
                                                 const SkSVGRenderContext&,
                                                 SkSVGPresentationContext*) {
@@ -406,6 +413,7 @@
     ApplyLazyInheritedAttribute(TextAnchor);
     ApplyLazyInheritedAttribute(Visibility);
     ApplyLazyInheritedAttribute(Color);
+    ApplyLazyInheritedAttribute(ColorInterpolationFilters);
 
     // Local 'color' attribute: update paints for attributes that are set to 'currentColor'.
     if (attrs.fColor.isValue()) {