[SVGDom] ClipPath support
authorFlorin Malita <fmalita@chromium.org>
Thu, 8 Dec 2016 14:26:47 +0000 (09:26 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Thu, 8 Dec 2016 15:00:18 +0000 (15:00 +0000)
  * 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>

30 files changed:
BUILD.gn
experimental/svg/model/SkSVGAttribute.h
experimental/svg/model/SkSVGAttributeParser.cpp
experimental/svg/model/SkSVGAttributeParser.h
experimental/svg/model/SkSVGCircle.cpp
experimental/svg/model/SkSVGCircle.h
experimental/svg/model/SkSVGClipPath.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGClipPath.h [new file with mode: 0644]
experimental/svg/model/SkSVGContainer.cpp
experimental/svg/model/SkSVGContainer.h
experimental/svg/model/SkSVGDOM.cpp
experimental/svg/model/SkSVGEllipse.cpp
experimental/svg/model/SkSVGEllipse.h
experimental/svg/model/SkSVGLine.cpp
experimental/svg/model/SkSVGLine.h
experimental/svg/model/SkSVGNode.cpp
experimental/svg/model/SkSVGNode.h
experimental/svg/model/SkSVGPath.cpp
experimental/svg/model/SkSVGPath.h
experimental/svg/model/SkSVGPoly.cpp
experimental/svg/model/SkSVGPoly.h
experimental/svg/model/SkSVGRect.cpp
experimental/svg/model/SkSVGRect.h
experimental/svg/model/SkSVGRenderContext.cpp
experimental/svg/model/SkSVGRenderContext.h
experimental/svg/model/SkSVGTransformableNode.cpp
experimental/svg/model/SkSVGTransformableNode.h
experimental/svg/model/SkSVGTypes.h
experimental/svg/model/SkSVGValue.h
gyp/svg.gyp

index 29acd54..1ee4d60 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -930,6 +930,7 @@ if (skia_enable_tools) {
       "experimental/svg/model/SkSVGAttribute.cpp",
       "experimental/svg/model/SkSVGAttributeParser.cpp",
       "experimental/svg/model/SkSVGCircle.cpp",
+      "experimental/svg/model/SkSVGClipPath.cpp",
       "experimental/svg/model/SkSVGContainer.cpp",
       "experimental/svg/model/SkSVGDOM.cpp",
       "experimental/svg/model/SkSVGEllipse.cpp",
index 93c5f99..2900a03 100644 (file)
@@ -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
index 5ca317c..f8eef8c 100644 (file)
@@ -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 {
index 2ffa79f..67c13f6 100644 (file)
@@ -15,6 +15,7 @@ public:
     SkSVGAttributeParser(const char[]);
 
     bool parseColor(SkSVGColorType*);
+    bool parseClipPath(SkSVGClip*);
     bool parseFillRule(SkSVGFillRule*);
     bool parseNumber(SkSVGNumberType*);
     bool parseLength(SkSVGLength*);
index 9d81173..4934879 100644 (file)
@@ -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;
+}
index 02aaad4..16ee54d 100644 (file)
@@ -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 (file)
index 0000000..60f1b6a
--- /dev/null
@@ -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 (file)
index 0000000..ec43953
--- /dev/null
@@ -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
index af19608..9f526c2 100644 (file)
@@ -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;
+}
index 25296fc..6a0b002 100644 (file)
@@ -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.
index 2120f80..cea1ae6 100644 (file)
@@ -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();              }},
index 481af5c..3f38b22 100644 (file)
@@ -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;
 }
index 0042b33..f17c1e1 100644 (file)
@@ -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);
index 27cdd46..9c8f5aa 100644 (file)
@@ -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;
 }
index 524fc2a..0f15ca5 100644 (file)
@@ -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);
index a5c8147..ec452ec 100644 (file)
@@ -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);
index 1e092a4..385b158 100644 (file)
 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; }
index dd24823..7568744 100644 (file)
@@ -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;
+}
index 8297a8d..8b1f2e9 100644 (file)
@@ -24,6 +24,8 @@ protected:
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
                 SkPath::FillType) const override;
 
+    SkPath onAsPath(const SkSVGRenderContext&) const override;
+
 private:
     SkSVGPath();
 
index 826ca24..479638e 100644 (file)
@@ -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;
+}
index f75d362..90fb354 100644 (file)
@@ -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);
 
index 1afd995..fe68d62 100644 (file)
@@ -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;
 }
index affa65f..a0c07a1 100644 (file)
@@ -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);
index fd02623..d8307a0 100644 (file)
@@ -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;
index 9d2fb3a..ab046cd 100644 (file)
@@ -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;
index 8a095ac..b2ad0b1 100644 (file)
@@ -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);
+}
index 05644ac..ad217a9 100644 (file)
@@ -24,6 +24,8 @@ protected:
 
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
+    void mapToParent(SkPath*) const;
+
 private:
     // FIXME: should be sparse
     SkSVGTransformType fTransform;
index d5d2b71..42a09b9 100644 (file)
@@ -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 {
index 9f3095c..8e58b06 100644 (file)
@@ -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   >;
index fc11fe5..4a0836a 100644 (file)
@@ -50,6 +50,8 @@
         '../experimental/svg/model/SkSVGAttributeParser.cpp',
         '../experimental/svg/model/SkSVGCircle.h',
         '../experimental/svg/model/SkSVGCircle.cpp',
+        '../experimental/svg/model/SkSVGClipPath.h',
+        '../experimental/svg/model/SkSVGClipPath.cpp',
         '../experimental/svg/model/SkSVGContainer.h',
         '../experimental/svg/model/SkSVGContainer.cpp',
         '../experimental/svg/model/SkSVGDefs.h',