[SVGDom] Linear gradient 'spreadMethod' support
authorfmalita <fmalita@chromium.org>
Tue, 13 Sep 2016 19:56:11 +0000 (12:56 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 13 Sep 2016 19:56:11 +0000 (12:56 -0700)
R=stephana@google.com,robertphillips@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2337203002

Review-Url: https://codereview.chromium.org/2337203002

experimental/svg/model/SkSVGAttribute.h
experimental/svg/model/SkSVGAttributeParser.cpp
experimental/svg/model/SkSVGAttributeParser.h
experimental/svg/model/SkSVGDOM.cpp
experimental/svg/model/SkSVGLinearGradient.cpp
experimental/svg/model/SkSVGLinearGradient.h
experimental/svg/model/SkSVGTypes.h
experimental/svg/model/SkSVGValue.h

index ce06b1e..13a6041 100644 (file)
@@ -27,6 +27,7 @@ enum class SkSVGAttribute {
     kR,  // <circle>: radius
     kRx, // <ellipse>,<rect>: horizontal (corner) radius
     kRy, // <ellipse>,<rect>: vertical (corner) radius
+    kSpreadMethod,
     kStopColor,
     kStopOpacity,
     kStroke,
index 9d2d6b8..04b0508 100644 (file)
@@ -491,6 +491,29 @@ bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
     return parsedValue && this->parseEOSToken();
 }
 
+// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementSpreadMethodAttribute
+bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
+    static const struct {
+        SkSVGSpreadMethod::Type fType;
+        const char*             fName;
+    } gSpreadInfo[] = {
+        { SkSVGSpreadMethod::Type::kPad    , "pad"     },
+        { SkSVGSpreadMethod::Type::kReflect, "reflect" },
+        { SkSVGSpreadMethod::Type::kRepeat , "repeat"  },
+    };
+
+    bool parsedValue = false;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
+        if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
+            *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
+            parsedValue = true;
+            break;
+        }
+    }
+
+    return parsedValue && this->parseEOSToken();
+}
+
 // https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
 bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
     SkTDArray<SkPoint> pts;
index c1700a8..9d6939b 100644 (file)
@@ -24,6 +24,7 @@ public:
     bool parseLineJoin(SkSVGLineJoin*);
     bool parsePoints(SkSVGPointsType*);
     bool parseIRI(SkSVGStringType*);
+    bool parseSpreadMethod(SkSVGSpreadMethod*);
 
 private:
     // Stack-only
index d6e4f4c..2694473 100644 (file)
@@ -149,6 +149,18 @@ bool SetLineJoinAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
     return true;
 }
 
