[svg] Convert stop-color and stop-opacity to presentation attrs

These are somewhat the first presentation attributes of their kind,
in that they are non-inherited but also not applied via canvas layers.

Implementation-wise the main difference is that these attributes are
not propagated through the fInherited field of the render context's
presentation attribute list.

Change-Id: I0909507b0ecbd21732b3f80c46a343f5a0a9bf7a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340661
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Tyler Denniston <tdenniston@google.com>
diff --git a/modules/svg/src/SkSVGAttribute.cpp b/modules/svg/src/SkSVGAttribute.cpp
index 6a9129d..d07aa91 100644
--- a/modules/svg/src/SkSVGAttribute.cpp
+++ b/modules/svg/src/SkSVGAttribute.cpp
@@ -34,5 +34,8 @@
     result.fFontWeight.init(SkSVGFontWeight::Type::kNormal);
     result.fTextAnchor.init(SkSVGTextAnchor::Type::kStart);
 
+    result.fStopColor.set(SkSVGColor(SK_ColorBLACK));
+    result.fStopOpacity.set(SkSVGNumberType(1));
+
     return result;
 }
diff --git a/modules/svg/src/SkSVGAttributeParser.cpp b/modules/svg/src/SkSVGAttributeParser.cpp
index d34f153..5bfea3b 100644
--- a/modules/svg/src/SkSVGAttributeParser.cpp
+++ b/modules/svg/src/SkSVGAttributeParser.cpp
@@ -604,23 +604,6 @@
     return parsedValue && this->parseEOSToken();
 }
 
-// https://www.w3.org/TR/SVG11/pservers.html#StopElement
-bool SkSVGAttributeParser::parseStopColor(SkSVGStopColor* stopColor) {
-    SkSVGColorType c;
-    bool parsedValue = false;
-    if (this->parse(&c)) {
-        *stopColor = SkSVGStopColor(c);
-        parsedValue = true;
-    } else if (this->parseExpectedStringToken("currentColor")) {
-        *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kCurrentColor);
-        parsedValue = true;
-    } else if (this->parseExpectedStringToken("inherit")) {
-        *stopColor = SkSVGStopColor(SkSVGStopColor::Type::kInherit);
-        parsedValue = true;
-    }
-    return parsedValue && this->parseEOSToken();
-}
-
 // https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
 template <>
 bool SkSVGAttributeParser::parse(SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
diff --git a/modules/svg/src/SkSVGDOM.cpp b/modules/svg/src/SkSVGDOM.cpp
index 356de27..643230e 100644
--- a/modules/svg/src/SkSVGDOM.cpp
+++ b/modules/svg/src/SkSVGDOM.cpp
@@ -93,17 +93,6 @@
     return true;
 }
 
-bool SetNumberAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
-                        const char* stringValue) {
-    auto parseResult = SkSVGAttributeParser::parse<SkSVGNumberType>(stringValue);
-    if (!parseResult.isValid()) {
-        return false;
-    }
-
-    node->setAttribute(attr, SkSVGNumberValue(*parseResult));
-    return true;
-}
-
 bool SetViewBoxAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                          const char* stringValue) {
     SkSVGViewBoxType viewBox;
@@ -116,18 +105,6 @@
     return true;
 }
 
