diff options
author | Florin Malita <fmalita@chromium.org> | 2016-12-08 09:26:47 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-12-08 15:00:18 +0000 |
commit | ce8840e3842f3a702c5d7bf440ff730bdfaf8e70 (patch) | |
tree | 0a94cdeb3d2d8632644490b70fdb0c40d185b8ec /experimental/svg | |
parent | c5a8366d9922902cffbd351fdd15755dd2ac99bf (diff) |
[SVGDom] ClipPath support
* clip-path attribute handling
* clipPath container element
* asPath() SkSVGNode virtual for capturing subtree geometry
R=robertphillips@google.com,stephana@google.com
Change-Id: I9597534fe3047b631da6309eafac055dff5696e9
Reviewed-on: https://skia-review.googlesource.com/5650
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
Diffstat (limited to 'experimental/svg')
28 files changed, 312 insertions, 20 deletions
diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index 93c5f99153..2900a03f7c 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -14,6 +14,7 @@ class SkSVGRenderContext; enum class SkSVGAttribute { + kClipPath, kCx, // <circle>,<ellipse>: center x position kCy, // <circle>,<ellipse>: center y position kD, @@ -65,7 +66,9 @@ struct SkSVGPresentationAttributes { SkTLazy<SkSVGNumberType> fStrokeOpacity; SkTLazy<SkSVGLength> fStrokeWidth; + // uninherited SkTLazy<SkSVGNumberType> fOpacity; + SkTLazy<SkSVGClip> fClipPath; }; #endif // SkSVGAttribute_DEFINED diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp index 5ca317ccc3..f8eef8cfa7 100644 --- a/experimental/svg/model/SkSVGAttributeParser.cpp +++ b/experimental/svg/model/SkSVGAttributeParser.cpp @@ -443,6 +443,25 @@ bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) { return parsedValue && this->parseEOSToken(); } +// https://www.w3.org/TR/SVG/masking.html#ClipPathProperty +bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) { + SkSVGStringType iri; + bool parsedValue = false; + + if (this->parseExpectedStringToken("none")) { + *clip = SkSVGClip(SkSVGClip::Type::kNone); + parsedValue = true; + } else if (this->parseExpectedStringToken("inherit")) { + *clip = SkSVGClip(SkSVGClip::Type::kInherit); + parsedValue = true; + } else if (this->parseFuncIRI(&iri)) { + *clip = SkSVGClip(iri.value()); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + // https://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) { static const struct { diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h index 2ffa79f543..67c13f6b7e 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 parseClipPath(SkSVGClip*); bool parseFillRule(SkSVGFillRule*); bool parseNumber(SkSVGNumberType*); bool parseLength(SkSVGLength*); diff --git a/experimental/svg/model/SkSVGCircle.cpp b/experimental/svg/model/SkSVGCircle.cpp index 9d8117379a..49348795ab 100644 --- a/experimental/svg/model/SkSVGCircle.cpp +++ b/experimental/svg/model/SkSVGCircle.cpp @@ -46,13 +46,32 @@ void SkSVGCircle::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } } -void SkSVGCircle::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint, SkPath::FillType) const { +std::tuple<SkPoint, SkScalar> SkSVGCircle::resolve(const SkSVGLengthContext& lctx) 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); + return std::make_tuple(SkPoint::Make(cx, cy), r); +} +void SkSVGCircle::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, + const SkPaint& paint, SkPath::FillType) const { + SkPoint pos; + SkScalar r; + std::tie(pos, r) = this->resolve(lctx); + if (r > 0) { - canvas->drawCircle(cx, cy, r, paint); + canvas->drawCircle(pos.x(), pos.y(), r, paint); } } + +SkPath SkSVGCircle::onAsPath(const SkSVGRenderContext& ctx) const { + SkPoint pos; + SkScalar r; + std::tie(pos, r) = this->resolve(ctx.lengthContext()); + + SkPath path; + path.addCircle(pos.x(), pos.y(), r); + this->mapToParent(&path); + + return path; +} diff --git a/experimental/svg/model/SkSVGCircle.h b/experimental/svg/model/SkSVGCircle.h index 02aaad4891..16ee54d280 100644 --- a/experimental/svg/model/SkSVGCircle.h +++ b/experimental/svg/model/SkSVGCircle.h @@ -11,6 +11,8 @@ #include "SkSVGShape.h" #include "SkSVGTypes.h" +struct SkPoint; + class SkSVGCircle final : public SkSVGShape { public: virtual ~SkSVGCircle() = default; @@ -26,9 +28,14 @@ protected: void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, SkPath::FillType) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + private: SkSVGCircle(); + // resolve and return the center and radius values + std::tuple<SkPoint, SkScalar> resolve(const SkSVGLengthContext&) const; + SkSVGLength fCx = SkSVGLength(0); SkSVGLength fCy = SkSVGLength(0); SkSVGLength fR = SkSVGLength(0); diff --git a/experimental/svg/model/SkSVGClipPath.cpp b/experimental/svg/model/SkSVGClipPath.cpp new file mode 100644 index 0000000000..60f1b6a347 --- /dev/null +++ b/experimental/svg/model/SkSVGClipPath.cpp @@ -0,0 +1,10 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSVGClipPath.h" + +SkSVGClipPath::SkSVGClipPath() : INHERITED(SkSVGTag::kClipPath) {} diff --git a/experimental/svg/model/SkSVGClipPath.h b/experimental/svg/model/SkSVGClipPath.h new file mode 100644 index 0000000000..ec4395308b --- /dev/null +++ b/experimental/svg/model/SkSVGClipPath.h @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGClipPath_DEFINED +#define SkSVGClipPath_DEFINED + +#include "SkSVGHiddenContainer.h" +#include "SkSVGTypes.h" + +class SkSVGClipPath final : public SkSVGHiddenContainer { +public: + virtual ~SkSVGClipPath() = default; + static sk_sp<SkSVGClipPath> Make() { + return sk_sp<SkSVGClipPath>(new SkSVGClipPath()); + } + +protected: + +private: + SkSVGClipPath(); + + typedef SkSVGHiddenContainer INHERITED; +}; + +#endif // SkSVGClipPath_DEFINED diff --git a/experimental/svg/model/SkSVGContainer.cpp b/experimental/svg/model/SkSVGContainer.cpp index af19608d25..9f526c22c3 100644 --- a/experimental/svg/model/SkSVGContainer.cpp +++ b/experimental/svg/model/SkSVGContainer.cpp @@ -7,6 +7,9 @@ #include "SkSVGContainer.h" +#include "SkPath.h" +#include "SkPathOps.h" + SkSVGContainer::SkSVGContainer(SkSVGTag t) : INHERITED(t) { } void SkSVGContainer::appendChild(sk_sp<SkSVGNode> node) { @@ -23,3 +26,16 @@ void SkSVGContainer::onRender(const SkSVGRenderContext& ctx) const { fChildren[i]->render(ctx); } } + +SkPath SkSVGContainer::onAsPath(const SkSVGRenderContext& ctx) const { + SkPath path; + + for (int i = 0; i < fChildren.count(); ++i) { + const SkPath childPath = fChildren[i]->asPath(ctx); + + Op(path, childPath, kUnion_SkPathOp, &path); + } + + this->mapToParent(&path); + return path; +} diff --git a/experimental/svg/model/SkSVGContainer.h b/experimental/svg/model/SkSVGContainer.h index 25296fcddb..6a0b00244c 100644 --- a/experimental/svg/model/SkSVGContainer.h +++ b/experimental/svg/model/SkSVGContainer.h @@ -22,6 +22,8 @@ protected: void onRender(const SkSVGRenderContext&) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + bool hasChildren() const final; // TODO: add some sort of child iterator, and hide the container. diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index 2120f80e36..cea1ae69fe 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -11,6 +11,7 @@ #include "SkString.h" #include "SkSVGAttributeParser.h" #include "SkSVGCircle.h" +#include "SkSVGClipPath.h" #include "SkSVGDefs.h" #include "SkSVGDOM.h" #include "SkSVGEllipse.h" @@ -66,6 +67,19 @@ bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, return true; } +bool SetClipPathAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGClip clip; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseClipPath(&clip)) { + return false; + } + + node->setAttribute(attr, SkSVGClipValue(clip)); + return true; +} + + bool SetPathDataAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, const char* stringValue) { SkPath path; @@ -263,6 +277,7 @@ struct AttrParseInfo { }; SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = { + { "clip-path" , { SkSVGAttribute::kClipPath , SetClipPathAttribute }}, { "cx" , { SkSVGAttribute::kCx , SetLengthAttribute }}, { "cy" , { SkSVGAttribute::kCy , SetLengthAttribute }}, { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }}, @@ -300,6 +315,7 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = { SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = { { "circle" , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make(); }}, + { "clipPath" , []() -> sk_sp<SkSVGNode> { return SkSVGClipPath::Make(); }}, { "defs" , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make(); }}, { "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make(); }}, { "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }}, diff --git a/experimental/svg/model/SkSVGEllipse.cpp b/experimental/svg/model/SkSVGEllipse.cpp index 481af5c3ab..3f38b222d5 100644 --- a/experimental/svg/model/SkSVGEllipse.cpp +++ b/experimental/svg/model/SkSVGEllipse.cpp @@ -55,14 +55,26 @@ void SkSVGEllipse::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } } -void SkSVGEllipse::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint, SkPath::FillType) const { +SkRect SkSVGEllipse::resolve(const SkSVGLengthContext& lctx) 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); const auto ry = lctx.resolve(fRy, SkSVGLengthContext::LengthType::kVertical); - if (rx > 0 && ry > 0) { - canvas->drawOval(SkRect::MakeXYWH(cx - rx, cy - ry, rx * 2, ry * 2), paint); - } + return (rx > 0 && ry > 0) + ? SkRect::MakeXYWH(cx - rx, cy - ry, rx * 2, ry * 2) + : SkRect::MakeEmpty(); +} + +void SkSVGEllipse::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, + const SkPaint& paint, SkPath::FillType) const { + canvas->drawOval(this->resolve(lctx), paint); +} + +SkPath SkSVGEllipse::onAsPath(const SkSVGRenderContext& ctx) const { + SkPath path; + path.addOval(this->resolve(ctx.lengthContext())); + this->mapToParent(&path); + + return path; } diff --git a/experimental/svg/model/SkSVGEllipse.h b/experimental/svg/model/SkSVGEllipse.h index 0042b3364e..f17c1e1137 100644 --- a/experimental/svg/model/SkSVGEllipse.h +++ b/experimental/svg/model/SkSVGEllipse.h @@ -11,6 +11,8 @@ #include "SkSVGShape.h" #include "SkSVGTypes.h" +struct SkRect; + class SkSVGEllipse final : public SkSVGShape { public: virtual ~SkSVGEllipse() = default; @@ -27,9 +29,13 @@ protected: void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, SkPath::FillType) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + private: SkSVGEllipse(); + SkRect resolve(const SkSVGLengthContext&) const; + SkSVGLength fCx = SkSVGLength(0); SkSVGLength fCy = SkSVGLength(0); SkSVGLength fRx = SkSVGLength(0); diff --git a/experimental/svg/model/SkSVGLine.cpp b/experimental/svg/model/SkSVGLine.cpp index 27cdd469d2..9c8f5aa02b 100644 --- a/experimental/svg/model/SkSVGLine.cpp +++ b/experimental/svg/model/SkSVGLine.cpp @@ -55,12 +55,30 @@ void SkSVGLine::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } } +std::tuple<SkPoint, SkPoint> SkSVGLine::resolve(const SkSVGLengthContext& lctx) const { + return std::make_tuple( + SkPoint::Make(lctx.resolve(fX1, SkSVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY1, SkSVGLengthContext::LengthType::kVertical)), + SkPoint::Make(lctx.resolve(fX2, SkSVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY2, SkSVGLengthContext::LengthType::kVertical))); +} + void SkSVGLine::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, 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); - const auto y2 = lctx.resolve(fY2, SkSVGLengthContext::LengthType::kVertical); + SkPoint p0, p1; + std::tie(p0, p1) = this->resolve(lctx); + + canvas->drawLine(p0.x(), p0.y(), p1.x(), p1.y(), paint); +} + +SkPath SkSVGLine::onAsPath(const SkSVGRenderContext& ctx) const { + SkPoint p0, p1; + std::tie(p0, p1) = this->resolve(ctx.lengthContext()); + + SkPath path; + path.moveTo(p0); + path.lineTo(p1); + this->mapToParent(&path); - canvas->drawLine(x1, y1, x2, y2, paint); + return path; } diff --git a/experimental/svg/model/SkSVGLine.h b/experimental/svg/model/SkSVGLine.h index 524fc2ac0e..0f15ca5ac7 100644 --- a/experimental/svg/model/SkSVGLine.h +++ b/experimental/svg/model/SkSVGLine.h @@ -11,6 +11,8 @@ #include "SkSVGShape.h" #include "SkSVGTypes.h" +struct SkPoint; + class SkSVGLine final : public SkSVGShape { public: virtual ~SkSVGLine() = default; @@ -27,9 +29,14 @@ protected: void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, SkPath::FillType) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + private: SkSVGLine(); + // resolve and return the two endpoints + std::tuple<SkPoint, SkPoint> resolve(const SkSVGLengthContext&) const; + SkSVGLength fX1 = SkSVGLength(0); SkSVGLength fY1 = SkSVGLength(0); SkSVGLength fX2 = SkSVGLength(0); diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp index a5c8147554..ec452ec9c6 100644 --- a/experimental/svg/model/SkSVGNode.cpp +++ b/experimental/svg/model/SkSVGNode.cpp @@ -30,6 +30,11 @@ bool SkSVGNode::asPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const { return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint); } +SkPath SkSVGNode::asPath(const SkSVGRenderContext& ctx) const { + SkSVGRenderContext localContext(ctx); + return this->onPrepareToRender(&localContext) ? this->onAsPath(localContext) : SkPath(); +} + bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const { ctx->applyPresentationAttributes(fPresentationAttributes, this->hasChildren() ? 0 : SkSVGRenderContext::kLeaf); @@ -40,6 +45,10 @@ void SkSVGNode::setAttribute(SkSVGAttribute attr, const SkSVGValue& v) { this->onSetAttribute(attr, v); } +void SkSVGNode::setClipPath(const SkSVGClip& clip) { + fPresentationAttributes.fClipPath.set(clip); +} + void SkSVGNode::setFill(const SkSVGPaint& svgPaint) { fPresentationAttributes.fFill.set(svgPaint); } @@ -73,6 +82,11 @@ void SkSVGNode::setStrokeWidth(const SkSVGLength& strokeWidth) { void SkSVGNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { switch (attr) { + case SkSVGAttribute::kClipPath: + if (const SkSVGClipValue* clip = v.as<SkSVGClipValue>()) { + this->setClipPath(*clip); + } + break; case SkSVGAttribute::kFill: if (const SkSVGPaintValue* paint = v.as<SkSVGPaintValue>()) { this->setFill(*paint); diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h index 1e092a4d87..385b1584b8 100644 --- a/experimental/svg/model/SkSVGNode.h +++ b/experimental/svg/model/SkSVGNode.h @@ -14,11 +14,13 @@ class SkCanvas; class SkMatrix; class SkPaint; +class SkPath; class SkSVGRenderContext; class SkSVGValue; enum class SkSVGTag { kCircle, + kClipPath, kDefs, kEllipse, kG, @@ -42,9 +44,11 @@ public: void render(const SkSVGRenderContext&) const; bool asPaint(const SkSVGRenderContext&, SkPaint*) const; + SkPath asPath(const SkSVGRenderContext&) const; void setAttribute(SkSVGAttribute, const SkSVGValue&); + void setClipPath(const SkSVGClip&); void setFill(const SkSVGPaint&); void setFillOpacity(const SkSVGNumberType&); void setFillRule(const SkSVGFillRule&); @@ -68,6 +72,8 @@ protected: virtual bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const { return false; } + virtual SkPath onAsPath(const SkSVGRenderContext&) const = 0; + virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&); virtual bool hasChildren() const { return false; } diff --git a/experimental/svg/model/SkSVGPath.cpp b/experimental/svg/model/SkSVGPath.cpp index dd248237f1..7568744035 100644 --- a/experimental/svg/model/SkSVGPath.cpp +++ b/experimental/svg/model/SkSVGPath.cpp @@ -31,3 +31,12 @@ void SkSVGPath::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPain fPath.setFillType(fillType); canvas->drawPath(fPath, paint); } + +SkPath SkSVGPath::onAsPath(const SkSVGRenderContext& ctx) const { + // the computed fillType follows inheritance rules and needs to be applied at draw time. + fPath.setFillType(FillRuleToFillType(*ctx.presentationContext().fInherited.fFillRule.get())); + + SkPath path = fPath; + this->mapToParent(&path); + return path; +} diff --git a/experimental/svg/model/SkSVGPath.h b/experimental/svg/model/SkSVGPath.h index 8297a8d557..8b1f2e98f8 100644 --- a/experimental/svg/model/SkSVGPath.h +++ b/experimental/svg/model/SkSVGPath.h @@ -24,6 +24,8 @@ protected: void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, SkPath::FillType) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + private: SkSVGPath(); diff --git a/experimental/svg/model/SkSVGPoly.cpp b/experimental/svg/model/SkSVGPoly.cpp index 826ca2457e..479638e93f 100644 --- a/experimental/svg/model/SkSVGPoly.cpp +++ b/experimental/svg/model/SkSVGPoly.cpp @@ -38,3 +38,12 @@ void SkSVGPoly::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPain fPath.setFillType(fillType); canvas->drawPath(fPath, paint); } + +SkPath SkSVGPoly::onAsPath(const SkSVGRenderContext& ctx) const { + // the computed fillType follows inheritance rules and needs to be applied at draw time. + fPath.setFillType(FillRuleToFillType(*ctx.presentationContext().fInherited.fFillRule.get())); + + SkPath path = fPath; + this->mapToParent(&path); + return path; +} diff --git a/experimental/svg/model/SkSVGPoly.h b/experimental/svg/model/SkSVGPoly.h index f75d3626cc..90fb354d35 100644 --- a/experimental/svg/model/SkSVGPoly.h +++ b/experimental/svg/model/SkSVGPoly.h @@ -32,6 +32,8 @@ protected: void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, SkPath::FillType) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + private: SkSVGPoly(SkSVGTag); diff --git a/experimental/svg/model/SkSVGRect.cpp b/experimental/svg/model/SkSVGRect.cpp index 1afd9957c6..fe68d62297 100644 --- a/experimental/svg/model/SkSVGRect.cpp +++ b/experimental/svg/model/SkSVGRect.cpp @@ -74,15 +74,24 @@ void SkSVGRect::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } } -void SkSVGRect::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, - const SkPaint& paint, SkPath::FillType) const { +SkRRect SkSVGRect::resolve(const SkSVGLengthContext& lctx) 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); - if (rx || ry) { - canvas->drawRRect(SkRRect::MakeRectXY(rect, rx, ry), paint); - } else { - canvas->drawRect(rect, paint); - } + return SkRRect::MakeRectXY(rect, rx ,ry); +} + +void SkSVGRect::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, + const SkPaint& paint, SkPath::FillType) const { + canvas->drawRRect(this->resolve(lctx), paint); +} + +SkPath SkSVGRect::onAsPath(const SkSVGRenderContext& ctx) const { + SkPath path; + path.addRRect(this->resolve(ctx.lengthContext())); + + this->mapToParent(&path); + + return path; } diff --git a/experimental/svg/model/SkSVGRect.h b/experimental/svg/model/SkSVGRect.h index affa65f610..a0c07a1957 100644 --- a/experimental/svg/model/SkSVGRect.h +++ b/experimental/svg/model/SkSVGRect.h @@ -11,6 +11,8 @@ #include "SkSVGShape.h" #include "SkSVGTypes.h" +class SkRRect; + class SkSVGRect final : public SkSVGShape { public: virtual ~SkSVGRect() = default; @@ -29,9 +31,13 @@ protected: void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&, SkPath::FillType) const override; + SkPath onAsPath(const SkSVGRenderContext&) const override; + private: SkSVGRect(); + SkRRect resolve(const SkSVGLengthContext&) const; + SkSVGLength fX = SkSVGLength(0); SkSVGLength fY = SkSVGLength(0); SkSVGLength fWidth = SkSVGLength(0); diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp index fd0262319a..d8307a05c0 100644 --- a/experimental/svg/model/SkSVGRenderContext.cpp +++ b/experimental/svg/model/SkSVGRenderContext.cpp @@ -280,6 +280,10 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr if (auto* opacity = attrs.fOpacity.getMaybeNull()) { this->applyOpacity(opacity->value(), flags); } + + if (auto* clip = attrs.fClipPath.getMaybeNull()) { + this->applyClip(*clip); + } } void SkSVGRenderContext::applyOpacity(SkScalar opacity, uint32_t flags) { @@ -312,6 +316,26 @@ void SkSVGRenderContext::applyOpacity(SkScalar opacity, uint32_t flags) { } } +void SkSVGRenderContext::applyClip(const SkSVGClip& clip) { + if (clip.type() != SkSVGClip::Type::kIRI) { + return; + } + + const SkSVGNode* clipNode = this->findNodeById(clip.iri()); + if (!clipNode || clipNode->tag() != SkSVGTag::kClipPath) { + return; + } + + const SkPath clipPath = clipNode->asPath(*this); + + // Only save if needed + if (fCanvas->getSaveCount() == fCanvasSaveCount) { + fCanvas->save(); + } + + fCanvas->clipPath(clipPath, true); +} + const SkPaint* SkSVGRenderContext::fillPaint() const { const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fFill.get()->type(); return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fFillPaint : nullptr; diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h index 9d2fb3a621..ab046cd290 100644 --- a/experimental/svg/model/SkSVGRenderContext.h +++ b/experimental/svg/model/SkSVGRenderContext.h @@ -86,6 +86,7 @@ private: SkSVGRenderContext& operator=(const SkSVGRenderContext&) = delete; void applyOpacity(SkScalar opacity, uint32_t flags); + void applyClip(const SkSVGClip&); const SkSVGIDMapper& fIDMapper; SkTCopyOnFirstWrite<SkSVGLengthContext> fLengthContext; diff --git a/experimental/svg/model/SkSVGTransformableNode.cpp b/experimental/svg/model/SkSVGTransformableNode.cpp index 8a095ac62a..b2ad0b1ab9 100644 --- a/experimental/svg/model/SkSVGTransformableNode.cpp +++ b/experimental/svg/model/SkSVGTransformableNode.cpp @@ -36,3 +36,16 @@ void SkSVGTransformableNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValu break; } } + +void SkSVGTransformableNode::mapToParent(SkPath* path) const { + if (fTransform.value().isIdentity()) { + return; + } + + SkMatrix inv; + if (!fTransform.value().invert(&inv)) { + return; + } + + path->transform(inv); +} diff --git a/experimental/svg/model/SkSVGTransformableNode.h b/experimental/svg/model/SkSVGTransformableNode.h index 05644ac973..ad217a92f9 100644 --- a/experimental/svg/model/SkSVGTransformableNode.h +++ b/experimental/svg/model/SkSVGTransformableNode.h @@ -24,6 +24,8 @@ protected: void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + void mapToParent(SkPath*) const; + private: // FIXME: should be sparse SkSVGTransformType fTransform; diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h index d5d2b7146f..42a09b93b5 100644 --- a/experimental/svg/model/SkSVGTypes.h +++ b/experimental/svg/model/SkSVGTypes.h @@ -119,6 +119,34 @@ private: SkString fIRI; }; +class SkSVGClip { +public: + enum class Type { + kNone, + kInherit, + kIRI, + }; + + SkSVGClip() : fType(Type::kNone) {} + explicit SkSVGClip(Type t) : fType(t) {} + explicit SkSVGClip(const SkString& iri) : fType(Type::kIRI), fIRI(iri) {} + + SkSVGClip(const SkSVGClip&) = default; + SkSVGClip& operator=(const SkSVGClip&) = default; + + bool operator==(const SkSVGClip& other) const { + return fType == other.fType && fIRI == other.fIRI; + } + bool operator!=(const SkSVGClip& other) const { return !(*this == other); } + + Type type() const { return fType; } + const SkString& iri() const { SkASSERT(fType == Type::kIRI); return fIRI; } + +private: + Type fType; + SkString fIRI; +}; + class SkSVGLineCap { public: enum class Type { diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h index 9f3095c918..8e58b0672a 100644 --- a/experimental/svg/model/SkSVGValue.h +++ b/experimental/svg/model/SkSVGValue.h @@ -17,6 +17,7 @@ class SkSVGValue : public SkNoncopyable { public: enum class Type { + kClip, kColor, kFillRule, kLength, @@ -70,6 +71,7 @@ private: typedef SkSVGValue INHERITED; }; +using SkSVGClipValue = SkSVGWrapperValue<SkSVGClip , SkSVGValue::Type::kClip >; using SkSVGColorValue = SkSVGWrapperValue<SkSVGColorType , SkSVGValue::Type::kColor >; using SkSVGFillRuleValue = SkSVGWrapperValue<SkSVGFillRule , SkSVGValue::Type::kFillRule >; using SkSVGLengthValue = SkSVGWrapperValue<SkSVGLength , SkSVGValue::Type::kLength >; |