+bool SetSpreadMethodAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                             const char* stringValue) {
+    SkSVGSpreadMethod spread;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseSpreadMethod(&spread)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGSpreadMethodValue(spread));
+    return true;
+}
+
 bool SetPointsAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                         const char* stringValue) {
     SkSVGPointsType points;
@@ -239,36 +251,37 @@ struct AttrParseInfo {
 };
 
 SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
-    { "cx"             , { SkSVGAttribute::kCx            , SetLengthAttribute    }},
-    { "cy"             , { SkSVGAttribute::kCy            , SetLengthAttribute    }},
-    { "d"              , { SkSVGAttribute::kD             , SetPathDataAttribute  }},
-    { "fill"           , { SkSVGAttribute::kFill          , SetPaintAttribute     }},
-    { "fill-opacity"   , { SkSVGAttribute::kFillOpacity   , SetNumberAttribute    }},
-    { "height"         , { SkSVGAttribute::kHeight        , SetLengthAttribute    }},
-    { "offset"         , { SkSVGAttribute::kOffset        , SetLengthAttribute    }},
-    { "opacity"        , { SkSVGAttribute::kOpacity       , SetNumberAttribute    }},
-    { "points"         , { SkSVGAttribute::kPoints        , SetPointsAttribute    }},
-    { "r"              , { SkSVGAttribute::kR             , SetLengthAttribute    }},
-    { "rx"             , { SkSVGAttribute::kRx            , SetLengthAttribute    }},
-    { "ry"             , { SkSVGAttribute::kRy            , SetLengthAttribute    }},
-    { "stop-color"     , { SkSVGAttribute::kStopColor     , SetColorAttribute     }},
-    { "stop-opacity"   , { SkSVGAttribute::kStopOpacity   , SetNumberAttribute    }},
-    { "stroke"         , { SkSVGAttribute::kStroke        , SetPaintAttribute     }},
-    { "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute   }},
-    { "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute  }},
-    { "stroke-opacity" , { SkSVGAttribute::kStrokeOpacity , SetNumberAttribute    }},
-    { "stroke-width"   , { SkSVGAttribute::kStrokeWidth   , SetLengthAttribute    }},
-    { "style"          , { SkSVGAttribute::kUnknown       , SetStyleAttributes    }},
-    { "transform"      , { SkSVGAttribute::kTransform     , SetTransformAttribute }},
-    { "viewBox"        , { SkSVGAttribute::kViewBox       , SetViewBoxAttribute   }},
-    { "width"          , { SkSVGAttribute::kWidth         , SetLengthAttribute    }},
-    { "x"              , { SkSVGAttribute::kX             , SetLengthAttribute    }},
-    { "x1"             , { SkSVGAttribute::kX1            , SetLengthAttribute    }},
-    { "x2"             , { SkSVGAttribute::kX2            , SetLengthAttribute    }},
-    { "xlink:href"     , { SkSVGAttribute::kHref          , SetIRIAttribute       }},
-    { "y"              , { SkSVGAttribute::kY             , SetLengthAttribute    }},
-    { "y1"             , { SkSVGAttribute::kY1            , SetLengthAttribute    }},
-    { "y2"             , { SkSVGAttribute::kY2            , SetLengthAttribute    }},
+    { "cx"             , { SkSVGAttribute::kCx            , SetLengthAttribute       }},
+    { "cy"             , { SkSVGAttribute::kCy            , SetLengthAttribute       }},
+    { "d"              , { SkSVGAttribute::kD             , SetPathDataAttribute     }},
+    { "fill"           , { SkSVGAttribute::kFill          , SetPaintAttribute        }},
+    { "fill-opacity"   , { SkSVGAttribute::kFillOpacity   , SetNumberAttribute       }},
+    { "height"         , { SkSVGAttribute::kHeight        , SetLengthAttribute       }},
+    { "offset"         , { SkSVGAttribute::kOffset        , SetLengthAttribute       }},
+    { "opacity"        , { SkSVGAttribute::kOpacity       , SetNumberAttribute       }},
+    { "points"         , { SkSVGAttribute::kPoints        , SetPointsAttribute       }},
+    { "r"              , { SkSVGAttribute::kR             , SetLengthAttribute       }},
+    { "rx"             , { SkSVGAttribute::kRx            , SetLengthAttribute       }},
+    { "ry"             , { SkSVGAttribute::kRy            , SetLengthAttribute       }},
+    { "spreadMethod"   , { SkSVGAttribute::kSpreadMethod  , SetSpreadMethodAttribute }},
+    { "stop-color"     , { SkSVGAttribute::kStopColor     , SetColorAttribute        }},
+    { "stop-opacity"   , { SkSVGAttribute::kStopOpacity   , SetNumberAttribute       }},
+    { "stroke"         , { SkSVGAttribute::kStroke        , SetPaintAttribute        }},
+    { "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute      }},
+    { "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute     }},
+    { "stroke-opacity" , { SkSVGAttribute::kStrokeOpacity , SetNumberAttribute       }},
+    { "stroke-width"   , { SkSVGAttribute::kStrokeWidth   , SetLengthAttribute       }},
+    { "style"          , { SkSVGAttribute::kUnknown       , SetStyleAttributes       }},
+    { "transform"      , { SkSVGAttribute::kTransform     , SetTransformAttribute    }},
+    { "viewBox"        , { SkSVGAttribute::kViewBox       , SetViewBoxAttribute      }},
+    { "width"          , { SkSVGAttribute::kWidth         , SetLengthAttribute       }},
+    { "x"              , { SkSVGAttribute::kX             , SetLengthAttribute       }},
+    { "x1"             , { SkSVGAttribute::kX1            , SetLengthAttribute       }},
+    { "x2"             , { SkSVGAttribute::kX2            , SetLengthAttribute       }},
+    { "xlink:href"     , { SkSVGAttribute::kHref          , SetIRIAttribute          }},
+    { "y"              , { SkSVGAttribute::kY             , SetLengthAttribute       }},
+    { "y1"             , { SkSVGAttribute::kY1            , SetLengthAttribute       }},
+    { "y2"             , { SkSVGAttribute::kY2            , SetLengthAttribute       }},
 };
 
 SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
index 20c27f5..289c5e3 100644 (file)
@@ -17,6 +17,10 @@ void SkSVGLinearGradient::setHref(const SkSVGStringType& href) {
     fHref = std::move(href);
 }
 
+void SkSVGLinearGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) {
+    fSpreadMethod = spread;
+}
+
 void SkSVGLinearGradient::setX1(const SkSVGLength& x1) {
     fX1 = x1;
 }
@@ -40,6 +44,11 @@ void SkSVGLinearGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue&
             this->setHref(*href);
         }
         break;
+    case SkSVGAttribute::kSpreadMethod:
+        if (const auto* spread = v.as<SkSVGSpreadMethodValue>()) {
+            this->setSpreadMethod(*spread);
+        }
+        break;
     case SkSVGAttribute::kX1:
         if (const auto* x1 = v.as<SkSVGLengthValue>()) {
             this->setX1(*x1);
@@ -110,10 +119,17 @@ bool SkSVGLinearGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* pain
     //       * stop (lazy?) sorting
     //       * href loop detection
     //       * href attribute inheritance (not just color stops)
-    //       * spreadMethods support
     //       * objectBoundingBox units support
 
+    static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kPad) ==
+                  SkShader::kClamp_TileMode, "SkSVGSpreadMethod::Type is out of sync");
+    static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kRepeat) ==
+                  SkShader::kRepeat_TileMode, "SkSVGSpreadMethod::Type is out of sync");
+    static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kReflect) ==
+                  SkShader::kMirror_TileMode, "SkSVGSpreadMethod::Type is out of sync");
+    const auto tileMode = static_cast<SkShader::TileMode>(fSpreadMethod.type());
+
     paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(),
