Index: experimental/svg/model/SkSVGDOM.cpp |
diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6f782c3a96368d51bddb1e407732ae17205d9aaf |
--- /dev/null |
+++ b/experimental/svg/model/SkSVGDOM.cpp |
@@ -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; |
+} |