diff options
Diffstat (limited to 'experimental/svg')
25 files changed, 135 insertions, 17 deletions
diff --git a/experimental/svg/model/SkSVGAttribute.cpp b/experimental/svg/model/SkSVGAttribute.cpp index 9d6b193e04..4935d6afcf 100644 --- a/experimental/svg/model/SkSVGAttribute.cpp +++ b/experimental/svg/model/SkSVGAttribute.cpp @@ -12,6 +12,7 @@ SkSVGPresentationAttributes SkSVGPresentationAttributes::MakeInitial() { result.fFill.set(SkSVGPaint(SkSVGColorType(SK_ColorBLACK))); result.fFillOpacity.set(SkSVGNumberType(1)); + result.fFillRule.set(SkSVGFillRule(SkSVGFillRule::Type::kNonZero)); result.fStroke.set(SkSVGPaint(SkSVGPaint::Type::kNone)); result.fStrokeLineCap.set(SkSVGLineCap(SkSVGLineCap::Type::kButt)); diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index 478a26b261..93c5f99153 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -19,6 +19,7 @@ enum class SkSVGAttribute { kD, kFill, kFillOpacity, + kFillRule, kGradientTransform, kHeight, kHref, @@ -56,6 +57,7 @@ struct SkSVGPresentationAttributes { SkTLazy<SkSVGPaint> fFill; SkTLazy<SkSVGNumberType> fFillOpacity; + SkTLazy<SkSVGFillRule> fFillRule; SkTLazy<SkSVGPaint> fStroke; SkTLazy<SkSVGLineCap> fStrokeLineCap; diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp index 04b050828a..5ca317ccc3 100644 --- a/experimental/svg/model/SkSVGAttributeParser.cpp +++ b/experimental/svg/model/SkSVGAttributeParser.cpp @@ -551,3 +551,26 @@ bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) { return false; } + +// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty +bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) { + static const struct { + SkSVGFillRule::Type fType; + const char* fName; + } gFillRuleInfo[] = { + { SkSVGFillRule::Type::kNonZero, "nonzero" }, + { SkSVGFillRule::Type::kEvenOdd, "evenodd" }, + { SkSVGFillRule::Type::kInherit, "inherit" }, + }; + + bool parsedValue = false; + for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) { + if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) { + *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h index 9d6939b2e2..2ffa79f543 100644 --- a/experimental/svg/model/SkSVGAttributeParser.h +++ b/experimental/svg/model/SkSVGAttributeParser.h @@ -15,6 +15,7 @@ public: SkSVGAttributeParser(const char[]); bool parseColor(SkSVGColorType*); + bool parseFillRule(SkSVGFillRule*); bool parseNumber(SkSVGNumberType*); bool parseLength(SkSVGLength*); bool parseViewBox(SkSVGViewBoxType*); diff --git a/experimental/svg/model/SkSVGCircle.cpp b/experimental/svg/model/SkSVGCircle.cpp index 692cd9ff54..9d8117379a 100644 --- a/experimental/svg/model/SkSVGCircle.cpp +++ b/experimental/svg/model/SkSVGCircle.cpp @@ -47,7 +47,7 @@ void SkSVGCircle::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } void SkSVGCircle::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint) const { + const SkPaint& paint, SkPath::FillType) const { const auto cx = lctx.resolve(fCx, SkSVGLengthContext::LengthType::kHorizontal); const auto cy = lctx.resolve(fCy, SkSVGLengthContext::LengthType::kVertical); const auto r = lctx.resolve(fR , SkSVGLengthContext::LengthType::kOther); diff --git a/experimental/svg/model/SkSVGCircle.h b/experimental/svg/model/SkSVGCircle.h index ef1bd2aadb..02aaad4891 100644 --- a/experimental/svg/model/SkSVGCircle.h +++ b/experimental/svg/model/SkSVGCircle.h @@ -23,7 +23,8 @@ public: protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; - void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const override; private: SkSVGCircle(); diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index c9745f1730..2120f80e36 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -173,6 +173,18 @@ bool SetPointsAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, return true; } +bool SetFillRuleAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGFillRule fillRule; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseFillRule(&fillRule)) { + return false; + } + + node->setAttribute(attr, SkSVGFillRuleValue(fillRule)); + return true; +} + SkString TrimmedString(const char* first, const char* last) { SkASSERT(first); SkASSERT(last); @@ -256,6 +268,7 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = { { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }}, { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }}, { "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }}, + { "fill-rule" , { SkSVGAttribute::kFillRule , SetFillRuleAttribute }}, { "gradientTransform", { SkSVGAttribute::kGradientTransform, SetTransformAttribute }}, { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, { "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }}, diff --git a/experimental/svg/model/SkSVGEllipse.cpp b/experimental/svg/model/SkSVGEllipse.cpp index f7461b8cd7..481af5c3ab 100644 --- a/experimental/svg/model/SkSVGEllipse.cpp +++ b/experimental/svg/model/SkSVGEllipse.cpp @@ -56,7 +56,7 @@ void SkSVGEllipse::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } void SkSVGEllipse::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint) const { + const SkPaint& paint, SkPath::FillType) const { const auto cx = lctx.resolve(fCx, SkSVGLengthContext::LengthType::kHorizontal); const auto cy = lctx.resolve(fCy, SkSVGLengthContext::LengthType::kVertical); const auto rx = lctx.resolve(fRx, SkSVGLengthContext::LengthType::kHorizontal); diff --git a/experimental/svg/model/SkSVGEllipse.h b/experimental/svg/model/SkSVGEllipse.h index 640d7f6f26..0042b3364e 100644 --- a/experimental/svg/model/SkSVGEllipse.h +++ b/experimental/svg/model/SkSVGEllipse.h @@ -24,7 +24,8 @@ public: protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; - void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const override; private: SkSVGEllipse(); diff --git a/experimental/svg/model/SkSVGLine.cpp b/experimental/svg/model/SkSVGLine.cpp index 030aad8db4..27cdd469d2 100644 --- a/experimental/svg/model/SkSVGLine.cpp +++ b/experimental/svg/model/SkSVGLine.cpp @@ -56,7 +56,7 @@ void SkSVGLine::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } void SkSVGLine::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint) const { + const SkPaint& paint, SkPath::FillType) const { const auto x1 = lctx.resolve(fX1, SkSVGLengthContext::LengthType::kHorizontal); const auto y1 = lctx.resolve(fY1, SkSVGLengthContext::LengthType::kVertical); const auto x2 = lctx.resolve(fX2, SkSVGLengthContext::LengthType::kHorizontal); diff --git a/experimental/svg/model/SkSVGLine.h b/experimental/svg/model/SkSVGLine.h index c2716e00e2..524fc2ac0e 100644 --- a/experimental/svg/model/SkSVGLine.h +++ b/experimental/svg/model/SkSVGLine.h @@ -24,7 +24,8 @@ public: protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; - void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const override; private: SkSVGLine(); diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp index f40ee5dadb..a5c8147554 100644 --- a/experimental/svg/model/SkSVGNode.cpp +++ b/experimental/svg/model/SkSVGNode.cpp @@ -49,6 +49,10 @@ void SkSVGNode::setFillOpacity(const SkSVGNumberType& opacity) { SkSVGNumberType(SkTPin<SkScalar>(opacity.value(), 0, 1))); } +void SkSVGNode::setFillRule(const SkSVGFillRule& fillRule) { + fPresentationAttributes.fFillRule.set(fillRule); +} + void SkSVGNode::setOpacity(const SkSVGNumberType& opacity) { fPresentationAttributes.fOpacity.set( SkSVGNumberType(SkTPin<SkScalar>(opacity.value(), 0, 1))); @@ -79,6 +83,11 @@ void SkSVGNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { this->setFillOpacity(*opacity); } break; + case SkSVGAttribute::kFillRule: + if (const SkSVGFillRuleValue* fillRule = v.as<SkSVGFillRuleValue>()) { + this->setFillRule(*fillRule); + } + break; case SkSVGAttribute::kOpacity: if (const SkSVGNumberValue* opacity = v.as<SkSVGNumberValue>()) { this->setOpacity(*opacity); diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h index 2a7c836413..1e092a4d87 100644 --- a/experimental/svg/model/SkSVGNode.h +++ b/experimental/svg/model/SkSVGNode.h @@ -47,6 +47,7 @@ public: void setFill(const SkSVGPaint&); void setFillOpacity(const SkSVGNumberType&); + void setFillRule(const SkSVGFillRule&); void setOpacity(const SkSVGNumberType&); void setStroke(const SkSVGPaint&); void setStrokeOpacity(const SkSVGNumberType&); diff --git a/experimental/svg/model/SkSVGPath.cpp b/experimental/svg/model/SkSVGPath.cpp index 07e0a3e545..dd248237f1 100644 --- a/experimental/svg/model/SkSVGPath.cpp +++ b/experimental/svg/model/SkSVGPath.cpp @@ -25,6 +25,9 @@ void SkSVGPath::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } } -void SkSVGPath::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint) const { +void SkSVGPath::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint, + SkPath::FillType fillType) const { + // the passed fillType follows inheritance rules and needs to be applied at draw time. + fPath.setFillType(fillType); canvas->drawPath(fPath, paint); } diff --git a/experimental/svg/model/SkSVGPath.h b/experimental/svg/model/SkSVGPath.h index e72f0d137e..8297a8d557 100644 --- a/experimental/svg/model/SkSVGPath.h +++ b/experimental/svg/model/SkSVGPath.h @@ -21,12 +21,13 @@ public: protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; - void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const override; private: SkSVGPath(); - SkPath fPath; + mutable SkPath fPath; // mutated in onDraw(), to apply inherited fill types. typedef SkSVGShape INHERITED; }; diff --git a/experimental/svg/model/SkSVGPoly.cpp b/experimental/svg/model/SkSVGPoly.cpp index bcc716f362..826ca2457e 100644 --- a/experimental/svg/model/SkSVGPoly.cpp +++ b/experimental/svg/model/SkSVGPoly.cpp @@ -6,6 +6,7 @@ */ #include "SkCanvas.h" +#include "SkTLazy.h" #include "SkSVGRenderContext.h" #include "SkSVGPoly.h" #include "SkSVGValue.h" @@ -31,6 +32,9 @@ void SkSVGPoly::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } } -void SkSVGPoly::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint) const { +void SkSVGPoly::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint, + SkPath::FillType fillType) const { + // the passed fillType follows inheritance rules and needs to be applied at draw time. + fPath.setFillType(fillType); canvas->drawPath(fPath, paint); } diff --git a/experimental/svg/model/SkSVGPoly.h b/experimental/svg/model/SkSVGPoly.h index 3ae8dc6f02..f75d3626cc 100644 --- a/experimental/svg/model/SkSVGPoly.h +++ b/experimental/svg/model/SkSVGPoly.h @@ -29,12 +29,13 @@ public: protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; - void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const override; private: SkSVGPoly(SkSVGTag); - SkPath fPath; + mutable SkPath fPath; // mutated in onDraw(), to apply inherited fill types. typedef SkSVGShape INHERITED; }; diff --git a/experimental/svg/model/SkSVGRect.cpp b/experimental/svg/model/SkSVGRect.cpp index cbb18306a5..1afd9957c6 100644 --- a/experimental/svg/model/SkSVGRect.cpp +++ b/experimental/svg/model/SkSVGRect.cpp @@ -75,7 +75,7 @@ void SkSVGRect::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } void SkSVGRect::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint) const { + const SkPaint& paint, SkPath::FillType) const { const SkRect rect = lctx.resolveRect(fX, fY, fWidth, fHeight); const SkScalar rx = lctx.resolve(fRx, SkSVGLengthContext::LengthType::kHorizontal); const SkScalar ry = lctx.resolve(fRy, SkSVGLengthContext::LengthType::kVertical); diff --git a/experimental/svg/model/SkSVGRect.h b/experimental/svg/model/SkSVGRect.h index 0da248c4eb..affa65f610 100644 --- a/experimental/svg/model/SkSVGRect.h +++ b/experimental/svg/model/SkSVGRect.h @@ -26,7 +26,8 @@ public: protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; - void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const override; private: SkSVGRect(); diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp index 147d64edb3..fd0262319a 100644 --- a/experimental/svg/model/SkSVGRenderContext.cpp +++ b/experimental/svg/model/SkSVGRenderContext.cpp @@ -6,6 +6,7 @@ */ #include "SkCanvas.h" +#include "SkPath.h" #include "SkSVGAttribute.h" #include "SkSVGNode.h" #include "SkSVGRenderContext.h" @@ -188,6 +189,13 @@ void commitToPaint<SkSVGAttribute::kStrokeWidth>(const SkSVGPresentationAttribut pctx->fStrokePaint.setStrokeWidth(strokeWidth); } +template <> +void commitToPaint<SkSVGAttribute::kFillRule>(const SkSVGPresentationAttributes&, + const SkSVGRenderContext&, + SkSVGPresentationContext*) { + // Not part of the SkPaint state; applied to the path at render time. +} + } // anonymous ns SkSVGPresentationContext::SkSVGPresentationContext() @@ -258,6 +266,7 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr ApplyLazyInheritedAttribute(Fill); ApplyLazyInheritedAttribute(FillOpacity); + ApplyLazyInheritedAttribute(FillRule); ApplyLazyInheritedAttribute(Stroke); ApplyLazyInheritedAttribute(StrokeLineCap); ApplyLazyInheritedAttribute(StrokeLineJoin); diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h index 393c2849eb..9d2fb3a621 100644 --- a/experimental/svg/model/SkSVGRenderContext.h +++ b/experimental/svg/model/SkSVGRenderContext.h @@ -65,6 +65,8 @@ public: const SkSVGLengthContext& lengthContext() const { return *fLengthContext; } SkSVGLengthContext* writableLengthContext() { return fLengthContext.writable(); } + const SkSVGPresentationContext& presentationContext() const { return *fPresentationContext; } + SkCanvas* canvas() const { return fCanvas; } enum ApplyFlags { diff --git a/experimental/svg/model/SkSVGShape.cpp b/experimental/svg/model/SkSVGShape.cpp index 38af4c9303..9351a2f096 100644 --- a/experimental/svg/model/SkSVGShape.cpp +++ b/experimental/svg/model/SkSVGShape.cpp @@ -11,16 +11,31 @@ SkSVGShape::SkSVGShape(SkSVGTag t) : INHERITED(t) {} void SkSVGShape::onRender(const SkSVGRenderContext& ctx) const { + const SkPath::FillType fillType = + FillRuleToFillType(*ctx.presentationContext().fInherited.fFillRule.get()); + // TODO: this approach forces duplicate geometry resolution in onDraw(); refactor to avoid. if (const SkPaint* fillPaint = ctx.fillPaint()) { - this->onDraw(ctx.canvas(), ctx.lengthContext(), *fillPaint); + this->onDraw(ctx.canvas(), ctx.lengthContext(), *fillPaint, fillType); } if (const SkPaint* strokePaint = ctx.strokePaint()) { - this->onDraw(ctx.canvas(), ctx.lengthContext(), *strokePaint); + this->onDraw(ctx.canvas(), ctx.lengthContext(), *strokePaint, fillType); } } void SkSVGShape::appendChild(sk_sp<SkSVGNode>) { SkDebugf("cannot append child nodes to an SVG shape.\n"); } + +SkPath::FillType SkSVGShape::FillRuleToFillType(const SkSVGFillRule& fillRule) { + switch (fillRule.type()) { + case SkSVGFillRule::Type::kNonZero: + return SkPath::kWinding_FillType; + case SkSVGFillRule::Type::kEvenOdd: + return SkPath::kEvenOdd_FillType; + default: + SkASSERT(false); + return SkPath::kWinding_FillType; + } +} diff --git a/experimental/svg/model/SkSVGShape.h b/experimental/svg/model/SkSVGShape.h index 1f3c4555c3..48b2ead0c1 100644 --- a/experimental/svg/model/SkSVGShape.h +++ b/experimental/svg/model/SkSVGShape.h @@ -8,6 +8,7 @@ #ifndef SkSVGShape_DEFINED #define SkSVGShape_DEFINED +#include "SkPath.h" #include "SkSVGTransformableNode.h" class SkSVGLengthContext; @@ -24,7 +25,10 @@ protected: void onRender(const SkSVGRenderContext&) const final; - virtual void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const = 0; + virtual void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, + SkPath::FillType) const = 0; + + static SkPath::FillType FillRuleToFillType(const SkSVGFillRule&); private: typedef SkSVGTransformableNode INHERITED; diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h index b07f9a2c8c..d5d2b7146f 100644 --- a/experimental/svg/model/SkSVGTypes.h +++ b/experimental/svg/model/SkSVGTypes.h @@ -191,4 +191,27 @@ private: Type fType; }; +class SkSVGFillRule { +public: + enum class Type { + kNonZero, + kEvenOdd, + kInherit, + }; + + constexpr SkSVGFillRule() : fType(Type::kInherit) {} + constexpr explicit SkSVGFillRule(Type t) : fType(t) {} + + SkSVGFillRule(const SkSVGFillRule&) = default; + SkSVGFillRule& operator=(const SkSVGFillRule&) = default; + + bool operator==(const SkSVGFillRule& other) const { return fType == other.fType; } + bool operator!=(const SkSVGFillRule& 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 0160af0371..9f3095c918 100644 --- a/experimental/svg/model/SkSVGValue.h +++ b/experimental/svg/model/SkSVGValue.h @@ -18,6 +18,7 @@ class SkSVGValue : public SkNoncopyable { public: enum class Type { kColor, + kFillRule, kLength, kLineCap, kLineJoin, @@ -70,6 +71,7 @@ private: }; using SkSVGColorValue = SkSVGWrapperValue<SkSVGColorType , SkSVGValue::Type::kColor >; +using SkSVGFillRuleValue = SkSVGWrapperValue<SkSVGFillRule , SkSVGValue::Type::kFillRule >; using SkSVGLengthValue = SkSVGWrapperValue<SkSVGLength , SkSVGValue::Type::kLength >; using SkSVGPathValue = SkSVGWrapperValue<SkPath , SkSVGValue::Type::kPath >; using SkSVGTransformValue = SkSVGWrapperValue<SkSVGTransformType, SkSVGValue::Type::kTransform>; |