Initial SVG model
authorfmalita <fmalita@chromium.org>
Wed, 27 Jul 2016 01:46:34 +0000 (18:46 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 27 Jul 2016 01:46:34 +0000 (18:46 -0700)
A minimal subset needed to render tiger.svg: <svg>, <g>, <path>, 'd', 'fill'/'stroke' (color-only), 'transform'.

R=reed@google.com,robertphillips@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2164193002

Review-Url: https://codereview.chromium.org/2164193002

25 files changed:
experimental/svg/model/SkSVGAttribute.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGAttribute.h [new file with mode: 0644]
experimental/svg/model/SkSVGContainer.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGContainer.h [new file with mode: 0644]
experimental/svg/model/SkSVGDOM.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGDOM.h [new file with mode: 0644]
experimental/svg/model/SkSVGG.h [new file with mode: 0644]
experimental/svg/model/SkSVGNode.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGNode.h [new file with mode: 0644]
experimental/svg/model/SkSVGPath.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGPath.h [new file with mode: 0644]
experimental/svg/model/SkSVGRenderContext.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGRenderContext.h [new file with mode: 0644]
experimental/svg/model/SkSVGSVG.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGSVG.h [new file with mode: 0644]
experimental/svg/model/SkSVGTransformableNode.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGTransformableNode.h [new file with mode: 0644]
experimental/svg/model/SkSVGValue.cpp [new file with mode: 0644]
experimental/svg/model/SkSVGValue.h [new file with mode: 0644]
gyp/samples.gypi
gyp/svg.gyp
include/xml/SkDOM.h
include/xml/SkXMLParser.h
samplecode/SampleApp.cpp
samplecode/SampleSVGFile.cpp [new file with mode: 0644]

diff --git a/experimental/svg/model/SkSVGAttribute.cpp b/experimental/svg/model/SkSVGAttribute.cpp
new file mode 100644 (file)
index 0000000..c4a374e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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);
+    }
+}
diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h
new file mode 100644 (file)
index 0000000..50989f1
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGContainer.cpp b/experimental/svg/model/SkSVGContainer.cpp
new file mode 100644 (file)
index 0000000..f866797
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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);
+    }
+}
diff --git a/experimental/svg/model/SkSVGContainer.h b/experimental/svg/model/SkSVGContainer.h
new file mode 100644 (file)
index 0000000..27c7944
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp
new file mode 100644 (file)
index 0000000..6f782c3
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * 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;
+}
diff --git a/experimental/svg/model/SkSVGDOM.h b/experimental/svg/model/SkSVGDOM.h
new file mode 100644 (file)
index 0000000..468ac00
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGG.h b/experimental/svg/model/SkSVGG.h
new file mode 100644 (file)
index 0000000..eaaf822
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp
new file mode 100644 (file)
index 0000000..2b89179
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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();
+}
diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h
new file mode 100644 (file)
index 0000000..de18014
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGPath.cpp b/experimental/svg/model/SkSVGPath.cpp
new file mode 100644 (file)
index 0000000..ba5e3d6
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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);
+    }
+}
diff --git a/experimental/svg/model/SkSVGPath.h b/experimental/svg/model/SkSVGPath.h
new file mode 100644 (file)
index 0000000..b650f67
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp
new file mode 100644 (file)
index 0000000..703606c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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);
+}
diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h
new file mode 100644 (file)
index 0000000..8ebaf12
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGSVG.cpp b/experimental/svg/model/SkSVGSVG.cpp
new file mode 100644 (file)
index 0000000..feae0d7
--- /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 "SkSVGSVG.h"
+
+SkSVGSVG::SkSVGSVG() : INHERITED(SkSVGTag::svg) { }
diff --git a/experimental/svg/model/SkSVGSVG.h b/experimental/svg/model/SkSVGSVG.h
new file mode 100644 (file)
index 0000000..8e69d14
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGTransformableNode.cpp b/experimental/svg/model/SkSVGTransformableNode.cpp
new file mode 100644 (file)
index 0000000..1af89d8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+    }
+}
diff --git a/experimental/svg/model/SkSVGTransformableNode.h b/experimental/svg/model/SkSVGTransformableNode.h
new file mode 100644 (file)
index 0000000..c345e85
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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
diff --git a/experimental/svg/model/SkSVGValue.cpp b/experimental/svg/model/SkSVGValue.cpp
new file mode 100644 (file)
index 0000000..674a200
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * 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"
diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h
new file mode 100644 (file)
index 0000000..3d3cd6e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
index 283d038..7e9c414 100644 (file)
@@ -94,6 +94,7 @@
     '../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',
   ],
index 8fc4b97..f5b43f8 100644 (file)
         ],
       },
     },
+    {
+      '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',
+        ],
+      },
+    },
+
   ],
 }
index 10ca3e7..b6f611a 100644 (file)
@@ -79,7 +79,7 @@ public:
 
     class AttrIter {
     public:
-        AttrIter(const class SkDOM&, const Node*);
+        AttrIter(const SkDOM&, const Node*);
         const char* next(const char** value);
     private:
         const Attr* fAttr;
index 2974fb5..3f69013 100644 (file)
@@ -73,10 +73,10 @@ protected:
 
 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;
index 0747fcf..64d460f 100644 (file)
@@ -80,6 +80,17 @@ public:
     }
 };
 
+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[]);
 
@@ -678,6 +689,8 @@ DEFINE_int32(msaa, 0, "Request multisampling with this count.");
 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?");
@@ -711,6 +724,19 @@ SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* dev
         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");
diff --git a/samplecode/SampleSVGFile.cpp b/samplecode/SampleSVGFile.cpp
new file mode 100644 (file)
index 0000000..01a1958
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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);
+}