-                                                  SkShader::kClamp_TileMode));
+                                                  tileMode));
     return true;
 }
index 1a2e332..e12b524 100644 (file)
@@ -19,6 +19,7 @@ public:
     }
 
     void setHref(const SkSVGStringType&);
+    void setSpreadMethod(const SkSVGSpreadMethod&);
     void setX1(const SkSVGLength&);
     void setY1(const SkSVGLength&);
     void setX2(const SkSVGLength&);
@@ -41,7 +42,8 @@ private:
     SkSVGLength fX2 = SkSVGLength(100, SkSVGLength::Unit::kPercentage);
     SkSVGLength fY2 = SkSVGLength(0  , SkSVGLength::Unit::kPercentage);
 
-    SkSVGStringType fHref;
+    SkSVGStringType   fHref;
+    SkSVGSpreadMethod fSpreadMethod = SkSVGSpreadMethod(SkSVGSpreadMethod::Type::kPad);
 
     typedef SkSVGHiddenContainer INHERITED;
 };
index b2343a1..b07f9a2 100644 (file)
@@ -167,4 +167,28 @@ private:
     Type fType;
 };
 
+class SkSVGSpreadMethod {
+public:
+    // These values must match Skia's SkShader::TileMode enum.
+    enum class Type {
+        kPad,       // kClamp_TileMode
+        kRepeat,    // kRepeat_TileMode
+        kReflect,   // kMirror_TileMode
+    };
+
+    constexpr SkSVGSpreadMethod() : fType(Type::kPad) {}
+    constexpr explicit SkSVGSpreadMethod(Type t) : fType(t) {}
+
+    SkSVGSpreadMethod(const SkSVGSpreadMethod&)            = default;
+    SkSVGSpreadMethod& operator=(const SkSVGSpreadMethod&) = default;
+
+    bool operator==(const SkSVGSpreadMethod& other) const { return fType == other.fType; }
+    bool operator!=(const SkSVGSpreadMethod& other) const { return !(*this == other); }
+
+    Type type() const { return fType; }
+
+private:
+    Type fType;
+};
+
 #endif // SkSVGTypes_DEFINED
