From cecd617a4297ffdcec632ca7e2ed1e6b61665e04 Mon Sep 17 00:00:00 2001 From: fmalita Date: Tue, 13 Sep 2016 12:56:11 -0700 Subject: [SVGDom] Linear gradient 'spreadMethod' support 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 | 1 + experimental/svg/model/SkSVGAttributeParser.cpp | 23 ++++++++ experimental/svg/model/SkSVGAttributeParser.h | 1 + experimental/svg/model/SkSVGDOM.cpp | 73 +++++++++++++++---------- experimental/svg/model/SkSVGLinearGradient.cpp | 20 ++++++- experimental/svg/model/SkSVGLinearGradient.h | 4 +- experimental/svg/model/SkSVGTypes.h | 24 ++++++++ experimental/svg/model/SkSVGValue.h | 25 +++++---- 8 files changed, 127 insertions(+), 44 deletions(-) (limited to 'experimental/svg') diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index ce06b1e8a6..13a6041b4b 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -27,6 +27,7 @@ enum class SkSVGAttribute { kR, // : radius kRx, // ,: horizontal (corner) radius kRy, // ,: vertical (corner) radius + kSpreadMethod, kStopColor, kStopOpacity, kStroke, diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp index 9d2d6b8136..04b050828a 100644 --- a/experimental/svg/model/SkSVGAttributeParser.cpp +++ b/experimental/svg/model/SkSVGAttributeParser.cpp @@ -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 pts; diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h index c1700a8e77..9d6939b2e2 100644 --- a/experimental/svg/model/SkSVGAttributeParser.h +++ b/experimental/svg/model/SkSVGAttributeParser.h @@ -24,6 +24,7 @@ public: bool parseLineJoin(SkSVGLineJoin*); bool parsePoints(SkSVGPointsType*); bool parseIRI(SkSVGStringType*); + bool parseSpreadMethod(SkSVGSpreadMethod*); private: // Stack-only diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index d6e4f4c9bd..26944737f1 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -149,6 +149,18 @@ bool SetLineJoinAttribute(const sk_sp& node, SkSVGAttribute attr, return true; } +bool SetSpreadMethodAttribute(const sk_sp& 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& node, SkSVGAttribute attr, const char* stringValue) { SkSVGPointsType points; @@ -239,36 +251,37 @@ struct AttrParseInfo { }; SortedDictionaryEntry 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(*)()> gTagFactories[] = { diff --git a/experimental/svg/model/SkSVGLinearGradient.cpp b/experimental/svg/model/SkSVGLinearGradient.cpp index 20c27f540c..289c5e3276 100644 --- a/experimental/svg/model/SkSVGLinearGradient.cpp +++ b/experimental/svg/model/SkSVGLinearGradient.cpp @@ -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()) { + this->setSpreadMethod(*spread); + } + break; case SkSVGAttribute::kX1: if (const auto* x1 = v.as()) { 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(SkSVGSpreadMethod::Type::kPad) == + SkShader::kClamp_TileMode, "SkSVGSpreadMethod::Type is out of sync"); + static_assert(static_cast(SkSVGSpreadMethod::Type::kRepeat) == + SkShader::kRepeat_TileMode, "SkSVGSpreadMethod::Type is out of sync"); + static_assert(static_cast(SkSVGSpreadMethod::Type::kReflect) == + SkShader::kMirror_TileMode, "SkSVGSpreadMethod::Type is out of sync"); + const auto tileMode = static_cast(fSpreadMethod.type()); + paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(), - SkShader::kClamp_TileMode)); + tileMode)); return true; } diff --git a/experimental/svg/model/SkSVGLinearGradient.h b/experimental/svg/model/SkSVGLinearGradient.h index 1a2e332baf..e12b5245e8 100644 --- a/experimental/svg/model/SkSVGLinearGradient.h +++ b/experimental/svg/model/SkSVGLinearGradient.h @@ -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; }; diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h index b2343a15e4..b07f9a2c8c 100644 --- a/experimental/svg/model/SkSVGTypes.h +++ b/experimental/svg/model/SkSVGTypes.h @@ -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 diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h index e4673e9150..0160af0371 100644 --- a/experimental/svg/model/SkSVGValue.h +++ b/experimental/svg/model/SkSVGValue.h @@ -25,6 +25,7 @@ public: kPaint, kPath, kPoints, + kSpreadMethod, kString, kTransform, kViewBox, @@ -68,16 +69,18 @@ private: typedef SkSVGValue INHERITED; }; -using SkSVGColorValue = SkSVGWrapperValue; -using SkSVGLengthValue = SkSVGWrapperValue; -using SkSVGPathValue = SkSVGWrapperValue; -using SkSVGTransformValue = SkSVGWrapperValue; -using SkSVGViewBoxValue = SkSVGWrapperValue; -using SkSVGPaintValue = SkSVGWrapperValue; -using SkSVGLineCapValue = SkSVGWrapperValue; -using SkSVGLineJoinValue = SkSVGWrapperValue; -using SkSVGNumberValue = SkSVGWrapperValue; -using SkSVGPointsValue = SkSVGWrapperValue; -using SkSVGStringValue = SkSVGWrapperValue; +using SkSVGColorValue = SkSVGWrapperValue; +using SkSVGLengthValue = SkSVGWrapperValue; +using SkSVGPathValue = SkSVGWrapperValue; +using SkSVGTransformValue = SkSVGWrapperValue; +using SkSVGViewBoxValue = SkSVGWrapperValue; +using SkSVGPaintValue = SkSVGWrapperValue; +using SkSVGLineCapValue = SkSVGWrapperValue; +using SkSVGLineJoinValue = SkSVGWrapperValue; +using SkSVGNumberValue = SkSVGWrapperValue; +using SkSVGPointsValue = SkSVGWrapperValue; +using SkSVGStringValue = SkSVGWrapperValue; +using SkSVGSpreadMethodValue = SkSVGWrapperValue; #endif // SkSVGValue_DEFINED -- cgit v1.2.3