-bool SetStopColorAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
-                           const char* stringValue) {
-    SkSVGStopColor stopColor;
-    SkSVGAttributeParser parser(stringValue);
-    if (!parser.parseStopColor(&stopColor)) {
-        return false;
-    }
-
-    node->setAttribute(attr, SkSVGStopColorValue(stopColor));
-    return true;
-}
-
 bool SetObjectBoundingBoxUnitsAttribute(const sk_sp<SkSVGNode>& node,
                                         SkSVGAttribute attr,
                                         const char* stringValue) {
@@ -259,8 +236,6 @@
     { "r"                  , { SkSVGAttribute::kR                , SetLengthAttribute       }},
     { "rx"                 , { SkSVGAttribute::kRx               , SetLengthAttribute       }},
     { "ry"                 , { SkSVGAttribute::kRy               , SetLengthAttribute       }},
-    { "stop-color"         , { SkSVGAttribute::kStopColor        , SetStopColorAttribute    }},
-    { "stop-opacity"       , { SkSVGAttribute::kStopOpacity      , SetNumberAttribute       }},
     { "style"              , { SkSVGAttribute::kUnknown          , SetStyleAttributes       }},
     { "text"               , { SkSVGAttribute::kText             , SetStringAttribute       }},
     { "transform"          , { SkSVGAttribute::kTransform        , SetTransformAttribute    }},
diff --git a/modules/svg/src/SkSVGGradient.cpp b/modules/svg/src/SkSVGGradient.cpp
index 2448d5b..cc5b297 100644
--- a/modules/svg/src/SkSVGGradient.cpp
+++ b/modules/svg/src/SkSVGGradient.cpp
@@ -53,25 +53,28 @@
 
 SkColor SkSVGGradient::resolveStopColor(const SkSVGRenderContext& ctx,
                                         const SkSVGStop& stop) const {
-    const SkSVGStopColor& stopColor = stop.stopColor();
+    const auto& stopColor = stop.getStopColor();
+    const auto& stopOpacity = stop.getStopOpacity();
+    // Uninherited presentation attrs should have a concrete value at this point.
+    if (!stopColor.isValue() || !stopOpacity.isValue()) {
+        SkDebugf("unhandled: stop-color or stop-opacity has no value\n");
+        return SK_ColorBLACK;
+    }
+
     SkColor color;
-    switch (stopColor.type()) {
-        case SkSVGStopColor::Type::kColor:
-            color = stopColor.color();
+    switch (stopColor->type()) {
+        case SkSVGColor::Type::kColor:
+            color = stopColor->color();
             break;
-        case SkSVGStopColor::Type::kCurrentColor:
+        case SkSVGColor::Type::kCurrentColor:
             color = *ctx.presentationContext().fInherited.fColor;
             break;
-        case SkSVGStopColor::Type::kICCColor:
+        case SkSVGColor::Type::kICCColor:
             SkDebugf("unimplemented 'icccolor' stop-color type\n");
             color = SK_ColorBLACK;
             break;
-        case SkSVGStopColor::Type::kInherit:
-            SkDebugf("unimplemented 'inherit' stop-color type\n");
-            color = SK_ColorBLACK;
-            break;
     }
-    return SkColorSetA(color, SkScalarRoundToInt(stop.stopOpacity() * 255));
+    return SkColorSetA(color, SkScalarRoundToInt(*stopOpacity * 255));
 }
 
 bool SkSVGGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
diff --git a/modules/svg/src/SkSVGNode.cpp b/modules/svg/src/SkSVGNode.cpp
index a8f39ad..acf8f8f 100644
--- a/modules/svg/src/SkSVGNode.cpp
+++ b/modules/svg/src/SkSVGNode.cpp
@@ -14,7 +14,11 @@
 #include "modules/svg/include/SkSVGValue.h"
 #include "src/core/SkTLazy.h"
 
-SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) { }
+SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) {
+    // Uninherited presentation attributes need a non-null default value.
+    fPresentationAttributes.fStopColor.set(SkSVGColor(SK_ColorBLACK));
+    fPresentationAttributes.fStopOpacity.set(SkSVGNumberType(1.0f));
+}
 
 SkSVGNode::~SkSVGNode() { }
 
@@ -94,6 +98,8 @@
            || PARSE_AND_SET("font-style"       , FontStyle)
            || PARSE_AND_SET("font-weight"      , FontWeight)
            || 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)
diff --git a/modules/svg/src/SkSVGRenderContext.cpp b/modules/svg/src/SkSVGRenderContext.cpp
index 7f7be49..6925a4d 100644
--- a/modules/svg/src/SkSVGRenderContext.cpp
+++ b/modules/svg/src/SkSVGRenderContext.cpp
@@ -455,6 +455,12 @@
     if (attrs.fFilter.isValue()) {
         this->applyFilter(*attrs.fFilter);
     }
+
+    // Remaining uninherited presentation attributes are accessed as SkSVGNode fields, not via
+    // the render context.
+    // TODO: resolve these in a pre-render styling pass and assert here that they are values.
+    // - stop-color
+    // - stop-opacity
 }
 
 void SkSVGRenderContext::applyOpacity(SkScalar opacity, uint32_t flags) {
diff --git a/modules/svg/src/SkSVGStop.cpp b/modules/svg/src/SkSVGStop.cpp
index edf39bc..03b80cc 100644
--- a/modules/svg/src/SkSVGStop.cpp
+++ b/modules/svg/src/SkSVGStop.cpp
@@ -16,14 +16,6 @@
     fOffset = offset;
 }
 
-void SkSVGStop::setStopColor(const SkSVGStopColor& color) {
-    fStopColor = color;
-}
-
-void SkSVGStop::setStopOpacity(const SkSVGNumberType& opacity) {
-    fStopOpacity = SkTPin<SkScalar>(opacity, 0, 1);
-}
-
 void SkSVGStop::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
     switch (attr) {
     case SkSVGAttribute::kOffset:
@@ -31,16 +23,6 @@
             this->setOffset(*offset);
         }
         break;
-    case SkSVGAttribute::kStopColor:
-        if (const auto* color = v.as<SkSVGStopColorValue>()) {
-            this->setStopColor(*color);
-        }
-        break;
-    case SkSVGAttribute::kStopOpacity:
-        if (const auto* opacity = v.as<SkSVGNumberValue>()) {
-            this->setStopOpacity(*opacity);
-        }
-        break;
     default:
         this->INHERITED::onSetAttribute(attr, v);
     }