index e4673e9..0160af0 100644 (file)
@@ -25,6 +25,7 @@ public:
         kPaint,
         kPath,
         kPoints,
+        kSpreadMethod,
         kString,
         kTransform,
         kViewBox,
@@ -68,16 +69,18 @@ private:
     typedef SkSVGValue INHERITED;
 };
 
-using SkSVGColorValue     = SkSVGWrapperValue<SkSVGColorType    , SkSVGValue::Type::kColor    >;
-using SkSVGLengthValue    = SkSVGWrapperValue<SkSVGLength       , SkSVGValue::Type::kLength   >;
-using SkSVGPathValue      = SkSVGWrapperValue<SkPath            , SkSVGValue::Type::kPath     >;
-using SkSVGTransformValue = SkSVGWrapperValue<SkSVGTransformType, SkSVGValue::Type::kTransform>;
-using SkSVGViewBoxValue   = SkSVGWrapperValue<SkSVGViewBoxType  , SkSVGValue::Type::kViewBox  >;
-using SkSVGPaintValue     = SkSVGWrapperValue<SkSVGPaint        , SkSVGValue::Type::kPaint    >;
-using SkSVGLineCapValue   = SkSVGWrapperValue<SkSVGLineCap      , SkSVGValue::Type::kLineCap  >;
-using SkSVGLineJoinValue  = SkSVGWrapperValue<SkSVGLineJoin     , SkSVGValue::Type::kLineJoin >;
-using SkSVGNumberValue    = SkSVGWrapperValue<SkSVGNumberType   , SkSVGValue::Type::kNumber   >;
-using SkSVGPointsValue    = SkSVGWrapperValue<SkSVGPointsType   , SkSVGValue::Type::kPoints   >;
-using SkSVGStringValue    = SkSVGWrapperValue<SkSVGStringType   , SkSVGValue::Type::kString   >;
+using SkSVGColorValue        = SkSVGWrapperValue<SkSVGColorType    , SkSVGValue::Type::kColor    >;
+using SkSVGLengthValue       = SkSVGWrapperValue<SkSVGLength       , SkSVGValue::Type::kLength   >;
+using SkSVGPathValue         = SkSVGWrapperValue<SkPath            , SkSVGValue::Type::kPath     >;
+using SkSVGTransformValue    = SkSVGWrapperValue<SkSVGTransformType, SkSVGValue::Type::kTransform>;
+using SkSVGViewBoxValue      = SkSVGWrapperValue<SkSVGViewBoxType  , SkSVGValue::Type::kViewBox  >;
+using SkSVGPaintValue        = SkSVGWrapperValue<SkSVGPaint        , SkSVGValue::Type::kPaint    >;
+using SkSVGLineCapValue      = SkSVGWrapperValue<SkSVGLineCap      , SkSVGValue::Type::kLineCap  >;
+using SkSVGLineJoinValue     = SkSVGWrapperValue<SkSVGLineJoin     , SkSVGValue::Type::kLineJoin >;
+using SkSVGNumberValue       = SkSVGWrapperValue<SkSVGNumberType   , SkSVGValue::Type::kNumber   >;
+using SkSVGPointsValue       = SkSVGWrapperValue<SkSVGPointsType   , SkSVGValue::Type::kPoints   >;
+using SkSVGStringValue       = SkSVGWrapperValue<SkSVGStringType   , SkSVGValue::Type::kString   >;
+using SkSVGSpreadMethodValue = SkSVGWrapperValue<SkSVGSpreadMethod ,
+                                                 SkSVGValue::Type::kSpreadMethod>;
 
 #endif // SkSVGValue_DEFINED