--- /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 "SkSVGAttribute.h"
+#include "SkSVGRenderContext.h"
+
+SkSVGPresentationAttributes::SkSVGPresentationAttributes()
+ : fFillIsSet(false)
+ , fStrokeIsSet(false) { }
+
+void SkSVGPresentationAttributes::setFill(SkColor c) {
+ fFill = c;
+ fFillIsSet = true;
+}
+
+void SkSVGPresentationAttributes::setStroke(SkColor c) {
+ fStroke = c;
+ fStrokeIsSet = true;
+}
+
+
+void SkSVGPresentationAttributes::applyTo(SkTCopyOnFirstWrite<SkSVGRenderContext>& ctx) const {
+ if (fFillIsSet) {
+ ctx.writable()->setFillColor(fFill);
+ }
+
+ if (fStrokeIsSet) {
+ ctx.writable()->setStrokeColor(fStroke);
+ }
+}
--- /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 SkSVGAttribute_DEFINED
+#define SkSVGAttribute_DEFINED
+
+#include "SkColor.h"
+#include "SkTLazy.h"
+
+enum class SkSVGAttribute {
+ d,
+ fill,
+ stroke,
+ transform,
+};
+
+class SkSVGRenderContext;
+
+class SkSVGPresentationAttributes {
+public:
+ SkSVGPresentationAttributes();
+
+ void setFill(SkColor);
+ void setStroke(SkColor);
+
+ void applyTo(SkTCopyOnFirstWrite<SkSVGRenderContext>&) const;
+
+private:
+ // Color only for now.
+ SkColor fFill;
+ SkColor fStroke;
+
+ unsigned fFillIsSet : 1;
+ unsigned fStrokeIsSet : 1;
+};
+
+#endif // SkSVGAttribute_DEFINED
--- /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 "SkSVGContainer.h"
+
+SkSVGContainer::SkSVGContainer(SkSVGTag t) : INHERITED(t) { }
+
+void SkSVGContainer::appendChild(sk_sp<SkSVGNode> node) {
+ SkASSERT(node);
+ fChildren.push_back(std::move(node));
+}
+
+void SkSVGContainer::onRender(SkCanvas* canvas, const SkSVGRenderContext& ctx) const {
+ for (int i = 0; i < fChildren.count(); ++i) {
+ fChildren[i]->render(canvas, ctx);
+ }
+}
--- /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 SkSVGContainer_DEFINED
+#define SkSVGContainer_DEFINED
+
+#include "SkSVGTransformableNode.h"
+#include "SkTArray.h"
+
+class SkSVGContainer : public SkSVGTransformableNode {
+public:
+ virtual ~SkSVGContainer() = default;
+
+ void appendChild(sk_sp<SkSVGNode>) override;
+
+protected:
+ SkSVGContainer(SkSVGTag);
+
+ void onRender(SkCanvas*, const SkSVGRenderContext&) const override;
+
+private:
+ SkSTArray<1, sk_sp<SkSVGNode>, true> fChildren;
+
+ typedef SkSVGTransformableNode INHERITED;
+};
+
+#endif // SkSVGSVG_DEFINED
--- /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 "SkCanvas.h"
+#include "SkDOM.h"
+#include "SkParse.h"
+#include "SkParsePath.h"
+#include "SkSVGDOM.h"
+#include "SkSVGG.h"
+#include "SkSVGNode.h"
+#include "SkSVGPath.h"
+#include "SkSVGSVG.h"
+#include "SkSVGValue.h"
+#include "SkTSearch.h"
+
+namespace {
+
+SkColor ParseColor(const char* str) {
+ // FIXME: real parser
+ if (*str++ != '#') {
+ return SK_ColorBLACK;
+ }
+
+ uint32_t v;
+ const char* consumed = SkParse::FindHex(str, &v);
+
+ switch(consumed - str) {
+ case 6:
+ // matched '#xxxxxx'
+ break;
+ case 3:
+ // matched '#xxx;
+ v = ((v << 12) & 0x00f00000) |
+ ((v << 8) & 0x000ff000) |
+ ((v << 4) & 0x00000ff0) |
+ ((v << 0) & 0x0000000f);
+ break;
+ default:
+ // failed
+ v = 0;
+ break;
+ }
+
+ return v | 0xff000000;
+}
+
+const char* ParseScalarPair(const char* str, SkScalar v[2]) {
+ str = SkParse::FindScalar(str, v);
+ if (str) {
+ const char* second = SkParse::FindScalar(str, v + 1);
+ if (!second) {
+ v[1] = v[0];
+ } else {
+ str = second;
+ }
+ }
+
+ return str;
+}
+
+SkMatrix ParseTransform(const char* str) {
+ SkMatrix m = SkMatrix::I();
+
+ // FIXME: real parser
+ if (!strncmp(str, "matrix(", 7)) {
+ SkScalar values[6];
+ str = SkParse::FindScalars(str + 7, values, 6);
+ if (str) {
+ m.setAffine(values);
+ }
+ } else if (!strncmp(str, "scale(", 6)) {
+ SkScalar values[2];
+ str = ParseScalarPair(str + 6, values);
+ if (str) {
+ m.setScale(values[0], values[1]);
+ }
+ } else if (!strncmp(str, "translate(", 10)) {
+ SkScalar values[2];
+ str = ParseScalarPair(str + 10, values);
+ if (str) {
+ m.setTranslate(values[0], values[1]);
+ }
+ } else if (!strncmp(str, "rotate(", 7)) {
+ SkScalar value;
+ str = SkParse::FindScalar(str + 7, &value);
+ if (str) {
+ m.setRotate(value);
+ }
+ }
+
+ return m;
+}
+
+bool SetPaintAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+ const char* stringValue) {
+ node->setAttribute(attr, SkSVGColorValue(ParseColor(stringValue)));
+ return true;
+}
+
+bool SetPathDataAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+ const char* stringValue) {
+ SkPath path;
+ if (!SkParsePath::FromSVGString(stringValue, &path)) {
+ return false;
+ }
+
+ node->setAttribute(attr, SkSVGPathValue(path));
+ return true;
+}
+
+bool SetTransformAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+ const char* stringValue) {
+ node->setAttribute(attr, SkSVGTransformValue(ParseTransform(stringValue)));
+ return true;
+}
+
+template<typename T>
+struct SortedDictionaryEntry {
+ const char* fKey;
+ const T fValue;
+};
+
+struct AttrParseInfo {
+ SkSVGAttribute fAttr;
+ bool (*fSetter)(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, const char* stringValue);
+};
+
+SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
+ { "d", { SkSVGAttribute::d, SetPathDataAttribute }},
+ { "fill", { SkSVGAttribute::fill, SetPaintAttribute }},
+ { "stroke", { SkSVGAttribute::stroke, SetPaintAttribute }},
+ { "transform", { SkSVGAttribute::transform, SetTransformAttribute }},
+};
+
+SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
+ { "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},
+ { "path", []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make(); }},
+ { "svg" , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make(); }},
+};
+
+struct ConstructionContext {
+ ConstructionContext() : fParent(nullptr) { }
+ ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
+ : fParent(newParent.get()) { }
+
+ const SkSVGNode* fParent;
+};
+
+void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
+ const sk_sp<SkSVGNode>& svgNode) {
+ const char* name, *value;
+ SkDOM::AttrIter attrIter(xmlDom, xmlNode);
+ while ((name = attrIter.next(&value))) {
+ const int attrIndex = SkStrSearch(&gAttributeParseInfo[0].fKey,
+ SkTo<int>(SK_ARRAY_COUNT(gAttributeParseInfo)),
+ name, sizeof(gAttributeParseInfo[0]));
+ if (attrIndex < 0) {
+ SkDebugf("unhandled attribute: %s\n", name);
+ continue;
+ }
+
+ SkASSERT(SkTo<size_t>(attrIndex) < SK_ARRAY_COUNT(gAttributeParseInfo));
+ const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue;
+ if (!attrInfo.fSetter(svgNode, attrInfo.fAttr, value)) {
+ SkDebugf("could not parse attribute: '%s=\"%s\"'\n", name, value);
+ }
+ }
+}
+
+sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext& ctx,
+ const SkDOM::Node* xmlNode) {
+ const char* elem = dom.getName(xmlNode);
+ const SkDOM::Type elemType = dom.getType(xmlNode);
+
+ if (elemType == SkDOM::kText_Type) {
+ SkASSERT(dom.countChildren(xmlNode) == 0);
+ // TODO: text handling
+ return nullptr;
+ }
+
+ SkASSERT(elemType == SkDOM::kElement_Type);
+
+ const int tagIndex = SkStrSearch(&gTagFactories[0].fKey,
+ SkTo<int>(SK_ARRAY_COUNT(gTagFactories)),
+ elem, sizeof(gTagFactories[0]));
+ if (tagIndex < 0) {
+ SkDebugf("unhandled element: <%s>\n", elem);
+ return nullptr;
+ }
+
+ SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
+ sk_sp<SkSVGNode> node = gTagFactories[tagIndex].fValue();
+ parse_node_attributes(dom, xmlNode, node);
+
+ ConstructionContext localCtx(ctx, node);
+ for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
+ child = dom.getNextSibling(child)) {
+ sk_sp<SkSVGNode> childNode = construct_svg_node(dom, localCtx, child);
+ if (childNode) {
+ node->appendChild(std::move(childNode));
+ }
+ }
+
+ return node;
+}
+
+} // anonymous namespace
+
+SkSVGDOM::SkSVGDOM(const SkSize& containerSize)
+ : fContainerSize(containerSize) {
+}
+
+sk_sp<SkSVGDOM> SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom, const SkSize& containerSize) {
+ sk_sp<SkSVGDOM> dom = sk_make_sp<SkSVGDOM>(containerSize);
+
+ ConstructionContext ctx;
+ dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
+
+ return dom;
+}
+
+sk_sp<SkSVGDOM> SkSVGDOM::MakeFromStream(SkStream& svgStream, const SkSize& containerSize) {
+ SkDOM xmlDom;
+ if (!xmlDom.build(svgStream)) {
+ return nullptr;
+ }
+
+ return MakeFromDOM(xmlDom, containerSize);
+}
+
+void SkSVGDOM::render(SkCanvas* canvas) const {
+ if (fRoot) {
+ fRoot->render(canvas);
+ }
+}
+
+void SkSVGDOM::setContainerSize(const SkSize& containerSize) {
+ // TODO: inval
+ fContainerSize = containerSize;
+}
--- /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 SkSVGDOM_DEFINED
+#define SkSVGDOM_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkSize.h"
+#include "SkTemplates.h"
+
+class SkCanvas;
+class SkDOM;
+class SkStream;
+class SkSVGNode;
+
+class SkSVGDOM : public SkRefCnt {
+public:
+ SkSVGDOM(const SkSize& containerSize);
+ ~SkSVGDOM() = default;
+
+ static sk_sp<SkSVGDOM> MakeFromDOM(const SkDOM&, const SkSize& containerSize);
+ static sk_sp<SkSVGDOM> MakeFromStream(SkStream&, const SkSize& containerSize);
+
+ void setContainerSize(const SkSize&);
+
+ void render(SkCanvas*) const;
+
+private:
+ SkSize fContainerSize;
+ sk_sp<SkSVGNode> fRoot;
+
+ typedef SkRefCnt INHERITED;
+};
+
+#endif // SkSVGDOM_DEFINED
--- /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 SkSVGG_DEFINED
+#define SkSVGG_DEFINED
+
+#include "SkSVGContainer.h"
+
+class SkSVGG : public SkSVGContainer {
+public:
+ virtual ~SkSVGG() = default;
+
+ static sk_sp<SkSVGG> Make() { return sk_sp<SkSVGG>(new SkSVGG()); }
+
+private:
+ SkSVGG() : INHERITED(SkSVGTag::g) { }
+
+ typedef SkSVGContainer INHERITED;
+};
+
+#endif // SkSVGG_DEFINED
--- /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 "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkSVGNode.h"
+#include "SkSVGRenderContext.h"
+#include "SkSVGValue.h"
+#include "SkTLazy.h"
+
+SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) { }
+
+SkSVGNode::~SkSVGNode() { }
+
+void SkSVGNode::render(SkCanvas* canvas) const {
+ this->render(canvas, SkSVGRenderContext());
+}
+
+void SkSVGNode::render(SkCanvas* canvas, const SkSVGRenderContext& ctx) const {
+ SkTCopyOnFirstWrite<SkSVGRenderContext> localContext(ctx);
+ fPresentationAttributes.applyTo(localContext);
+
+ SkAutoCanvasRestore acr(canvas, false);
+ const SkMatrix& m = this->onLocalMatrix();
+ if (!m.isIdentity()) {
+ canvas->save();
+ canvas->concat(m);
+ }
+
+ this->onRender(canvas, *localContext);
+}
+
+void SkSVGNode::setAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ this->onSetAttribute(attr, v);
+}
+
+void SkSVGNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ switch (attr) {
+ case SkSVGAttribute::fill:
+ if (const SkSVGColorValue* color = v.as<SkSVGColorValue>()) {
+ fPresentationAttributes.setFill(*color);
+ }
+ break;
+ case SkSVGAttribute::stroke:
+ if (const SkSVGColorValue* color = v.as<SkSVGColorValue>()) {
+ fPresentationAttributes.setStroke(*color);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+const SkMatrix& SkSVGNode::onLocalMatrix() const {
+ return SkMatrix::I();
+}
--- /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 SkSVGNode_DEFINED
+#define SkSVGNode_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkSVGAttribute.h"
+
+class SkCanvas;
+class SkMatrix;
+class SkSVGRenderContext;
+class SkSVGValue;
+
+enum class SkSVGTag {
+ g,
+ path,
+ svg
+};
+
+class SkSVGNode : public SkRefCnt {
+public:
+ virtual ~SkSVGNode();
+
+ SkSVGTag tag() const { return fTag; }
+
+ virtual void appendChild(sk_sp<SkSVGNode>) = 0;
+
+ void render(SkCanvas*) const;
+ void render(SkCanvas*, const SkSVGRenderContext&) const;
+
+ void setAttribute(SkSVGAttribute, const SkSVGValue&);
+
+protected:
+ SkSVGNode(SkSVGTag);
+
+ virtual void onRender(SkCanvas*, const SkSVGRenderContext&) const = 0;
+
+ virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&);
+
+ virtual const SkMatrix& onLocalMatrix() const;
+
+private:
+ SkSVGTag fTag;
+
+ // FIXME: this should be sparse
+ SkSVGPresentationAttributes fPresentationAttributes;
+
+ typedef SkRefCnt INHERITED;
+};
+
+#endif // SkSVGNode_DEFINED
--- /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 "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkSVGPath.h"
+#include "SkSVGRenderContext.h"
+#include "SkSVGValue.h"
+
+SkSVGPath::SkSVGPath() : INHERITED(SkSVGTag::path) { }
+
+void SkSVGPath::doRender(SkCanvas* canvas, const SkPaint* paint) const {
+ if (paint) {
+ canvas->drawPath(fPath, *paint);
+ }
+}
+
+void SkSVGPath::onRender(SkCanvas* canvas, const SkSVGRenderContext& ctx) const {
+ this->doRender(canvas, ctx.fillPaint());
+ this->doRender(canvas, ctx.strokePaint());
+}
+
+void SkSVGPath::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ switch (attr) {
+ case SkSVGAttribute::d:
+ if (const auto* path = v.as<SkSVGPathValue>()) {
+ this->setPath(*path);
+ }
+ break;
+ default:
+ this->INHERITED::onSetAttribute(attr, v);
+ }
+}
--- /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 SkSVGPath_DEFINED
+#define SkSVGPath_DEFINED
+
+#include "SkPath.h"
+#include "SkSVGTransformableNode.h"
+
+class SkSVGPath final : public SkSVGTransformableNode {
+public:
+ virtual ~SkSVGPath() = default;
+ static sk_sp<SkSVGPath> Make() { return sk_sp<SkSVGPath>(new SkSVGPath()); }
+
+ void appendChild(sk_sp<SkSVGNode>) override { }
+
+ void setPath(const SkPath& path) { fPath = path; }
+
+protected:
+ void onRender(SkCanvas*, const SkSVGRenderContext&) const override;
+
+ void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+private:
+ SkSVGPath();
+
+ void doRender(SkCanvas*, const SkPaint*) const;
+
+ SkPath fPath;
+
+ typedef SkSVGTransformableNode INHERITED;
+};
+
+#endif // SkSVGPath_DEFINED
--- /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 "SkSVGRenderContext.h"
+
+SkSVGRenderContext::SkSVGRenderContext() { }
+
+SkSVGRenderContext& SkSVGRenderContext::operator=(const SkSVGRenderContext& other) {
+ if (other.fFill.isValid()) {
+ fFill.set(*other.fFill.get());
+ } else {
+ fFill.reset();
+ }
+
+ if (other.fStroke.isValid()) {
+ fStroke.set(*other.fStroke.get());
+ } else {
+ fStroke.reset();
+ }
+
+ return *this;
+}
+
+SkPaint& SkSVGRenderContext::ensureFill() {
+ if (!fFill.isValid()) {
+ fFill.init();
+ fFill.get()->setStyle(SkPaint::kFill_Style);
+ fFill.get()->setAntiAlias(true);
+ }
+ return *fFill.get();
+}
+
+SkPaint& SkSVGRenderContext::ensureStroke() {
+ if (!fStroke.isValid()) {
+ fStroke.init();
+ fStroke.get()->setStyle(SkPaint::kStroke_Style);
+ fStroke.get()->setAntiAlias(true);
+ }
+ return *fStroke.get();
+}
+
+void SkSVGRenderContext::setFillColor(SkColor color) {
+ this->ensureFill().setColor(color);
+}
+
+void SkSVGRenderContext::setStrokeColor(SkColor color) {
+ this->ensureStroke().setColor(color);
+}
--- /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 SkSVGRenderContext_DEFINED
+#define SkSVGRenderContext_DEFINED
+
+#include "SkPaint.h"
+#include "SkTLazy.h"
+
+class SkPaint;
+
+class SkSVGRenderContext {
+public:
+ SkSVGRenderContext();
+ SkSVGRenderContext(const SkSVGRenderContext&) = default;
+ SkSVGRenderContext& operator=(const SkSVGRenderContext&);
+
+ const SkPaint* fillPaint() const { return fFill.getMaybeNull(); }
+ const SkPaint* strokePaint() const { return fStroke.getMaybeNull(); }
+
+ void setFillColor(SkColor);
+ void setStrokeColor(SkColor);
+
+private:
+ SkPaint& ensureFill();
+ SkPaint& ensureStroke();
+
+ SkTLazy<SkPaint> fFill;
+ SkTLazy<SkPaint> fStroke;
+};
+
+#endif // SkSVGRenderContext_DEFINED
--- /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 "SkSVGSVG.h"
+
+SkSVGSVG::SkSVGSVG() : INHERITED(SkSVGTag::svg) { }
--- /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 SkSVGSVG_DEFINED
+#define SkSVGSVG_DEFINED
+
+#include "SkSVGContainer.h"
+
+class SkSVGSVG : public SkSVGContainer {
+public:
+ virtual ~SkSVGSVG() = default;
+
+ static sk_sp<SkSVGSVG> Make() { return sk_sp<SkSVGSVG>(new SkSVGSVG()); }
+
+private:
+ SkSVGSVG();
+
+ typedef SkSVGContainer INHERITED;
+};
+
+#endif // SkSVGSVG_DEFINED
--- /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 "SkSVGTransformableNode.h"
+#include "SkSVGValue.h"
+
+SkSVGTransformableNode::SkSVGTransformableNode(SkSVGTag tag)
+ : INHERITED(tag)
+ , fMatrix(SkMatrix::I()) { }
+
+void SkSVGTransformableNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ switch (attr) {
+ case SkSVGAttribute::transform:
+ if (const auto* transform = v.as<SkSVGTransformValue>()) {
+ this->setTransform(*transform);
+ }
+ break;
+ default:
+ this->INHERITED::onSetAttribute(attr, v);
+ break;
+ }
+}
--- /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 SkSVGTransformableNode_DEFINED
+#define SkSVGTransformableNode_DEFINED
+
+#include "SkMatrix.h"
+#include "SkSVGNode.h"
+
+class SkSVGTransformableNode : public SkSVGNode {
+public:
+ virtual ~SkSVGTransformableNode() = default;
+
+ void setTransform(const SkMatrix& m) { fMatrix = m; }
+
+protected:
+ SkSVGTransformableNode(SkSVGTag);
+
+ void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+ const SkMatrix& onLocalMatrix() const override { return fMatrix; }
+
+private:
+ // FIXME: should be sparse
+ SkMatrix fMatrix;
+
+ typedef SkSVGNode INHERITED;
+};
+
+#endif // SkSVGTransformableNode_DEFINED
--- /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 "SkSVGValue.h"
--- /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 SkSVGValue_DEFINED
+#define SkSVGValue_DEFINED
+
+#include "SkColor.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkTypes.h"
+
+class SkSVGValue : public SkNoncopyable {
+public:
+ enum class Type {
+ Color,
+ Path,
+ Transform,
+ };
+
+ Type type() const { return fType; }
+
+ template <typename T>
+ const T* as() const {
+ return fType == T::TYPE ? static_cast<const T*>(this) : nullptr;
+ }
+
+protected:
+ SkSVGValue(Type t) : fType(t) { }
+
+private:
+ Type fType;
+};
+
+template <typename SkiaType, SkSVGValue::Type ValueType>
+class SkSVGWrapperValue final : public SkSVGValue {
+public:
+ static constexpr Type TYPE = ValueType;
+
+ explicit SkSVGWrapperValue(const SkiaType& v)
+ : INHERITED(ValueType)
+ , fWrappedValue(v) { }
+
+ operator const SkiaType&() const { return fWrappedValue; }
+
+private:
+ SkiaType fWrappedValue;
+
+ using INHERITED = SkSVGValue;
+};
+
+using SkSVGColorValue = SkSVGWrapperValue<SkColor , SkSVGValue::Type::Color >;
+using SkSVGPathValue = SkSVGWrapperValue<SkPath , SkSVGValue::Type::Path >;
+using SkSVGTransformValue = SkSVGWrapperValue<SkMatrix, SkSVGValue::Type::Transform>;
+
+#endif // SkSVGValue_DEFINED
'../samplecode/SampleStringArt.cpp',
'../samplecode/SampleStrokePath.cpp',
'../samplecode/SampleSubpixelTranslate.cpp',
+ '../samplecode/SampleSVGFile.cpp',
'../samplecode/SampleText.cpp',
'../samplecode/SampleTextAlpha.cpp',
'../samplecode/SampleTextBox.cpp',
'experimental.gyp:experimental',
'lua.gyp:lua',
'pdf.gyp:pdf',
+ 'svg.gyp:svgdom',
'views.gyp:views',
'xml.gyp:xml',
],
],
},
},
+ {
+ 'target_name': 'svgdom',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ 'dependencies': [
+ 'skia_lib.gyp:skia_lib',
+ 'xml.gyp:xml',
+ ],
+ 'include_dirs': [
+ '<(skia_include_path)/private',
+ '../experimental/svg/model',
+ ],
+ 'sources': [
+ '../experimental/svg/model/SkSVGAttribute.h',
+ '../experimental/svg/model/SkSVGAttribute.cpp',
+ '../experimental/svg/model/SkSVGContainer.h',
+ '../experimental/svg/model/SkSVGContainer.cpp',
+ '../experimental/svg/model/SkSVGDOM.h',
+ '../experimental/svg/model/SkSVGDOM.cpp',
+ '../experimental/svg/model/SkSVGG.h',
+ '../experimental/svg/model/SkSVGNode.h',
+ '../experimental/svg/model/SkSVGNode.cpp',
+ '../experimental/svg/model/SkSVGPath.h',
+ '../experimental/svg/model/SkSVGPath.cpp',
+ '../experimental/svg/model/SkSVGRenderContext.h',
+ '../experimental/svg/model/SkSVGRenderContext.cpp',
+ '../experimental/svg/model/SkSVGSVG.h',
+ '../experimental/svg/model/SkSVGSVG.cpp',
+ '../experimental/svg/model/SkSVGTransformableNode.h',
+ '../experimental/svg/model/SkSVGTransformableNode.cpp',
+ '../experimental/svg/model/SkSVGValue.h',
+ '../experimental/svg/model/SkSVGValue.cpp',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../experimental/svg/model',
+ ],
+ },
+ },
+
],
}
class AttrIter {
public:
- AttrIter(const class SkDOM&, const Node*);
+ AttrIter(const SkDOM&, const Node*);
const char* next(const char** value);
private:
const Attr* fAttr;
public:
// public for ported implementation, not meant for clients to call
- virtual bool startElement(const char elem[]);
- virtual bool addAttribute(const char name[], const char value[]);
- virtual bool endElement(const char elem[]);
- virtual bool text(const char text[], int len);
+ bool startElement(const char elem[]);
+ bool addAttribute(const char name[], const char value[]);
+ bool endElement(const char elem[]);
+ bool text(const char text[], int len);
void* fParser;
protected:
SkXMLParserError* fError;
}
};
+extern SampleView* CreateSampleSVGFileView(const char filename[]);
+
+class SVGFileFactory : public SkViewFactory {
+ SkString fFilename;
+public:
+ SVGFileFactory(const SkString& filename) : fFilename(filename) {}
+ SkView* operator() () const override {
+ return CreateSampleSVGFileView(fFilename.c_str());
+ }
+};
+
#ifdef SAMPLE_PDF_FILE_VIEWER
extern SampleView* CreateSamplePdfFileViewer(const char filename[]);
DEFINE_bool(deepColor, false, "Request deep color (10-bit/channel or more) display buffer.");
DEFINE_string(pictureDir, "", "Read pictures from here.");
DEFINE_string(picture, "", "Path to single picture.");
+DEFINE_string(svg, "", "Path to single SVG file.");
+DEFINE_string(svgDir, "", "Read SVGs from here.");
DEFINE_string(sequence, "", "Path to file containing the desired samples/gms to show.");
DEFINE_bool(sort, false, "Sort samples by title.");
DEFINE_bool(list, false, "List samples?");
fCurrIndex = fSamples.count();
*fSamples.append() = new PictFileFactory(path);
}
+ if (!FLAGS_svg.isEmpty()) {
+ SkString path(FLAGS_svg[0]);
+ fCurrIndex = fSamples.count();
+ *fSamples.append() = new SVGFileFactory(path);
+ }
+ if (!FLAGS_svgDir.isEmpty()) {
+ SkOSFile::Iter iter(FLAGS_svgDir[0], "svg");
+ SkString filename;
+ while (iter.next(&filename)) {
+ *fSamples.append() = new SVGFileFactory(
+ SkOSPath::Join(FLAGS_svgDir[0], filename.c_str()));
+ }
+ }
#ifdef SAMPLE_PDF_FILE_VIEWER
if (!FLAGS_pdfPath.isEmpty()) {
SkOSFile::Iter iter(FLAGS_pdfPath[0], "pdf");
--- /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 "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkDOM.h"
+#include "SkStream.h"
+#include "SkSVGDOM.h"
+#include "SkView.h"
+
+namespace {
+
+class SVGFileView : public SampleView {
+public:
+ SVGFileView(const char path[]) {
+ SkFILEStream svgStream(path);
+ if (!svgStream.isValid()) {
+ SkDebugf("file not found: \"path\"\n", path);
+ return;
+ }
+
+ SkDOM xmlDom;
+ if (!xmlDom.build(svgStream)) {
+ SkDebugf("XML parsing failed: \"path\"\n", path);
+ return;
+ }
+
+ fDom = SkSVGDOM::MakeFromDOM(xmlDom, SkSize::Make(this->width(), this->height()));
+ }
+
+ virtual ~SVGFileView() = default;
+
+protected:
+ void onDrawContent(SkCanvas* canvas) override {
+ if (fDom) {
+ fDom->render(canvas);
+ }
+ }
+
+ void onSizeChange() override {
+ if (fDom) {
+ fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
+ }
+
+ this->INHERITED::onSizeChange();
+ }
+
+private:
+ sk_sp<SkSVGDOM> fDom;
+
+ typedef SampleView INHERITED;
+};
+
+} // anonymous namespace
+
+SampleView* CreateSampleSVGFileView(const char filename[]);
+SampleView* CreateSampleSVGFileView(const char filename[]) {
+ return new SVGFileView(filename);
+}