"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",
class SkSVGRenderContext;
enum class SkSVGAttribute {
+ kClipPath,
kCx, // <circle>,<ellipse>: center x position
kCy, // <circle>,<ellipse>: center y position
kD,
SkTLazy<SkSVGNumberType> fStrokeOpacity;
SkTLazy<SkSVGLength> fStrokeWidth;
+ // uninherited
SkTLazy<SkSVGNumberType> fOpacity;
+ SkTLazy<SkSVGClip> fClipPath;
};
#endif // SkSVGAttribute_DEFINED
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 {
SkSVGAttributeParser(const char[]);
bool parseColor(SkSVGColorType*);
+ bool parseClipPath(SkSVGClip*);
bool parseFillRule(SkSVGFillRule*);
bool parseNumber(SkSVGNumberType*);
bool parseLength(SkSVGLength*);
}
}
-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;
+}
#include "SkSVGShape.h"
#include "SkSVGTypes.h"
+struct SkPoint;
+
class SkSVGCircle final : public SkSVGShape {
public:
virtual ~SkSVGCircle() = default;
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);
--- /dev/null
+/*
+ * 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) {}
--- /dev/null
+/*
+ * 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
#include "SkSVGContainer.h"
+#include "SkPath.h"
+#include "SkPathOps.h"
+
SkSVGContainer::SkSVGContainer(SkSVGTag t) : INHERITED(t) { }
void SkSVGContainer::appendChild(sk_sp<SkSVGNode> node) {
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;
+}
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.
#include "SkString.h"
#include "SkSVGAttributeParser.h"
#include "SkSVGCircle.h"
+#include "SkSVGClipPath.h"
#include "SkSVGDefs.h"
#include "SkSVGDOM.h"
#include "SkSVGEllipse.h"
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;
};
SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
+ { "clip-path" , { SkSVGAttribute::kClipPath , SetClipPathAttribute }},
{ "cx" , { SkSVGAttribute::kCx , SetLengthAttribute }},
{ "cy" , { SkSVGAttribute::kCy , SetLengthAttribute }},
{ "d" , { SkSVGAttribute::kD , SetPathDataAttribute }},
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(); }},
}
}
-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;
}
#include "SkSVGShape.h"
#include "SkSVGTypes.h"
+struct SkRect;
+
class SkSVGEllipse final : public SkSVGShape {
public:
virtual ~SkSVGEllipse() = default;
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);
}
}
+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;
}
#include "SkSVGShape.h"
#include "SkSVGTypes.h"
+struct SkPoint;
+
class SkSVGLine final : public SkSVGShape {
public:
virtual ~SkSVGLine() = default;
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);
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);
this->onSetAttribute(attr, v);
}
+void SkSVGNode::setClipPath(const SkSVGClip& clip) {
+ fPresentationAttributes.fClipPath.set(clip);
+}
+
void SkSVGNode::setFill(const SkSVGPaint& svgPaint) {
fPresentationAttributes.fFill.set(svgPaint);
}
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);
class SkCanvas;
class SkMatrix;
class SkPaint;
+class SkPath;
class SkSVGRenderContext;
class SkSVGValue;
enum class SkSVGTag {
kCircle,
+ kClipPath,
kDefs,
kEllipse,
kG,
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&);
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; }
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;
+}
void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
SkPath::FillType) const override;
+ SkPath onAsPath(const SkSVGRenderContext&) const override;
+
private:
SkSVGPath();
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;
+}
void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
SkPath::FillType) const override;
+ SkPath onAsPath(const SkSVGRenderContext&) const override;
+
private:
SkSVGPoly(SkSVGTag);
}
}
-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;
}
#include "SkSVGShape.h"
#include "SkSVGTypes.h"
+class SkRRect;
+
class SkSVGRect final : public SkSVGShape {
public:
virtual ~SkSVGRect() = default;
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);
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) {
}
}
+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;
SkSVGRenderContext& operator=(const SkSVGRenderContext&) = delete;
void applyOpacity(SkScalar opacity, uint32_t flags);
+ void applyClip(const SkSVGClip&);
const SkSVGIDMapper& fIDMapper;
SkTCopyOnFirstWrite<SkSVGLengthContext> fLengthContext;
break;
}
}
+
+void SkSVGTransformableNode::mapToParent(SkPath* path) const {
+ if (fTransform.value().isIdentity()) {
+ return;
+ }
+
+ SkMatrix inv;
+ if (!fTransform.value().invert(&inv)) {
+ return;
+ }
+
+ path->transform(inv);
+}
void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+ void mapToParent(SkPath*) const;
+
private:
// FIXME: should be sparse
SkSVGTransformType fTransform;
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 {
class SkSVGValue : public SkNoncopyable {
public:
enum class Type {
+ kClip,
kColor,
kFillRule,
kLength,
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 >;
'../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',