aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/svg
diff options
context:
space:
mode:
authorGravatar fmalita <fmalita@chromium.org>2016-09-12 17:06:47 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-09-12 17:06:47 -0700
commit28d5b72d86fdfae20dc47ba548748f119c7273e3 (patch)
tree579d10ae07a10a882ca68d00c2da6fa407c0c658 /experimental/svg
parent8c24f4fae3389b9937eb73128e76226cffebdd72 (diff)
[SVGDom] Initial linear gradient support
Kind of a big change, to connect several new bits into something useful: * ID tracking & lookup * new asPaint() node virtual to support shader (and in the future filter) based paint servers * <defs>, <linearGradient> and <stop> element support * 'href', 'offset', 'stop-color', 'stop-opacity' attribute support * IRI/FuncIRI and rgb(...) parsing BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2327233003 Review-Url: https://codereview.chromium.org/2327233003
Diffstat (limited to 'experimental/svg')
-rw-r--r--experimental/svg/model/SkSVGAttribute.h4
-rw-r--r--experimental/svg/model/SkSVGAttributeParser.cpp67
-rw-r--r--experimental/svg/model/SkSVGAttributeParser.h4
-rw-r--r--experimental/svg/model/SkSVGContainer.h5
-rw-r--r--experimental/svg/model/SkSVGDOM.cpp75
-rw-r--r--experimental/svg/model/SkSVGDOM.h2
-rw-r--r--experimental/svg/model/SkSVGDefs.h24
-rw-r--r--experimental/svg/model/SkSVGHiddenContainer.h26
-rw-r--r--experimental/svg/model/SkSVGIDMapper.h19
-rw-r--r--experimental/svg/model/SkSVGLinearGradient.cpp119
-rw-r--r--experimental/svg/model/SkSVGLinearGradient.h49
-rw-r--r--experimental/svg/model/SkSVGNode.cpp6
-rw-r--r--experimental/svg/model/SkSVGNode.h7
-rw-r--r--experimental/svg/model/SkSVGRenderContext.cpp51
-rw-r--r--experimental/svg/model/SkSVGRenderContext.h7
-rw-r--r--experimental/svg/model/SkSVGStop.cpp46
-rw-r--r--experimental/svg/model/SkSVGStop.h44
-rw-r--r--experimental/svg/model/SkSVGTypes.h16
-rw-r--r--experimental/svg/model/SkSVGValue.h2
19 files changed, 529 insertions, 44 deletions
diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h
index 196575de0b..ce06b1e8a6 100644
--- a/experimental/svg/model/SkSVGAttribute.h
+++ b/experimental/svg/model/SkSVGAttribute.h
@@ -20,11 +20,15 @@ enum class SkSVGAttribute {
kFill,
kFillOpacity,
kHeight,
+ kHref,
+ kOffset,
kOpacity,
kPoints,
kR, // <circle>: radius
kRx, // <ellipse>,<rect>: horizontal (corner) radius
kRy, // <ellipse>,<rect>: vertical (corner) radius
+ kStopColor,
+ kStopOpacity,
kStroke,
kStrokeOpacity,
kStrokeLineCap,
diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp
index f8bfa2033f..9d2d6b8136 100644
--- a/experimental/svg/model/SkSVGAttributeParser.cpp
+++ b/experimental/svg/model/SkSVGAttributeParser.cpp
@@ -113,6 +113,7 @@ bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
return false;
}
+// https://www.w3.org/TR/SVG/types.html#DataTypeColor
bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
fCurPos = next;
@@ -148,7 +149,38 @@ bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
return true;
}
-// https://www.w3.org/TR/SVG/types.html#DataTypeColor
+bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
+ fCurPos = SkParse::FindS32(fCurPos, c);
+ if (!fCurPos) {
+ return false;
+ }
+
+ if (*fCurPos == '%') {
+ *c = SkScalarRoundToInt(*c * 255.0f / 100);
+ fCurPos++;
+ }
+
+ return true;
+}
+
+bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
+ return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
+ int32_t r, g, b;
+ if (this->parseColorComponentToken(&r) &&
+ this->parseSepToken() &&
+ this->parseColorComponentToken(&g) &&
+ this->parseSepToken() &&
+ this->parseColorComponentToken(&b)) {
+
+ *c = SkColorSetRGB(static_cast<uint8_t>(r),
+ static_cast<uint8_t>(g),
+ static_cast<uint8_t>(b));
+ return true;
+ }
+ return false;
+ }, c);
+}
+
bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
SkColor c;
@@ -157,7 +189,9 @@ bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
// TODO: rgb(...)
bool parsedValue = false;
- if (this->parseHexColorToken(&c) || this->parseNamedColorToken(&c)) {
+ if (this->parseHexColorToken(&c)
+ || this->parseNamedColorToken(&c)
+ || this->parseRGBColorToken(&c)) {
*color = SkSVGColorType(c);
parsedValue = true;
@@ -168,6 +202,31 @@ bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
return parsedValue && this->parseEOSToken();
}
+// https://www.w3.org/TR/SVG/linking.html#IRIReference
+bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
+ // consume preceding whitespace
+ this->parseWSToken();
+
+ // we only support local fragments
+ if (!this->parseExpectedStringToken("#")) {
+ return false;
+ }
+ const auto* start = fCurPos;
+ this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
+ if (start == fCurPos) {
+ return false;
+ }
+ *iri = SkString(start, fCurPos - start);
+ return true;
+}
+
+// https://www.w3.org/TR/SVG/types.html#DataTypeFuncIRI
+bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
+ return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
+ return this->parseIRI(iri);
+ }, iri);
+}
+
// https://www.w3.org/TR/SVG/types.html#DataTypeNumber
bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
// consume WS
@@ -363,6 +422,7 @@ bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) {
// https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
SkSVGColorType c;
+ SkSVGStringType iri;
bool parsedValue = false;
if (this->parseColor(&c)) {
*paint = SkSVGPaint(c);
@@ -376,6 +436,9 @@ bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
} else if (this->parseExpectedStringToken("inherit")) {
*paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
parsedValue = true;
+ } else if (this->parseFuncIRI(&iri)) {
+ *paint = SkSVGPaint(iri.value());
+ parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h
index d1ead39223..c1700a8e77 100644
--- a/experimental/svg/model/SkSVGAttributeParser.h
+++ b/experimental/svg/model/SkSVGAttributeParser.h
@@ -23,6 +23,7 @@ public:
bool parseLineCap(SkSVGLineCap*);
bool parseLineJoin(SkSVGLineJoin*);
bool parsePoints(SkSVGPointsType*);
+ bool parseIRI(SkSVGStringType*);
private:
// Stack-only
@@ -41,6 +42,9 @@ private:
bool parseLengthUnitToken(SkSVGLength::Unit*);
bool parseNamedColorToken(SkColor*);
bool parseHexColorToken(SkColor*);
+ bool parseColorComponentToken(int32_t*);
+ bool parseRGBColorToken(SkColor*);
+ bool parseFuncIRI(SkSVGStringType*);
// Transform helpers
bool parseMatrixToken(SkMatrix*);
diff --git a/experimental/svg/model/SkSVGContainer.h b/experimental/svg/model/SkSVGContainer.h
index 9a3fd621ab..73c7c1803b 100644
--- a/experimental/svg/model/SkSVGContainer.h
+++ b/experimental/svg/model/SkSVGContainer.h
@@ -18,13 +18,14 @@ public:
void appendChild(sk_sp<SkSVGNode>) override;
protected:
- SkSVGContainer(SkSVGTag);
+ explicit SkSVGContainer(SkSVGTag);
void onRender(const SkSVGRenderContext&) const override;
-private:
+ // TODO: add some sort of child iterator, and hide the container.
SkSTArray<1, sk_sp<SkSVGNode>, true> fChildren;
+private:
typedef SkSVGTransformableNode INHERITED;
};
diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp
index c123bc6bfb..d6e4f4c9bd 100644
--- a/experimental/svg/model/SkSVGDOM.cpp
+++ b/experimental/svg/model/SkSVGDOM.cpp
@@ -11,15 +11,18 @@
#include "SkString.h"
#include "SkSVGAttributeParser.h"
#include "SkSVGCircle.h"
+#include "SkSVGDefs.h"
#include "SkSVGDOM.h"
#include "SkSVGEllipse.h"
#include "SkSVGG.h"
#include "SkSVGLine.h"
+#include "SkSVGLinearGradient.h"
#include "SkSVGNode.h"
#include "SkSVGPath.h"
#include "SkSVGPoly.h"
#include "SkSVGRect.h"
#include "SkSVGRenderContext.h"
+#include "SkSVGStop.h"
#include "SkSVGSVG.h"
#include "SkSVGTypes.h"
#include "SkSVGValue.h"
@@ -32,17 +35,37 @@ bool SetPaintAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
SkSVGPaint paint;
SkSVGAttributeParser parser(stringValue);
if (!parser.parsePaint(&paint)) {
- // Until we have paint server support, failing here will cause default/all-black rendering.
- // It's better to just not draw for now.
- paint = SkSVGPaint(SkSVGPaint::Type::kNone);
-
- // return false;
+ return false;
}
node->setAttribute(attr, SkSVGPaintValue(paint));
return true;
}
+bool SetColorAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+ const char* stringValue) {
+ SkSVGColorType color;
+ SkSVGAttributeParser parser(stringValue);
+ if (!parser.parseColor(&color)) {
+ return false;
+ }
+
+ node->setAttribute(attr, SkSVGColorValue(color));
+ return true;
+}
+
+bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+ const char* stringValue) {
+ SkSVGStringType iri;
+ SkSVGAttributeParser parser(stringValue);
+ if (!parser.parseIRI(&iri)) {
+ return false;
+ }
+
+ node->setAttribute(attr, SkSVGStringValue(iri));
+ return true;
+}
+
bool SetPathDataAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
const char* stringValue) {
SkPath path;
@@ -222,11 +245,14 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
{ "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }},
{ "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }},
{ "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }},
+ { "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }},
{ "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }},
{ "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }},
{ "r" , { SkSVGAttribute::kR , SetLengthAttribute }},
{ "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }},
{ "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }},
+ { "stop-color" , { SkSVGAttribute::kStopColor , SetColorAttribute }},
+ { "stop-opacity" , { SkSVGAttribute::kStopOpacity , SetNumberAttribute }},
{ "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }},
{ "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute }},
{ "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute }},
@@ -239,29 +265,34 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
{ "x" , { SkSVGAttribute::kX , SetLengthAttribute }},
{ "x1" , { SkSVGAttribute::kX1 , SetLengthAttribute }},
{ "x2" , { SkSVGAttribute::kX2 , SetLengthAttribute }},
+ { "xlink:href" , { SkSVGAttribute::kHref , SetIRIAttribute }},
{ "y" , { SkSVGAttribute::kY , SetLengthAttribute }},
{ "y1" , { SkSVGAttribute::kY1 , SetLengthAttribute }},
{ "y2" , { SkSVGAttribute::kY2 , SetLengthAttribute }},
};
SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
- { "circle" , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make(); }},
- { "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make(); }},
- { "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},
- { "line" , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make(); }},
- { "path" , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make(); }},
- { "polygon" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon(); }},
- { "polyline", []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline(); }},
- { "rect" , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make(); }},
- { "svg" , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make(); }},
+ { "circle" , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make(); }},
+ { "defs" , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make(); }},
+ { "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make(); }},
+ { "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},
+ { "line" , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make(); }},
+ { "linearGradient", []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make(); }},
+ { "path" , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make(); }},
+ { "polygon" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon(); }},
+ { "polyline" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline(); }},
+ { "rect" , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make(); }},
+ { "stop" , []() -> sk_sp<SkSVGNode> { return SkSVGStop::Make(); }},
+ { "svg" , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make(); }},
};
struct ConstructionContext {
- ConstructionContext() : fParent(nullptr) { }
+ ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {}
ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
- : fParent(newParent.get()) { }
+ : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}
const SkSVGNode* fParent;
+ SkSVGIDMapper* fIDMapper;
};
void set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
@@ -285,10 +316,15 @@ void set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const
}
void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
- const sk_sp<SkSVGNode>& svgNode) {
+ const sk_sp<SkSVGNode>& svgNode, SkSVGIDMapper* mapper) {
const char* name, *value;
SkDOM::AttrIter attrIter(xmlDom, xmlNode);
while ((name = attrIter.next(&value))) {
+ // We're handling id attributes out of band for now.
+ if (!strcmp(name, "id")) {
+ mapper->set(SkString(value), svgNode);
+ continue;
+ }
set_string_attribute(svgNode, name, value);
}
}
@@ -318,7 +354,7 @@ sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext&
SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
sk_sp<SkSVGNode> node = gTagFactories[tagIndex].fValue();
- parse_node_attributes(dom, xmlNode, node);
+ parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper);
ConstructionContext localCtx(ctx, node);
for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
@@ -341,7 +377,7 @@ SkSVGDOM::SkSVGDOM(const SkSize& containerSize)
sk_sp<SkSVGDOM> SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom, const SkSize& containerSize) {
sk_sp<SkSVGDOM> dom = sk_make_sp<SkSVGDOM>(containerSize);
- ConstructionContext ctx;
+ ConstructionContext ctx(&dom->fIDMapper);
dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
return dom;
@@ -359,6 +395,7 @@ sk_sp<SkSVGDOM> SkSVGDOM::MakeFromStream(SkStream& svgStream, const SkSize& cont
void SkSVGDOM::render(SkCanvas* canvas) const {
if (fRoot) {
SkSVGRenderContext ctx(canvas,
+ fIDMapper,
SkSVGLengthContext(fContainerSize),
SkSVGPresentationContext());
fRoot->render(ctx);
diff --git a/experimental/svg/model/SkSVGDOM.h b/experimental/svg/model/SkSVGDOM.h
index 98f846246c..9c59b7c561 100644
--- a/experimental/svg/model/SkSVGDOM.h
+++ b/experimental/svg/model/SkSVGDOM.h
@@ -10,6 +10,7 @@
#include "SkRefCnt.h"
#include "SkSize.h"
+#include "SkSVGIDMapper.h"
#include "SkTemplates.h"
class SkCanvas;
@@ -33,6 +34,7 @@ public:
private:
SkSize fContainerSize;
sk_sp<SkSVGNode> fRoot;
+ SkSVGIDMapper fIDMapper;
typedef SkRefCnt INHERITED;
};
diff --git a/experimental/svg/model/SkSVGDefs.h b/experimental/svg/model/SkSVGDefs.h
new file mode 100644
index 0000000000..3064fe2197
--- /dev/null
+++ b/experimental/svg/model/SkSVGDefs.h
@@ -0,0 +1,24 @@
+/*
+ * 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 SkSVGDefs_DEFINED
+#define SkSVGDefs_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+
+class SkSVGDefs : public SkSVGHiddenContainer {
+public:
+ virtual ~SkSVGDefs() = default;
+ static sk_sp<SkSVGDefs> Make() { return sk_sp<SkSVGDefs>(new SkSVGDefs()); }
+
+private:
+ SkSVGDefs() : INHERITED(SkSVGTag::kDefs) {}
+
+ typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGDefs_DEFINED
diff --git a/experimental/svg/model/SkSVGHiddenContainer.h b/experimental/svg/model/SkSVGHiddenContainer.h
new file mode 100644
index 0000000000..e224e55038
--- /dev/null
+++ b/experimental/svg/model/SkSVGHiddenContainer.h
@@ -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.
+ */
+
+#ifndef SkSVGHiddenContainer_DEFINED
+#define SkSVGHiddenContainer_DEFINED
+
+#include "SkSVGContainer.h"
+
+class SkSVGHiddenContainer : public SkSVGContainer {
+public:
+ virtual ~SkSVGHiddenContainer() = default;
+
+protected:
+ explicit SkSVGHiddenContainer(SkSVGTag t) : INHERITED(t) {}
+
+ void onRender(const SkSVGRenderContext&) const final {}
+
+private:
+ typedef SkSVGContainer INHERITED;
+};
+
+#endif // SkSVGHiddenContainer_DEFINED
diff --git a/experimental/svg/model/SkSVGIDMapper.h b/experimental/svg/model/SkSVGIDMapper.h
new file mode 100644
index 0000000000..d88c1f12b1
--- /dev/null
+++ b/experimental/svg/model/SkSVGIDMapper.h
@@ -0,0 +1,19 @@
+/*
+ * 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 SkSVGIDMapper_DEFINED
+#define SkSVGIDMapper_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkTHash.h"
+
+class SkString;
+class SkSVGNode;
+
+using SkSVGIDMapper = SkTHashMap<SkString, sk_sp<SkSVGNode>>;
+
+#endif // SkSVGIDMapper_DEFINED
diff --git a/experimental/svg/model/SkSVGLinearGradient.cpp b/experimental/svg/model/SkSVGLinearGradient.cpp
new file mode 100644
index 0000000000..20c27f540c
--- /dev/null
+++ b/experimental/svg/model/SkSVGLinearGradient.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "SkGradientShader.h"
+#include "SkSVGLinearGradient.h"
+#include "SkSVGRenderContext.h"
+#include "SkSVGStop.h"
+#include "SkSVGValue.h"
+
+SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SkSVGTag::kLinearGradient) {}
+
+void SkSVGLinearGradient::setHref(const SkSVGStringType& href) {
+ fHref = std::move(href);
+}
+
+void SkSVGLinearGradient::setX1(const SkSVGLength& x1) {
+ fX1 = x1;
+}
+
+void SkSVGLinearGradient::setY1(const SkSVGLength& y1) {
+ fY1 = y1;
+}
+
+void SkSVGLinearGradient::setX2(const SkSVGLength& x2) {
+ fX2 = x2;
+}
+
+void SkSVGLinearGradient::setY2(const SkSVGLength& y2) {
+ fY2 = y2;
+}
+
+void SkSVGLinearGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ switch (attr) {
+ case SkSVGAttribute::kHref:
+ if (const auto* href = v.as<SkSVGStringValue>()) {
+ this->setHref(*href);
+ }
+ break;
+ case SkSVGAttribute::kX1:
+ if (const auto* x1 = v.as<SkSVGLengthValue>()) {
+ this->setX1(*x1);
+ }
+ break;
+ case SkSVGAttribute::kY1:
+ if (const auto* y1 = v.as<SkSVGLengthValue>()) {
+ this->setY1(*y1);
+ }
+ break;
+ case SkSVGAttribute::kX2:
+ if (const auto* x2 = v.as<SkSVGLengthValue>()) {
+ this->setX2(*x2);
+ }
+ break;
+ case SkSVGAttribute::kY2:
+ if (const auto* y2 = v.as<SkSVGLengthValue>()) {
+ this->setY2(*y2);
+ }
+ break;
+ default:
+ this->INHERITED::onSetAttribute(attr, v);
+ }
+}
+
+// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementHrefAttribute
+void SkSVGLinearGradient::collectColorStops(const SkSVGRenderContext& ctx,
+ SkSTArray<2, SkScalar, true>* pos,
+ SkSTArray<2, SkColor, true>* colors) const {
+ // Used to resolve percentage offsets.
+ const SkSVGLengthContext ltx(SkSize::Make(1, 1));
+
+ for (const auto& child : fChildren) {
+ if (child->tag() != SkSVGTag::kStop) {
+ continue;
+ }
+
+ const auto& stop = static_cast<const SkSVGStop&>(*child);
+ colors->push_back(SkColorSetA(stop.stopColor(),
+ SkScalarRoundToInt(stop.stopOpacity() * 255)));
+ pos->push_back(SkTPin(ltx.resolve(stop.offset(), SkSVGLengthContext::LengthType::kOther),
+ 0.f, 1.f));
+ }
+
+ SkASSERT(colors->count() == pos->count());
+
+ if (pos->empty() && !fHref.value().isEmpty()) {
+ const auto* ref = ctx.findNodeById(fHref);
+ if (ref && ref->tag() == SkSVGTag::kLinearGradient) {
+ static_cast<const SkSVGLinearGradient*>(ref)->collectColorStops(ctx, pos, colors);
+ }
+ }
+}
+
+bool SkSVGLinearGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
+ const auto& lctx = ctx.lengthContext();
+ 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);
+
+ const SkPoint pts[2] = { {x1, y1}, {x2, y2}};
+ SkSTArray<2, SkColor , true> colors;
+ SkSTArray<2, SkScalar, true> pos;
+
+ this->collectColorStops(ctx, &pos, &colors);
+ // TODO:
+ // * stop (lazy?) sorting
+ // * href loop detection
+ // * href attribute inheritance (not just color stops)
+ // * spreadMethods support
+ // * objectBoundingBox units support
+
+ paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(),
+ SkShader::kClamp_TileMode));
+ return true;
+}
diff --git a/experimental/svg/model/SkSVGLinearGradient.h b/experimental/svg/model/SkSVGLinearGradient.h
new file mode 100644
index 0000000000..1a2e332baf
--- /dev/null
+++ b/experimental/svg/model/SkSVGLinearGradient.h
@@ -0,0 +1,49 @@
+/*
+ * 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 SkSVGLinearGradient_DEFINED
+#define SkSVGLinearGradient_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+#include "SkSVGTypes.h"
+
+class SkSVGLinearGradient : public SkSVGHiddenContainer {
+public:
+ virtual ~SkSVGLinearGradient() = default;
+ static sk_sp<SkSVGLinearGradient> Make() {
+ return sk_sp<SkSVGLinearGradient>(new SkSVGLinearGradient());
+ }
+
+ void setHref(const SkSVGStringType&);
+ void setX1(const SkSVGLength&);
+ void setY1(const SkSVGLength&);
+ void setX2(const SkSVGLength&);
+ void setY2(const SkSVGLength&);
+
+protected:
+ bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const override;
+
+ void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+private:
+ SkSVGLinearGradient();
+
+ void collectColorStops(const SkSVGRenderContext&,
+ SkSTArray<2, SkScalar, true>*,
+ SkSTArray<2, SkColor, true>*) const;
+
+ SkSVGLength fX1 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage);
+ SkSVGLength fY1 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage);
+ SkSVGLength fX2 = SkSVGLength(100, SkSVGLength::Unit::kPercentage);
+ SkSVGLength fY2 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage);
+
+ SkSVGStringType fHref;
+
+ typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGLinearGradient_DEFINED
diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp
index 5a73ace548..012c7d2277 100644
--- a/experimental/svg/model/SkSVGNode.cpp
+++ b/experimental/svg/model/SkSVGNode.cpp
@@ -24,6 +24,12 @@ void SkSVGNode::render(const SkSVGRenderContext& ctx) const {
}
}
+bool SkSVGNode::asPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
+ SkSVGRenderContext localContext(ctx);
+
+ return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint);
+}
+
bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
ctx->applyPresentationAttributes(fPresentationAttributes);
return true;
diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h
index c02fd3a214..dcdb58958a 100644
--- a/experimental/svg/model/SkSVGNode.h
+++ b/experimental/svg/model/SkSVGNode.h
@@ -13,18 +13,22 @@
class SkCanvas;
class SkMatrix;
+class SkPaint;
class SkSVGRenderContext;
class SkSVGValue;
enum class SkSVGTag {
kCircle,
+ kDefs,
kEllipse,
kG,
kLine,
+ kLinearGradient,
kPath,
kPolygon,
kPolyline,
kRect,
+ kStop,
kSvg
};
@@ -37,6 +41,7 @@ public:
virtual void appendChild(sk_sp<SkSVGNode>) = 0;
void render(const SkSVGRenderContext&) const;
+ bool asPaint(const SkSVGRenderContext&, SkPaint*) const;
void setAttribute(SkSVGAttribute, const SkSVGValue&);
@@ -60,6 +65,8 @@ protected:
virtual void onRender(const SkSVGRenderContext&) const = 0;
+ virtual bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const { return false; }
+
virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&);
private:
diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp
index d8f762142f..46ed5a28c4 100644
--- a/experimental/svg/model/SkSVGRenderContext.cpp
+++ b/experimental/svg/model/SkSVGRenderContext.cpp
@@ -7,6 +7,7 @@
#include "SkCanvas.h"
#include "SkSVGAttribute.h"
+#include "SkSVGNode.h"
#include "SkSVGRenderContext.h"
#include "SkSVGTypes.h"
@@ -98,11 +99,18 @@ SkPaint::Join toSkJoin(const SkSVGLineJoin& join) {
}
}
-void applySvgPaint(const SkSVGPaint& svgPaint, SkPaint* p) {
+void applySvgPaint(const SkSVGRenderContext& ctx, const SkSVGPaint& svgPaint, SkPaint* p) {
switch (svgPaint.type()) {
case SkSVGPaint::Type::kColor:
p->setColor(SkColorSetA(svgPaint.color(), p->getAlpha()));
break;
+ case SkSVGPaint::Type::kIRI: {
+ const auto* node = ctx.findNodeById(svgPaint.iri());
+ if (!node || !node->asPaint(ctx, p)) {
+ p->setColor(SK_ColorTRANSPARENT);
+ }
+ break;
+ }
case SkSVGPaint::Type::kCurrentColor:
SkDebugf("unimplemented 'currentColor' paint type");
// Fall through.
@@ -120,33 +128,33 @@ inline uint8_t opacity_to_alpha(SkScalar o) {
// Commit the selected attribute to the paint cache.
template <SkSVGAttribute>
void commitToPaint(const SkSVGPresentationAttributes&,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext&,
SkSVGPresentationContext*);
template <>
void commitToPaint<SkSVGAttribute::kFill>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext& ctx,
SkSVGPresentationContext* pctx) {
- applySvgPaint(*attrs.fFill.get(), &pctx->fFillPaint);
+ applySvgPaint(ctx, *attrs.fFill.get(), &pctx->fFillPaint);
}
template <>
void commitToPaint<SkSVGAttribute::kStroke>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext& ctx,
SkSVGPresentationContext* pctx) {
- applySvgPaint(*attrs.fStroke.get(), &pctx->fStrokePaint);
+ applySvgPaint(ctx, *attrs.fStroke.get(), &pctx->fStrokePaint);
}
template <>
void commitToPaint<SkSVGAttribute::kFillOpacity>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext&,
SkSVGPresentationContext* pctx) {
pctx->fFillPaint.setAlpha(opacity_to_alpha(*attrs.fFillOpacity.get()));
}
template <>
void commitToPaint<SkSVGAttribute::kStrokeLineCap>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext&,
SkSVGPresentationContext* pctx) {
const auto& cap = *attrs.fStrokeLineCap.get();
if (cap.type() != SkSVGLineCap::Type::kInherit) {
@@ -156,7 +164,7 @@ void commitToPaint<SkSVGAttribute::kStrokeLineCap>(const SkSVGPresentationAttrib
template <>
void commitToPaint<SkSVGAttribute::kStrokeLineJoin>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext&,
SkSVGPresentationContext* pctx) {
const auto& join = *attrs.fStrokeLineJoin.get();
if (join.type() != SkSVGLineJoin::Type::kInherit) {
@@ -166,17 +174,17 @@ void commitToPaint<SkSVGAttribute::kStrokeLineJoin>(const SkSVGPresentationAttri
template <>
void commitToPaint<SkSVGAttribute::kStrokeOpacity>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext&,
+ const SkSVGRenderContext&,
SkSVGPresentationContext* pctx) {
pctx->fStrokePaint.setAlpha(opacity_to_alpha(*attrs.fStrokeOpacity.get()));
}
template <>
void commitToPaint<SkSVGAttribute::kStrokeWidth>(const SkSVGPresentationAttributes& attrs,
- const SkSVGLengthContext& lctx,
+ const SkSVGRenderContext& ctx,
SkSVGPresentationContext* pctx) {
- auto strokeWidth = lctx.resolve(*attrs.fStrokeWidth.get(),
- SkSVGLengthContext::LengthType::kOther);
+ auto strokeWidth = ctx.lengthContext().resolve(*attrs.fStrokeWidth.get(),
+ SkSVGLengthContext::LengthType::kOther);
pctx->fStrokePaint.setStrokeWidth(strokeWidth);
}
@@ -193,7 +201,10 @@ SkSVGPresentationContext::SkSVGPresentationContext()
fStrokePaint.setAntiAlias(true);
// Commit initial values to the paint cache.
- SkSVGLengthContext dummy(SkSize::Make(0, 0));
+ SkCanvas dummyCanvas(0, 0);
+ SkSVGRenderContext dummy(&dummyCanvas, SkSVGIDMapper(), SkSVGLengthContext(SkSize::Make(0, 0)),
+ *this);
+
commitToPaint<SkSVGAttribute::kFill>(fInherited, dummy, this);
commitToPaint<SkSVGAttribute::kFillOpacity>(fInherited, dummy, this);
commitToPaint<SkSVGAttribute::kStroke>(fInherited, dummy, this);
@@ -204,15 +215,18 @@ SkSVGPresentationContext::SkSVGPresentationContext()
}
SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
+ const SkSVGIDMapper& mapper,
const SkSVGLengthContext& lctx,
const SkSVGPresentationContext& pctx)
- : fLengthContext(lctx)
+ : fIDMapper(mapper)
+ , fLengthContext(lctx)
, fPresentationContext(pctx)
, fCanvas(canvas)
, fCanvasSaveCount(canvas->getSaveCount()) {}
SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
: SkSVGRenderContext(other.fCanvas,
+ other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext) {}
@@ -220,6 +234,11 @@ SkSVGRenderContext::~SkSVGRenderContext() {
fCanvas->restoreToCount(fCanvasSaveCount);
}
+const SkSVGNode* SkSVGRenderContext::findNodeById(const SkString& id) const {
+ const auto* v = fIDMapper.find(id);
+ return v ? v->get() : nullptr;
+}
+
void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs) {
#define ApplyLazyInheritedAttribute(ATTR) \
@@ -231,7 +250,7 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr
/* Update the local attribute value */ \
fPresentationContext.writable()->fInherited.f ## ATTR.set(*value); \
/* Update the cached paints */ \
- commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *fLengthContext, \
+ commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *this, \
fPresentationContext.writable()); \
} \
} while (false)
diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h
index 61f8746376..68209a73cc 100644
--- a/experimental/svg/model/SkSVGRenderContext.h
+++ b/experimental/svg/model/SkSVGRenderContext.h
@@ -12,6 +12,7 @@
#include "SkRect.h"
#include "SkSize.h"
#include "SkSVGAttribute.h"
+#include "SkSVGIDMapper.h"
#include "SkTLazy.h"
#include "SkTypes.h"
@@ -56,7 +57,8 @@ struct SkSVGPresentationContext {
class SkSVGRenderContext {
public:
- SkSVGRenderContext(SkCanvas*, const SkSVGLengthContext&, const SkSVGPresentationContext&);
+ SkSVGRenderContext(SkCanvas*, const SkSVGIDMapper&, const SkSVGLengthContext&,
+ const SkSVGPresentationContext&);
SkSVGRenderContext(const SkSVGRenderContext&);
~SkSVGRenderContext();
@@ -67,6 +69,8 @@ public:
void applyPresentationAttributes(const SkSVGPresentationAttributes&);
+ const SkSVGNode* findNodeById(const SkString&) const;
+
const SkPaint* fillPaint() const;
const SkPaint* strokePaint() const;
@@ -76,6 +80,7 @@ private:
void* operator new(size_t, void*) = delete;
SkSVGRenderContext& operator=(const SkSVGRenderContext&) = delete;
+ const SkSVGIDMapper& fIDMapper;
SkTCopyOnFirstWrite<SkSVGLengthContext> fLengthContext;
SkTCopyOnFirstWrite<SkSVGPresentationContext> fPresentationContext;
SkCanvas* fCanvas;
diff --git a/experimental/svg/model/SkSVGStop.cpp b/experimental/svg/model/SkSVGStop.cpp
new file mode 100644
index 0000000000..3abf505abb
--- /dev/null
+++ b/experimental/svg/model/SkSVGStop.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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"
+#include "SkSVGStop.h"
+#include "SkSVGValue.h"
+
+SkSVGStop::SkSVGStop() : INHERITED(SkSVGTag::kStop) {}
+
+void SkSVGStop::setOffset(const SkSVGLength& offset) {
+ fOffset = offset;
+}
+
+void SkSVGStop::setStopColor(const SkSVGColorType& color) {
+ fStopColor = color;
+}
+
+void SkSVGStop::setStopOpacity(const SkSVGNumberType& opacity) {
+ fStopOpacity = SkTPin<SkScalar>(opacity.value(), 0, 1);
+}
+
+void SkSVGStop::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ switch (attr) {
+ case SkSVGAttribute::kOffset:
+ if (const auto* offset = v.as<SkSVGLengthValue>()) {
+ this->setOffset(*offset);
+ }
+ break;
+ case SkSVGAttribute::kStopColor:
+ if (const auto* color = v.as<SkSVGColorValue>()) {
+ this->setStopColor(*color);
+ }
+ break;
+ case SkSVGAttribute::kStopOpacity:
+ if (const auto* opacity = v.as<SkSVGNumberValue>()) {
+ this->setStopOpacity(*opacity);
+ }
+ break;
+ default:
+ this->INHERITED::onSetAttribute(attr, v);
+ }
+}
diff --git a/experimental/svg/model/SkSVGStop.h b/experimental/svg/model/SkSVGStop.h
new file mode 100644
index 0000000000..2ffbc5c852
--- /dev/null
+++ b/experimental/svg/model/SkSVGStop.h
@@ -0,0 +1,44 @@
+/*
+ * 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 SkSVGStop_DEFINED
+#define SkSVGStop_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+#include "SkSVGTypes.h"
+
+class SkSVGLengthContext;
+
+class SkSVGStop : public SkSVGHiddenContainer {
+public:
+ virtual ~SkSVGStop() = default;
+ static sk_sp<SkSVGStop> Make() {
+ return sk_sp<SkSVGStop>(new SkSVGStop());
+ }
+
+ const SkSVGLength& offset() const { return fOffset; }
+ const SkSVGColorType& stopColor() const { return fStopColor; }
+ const SkSVGNumberType& stopOpacity() const { return fStopOpacity; }
+
+ void setOffset(const SkSVGLength&);
+ void setStopColor(const SkSVGColorType&);
+ void setStopOpacity(const SkSVGNumberType&);
+
+protected:
+ void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+private:
+ SkSVGStop();
+
+ SkSVGLength fOffset = SkSVGLength(0 , SkSVGLength::Unit::kPercentage);
+ SkSVGColorType fStopColor = SkSVGColorType(SK_ColorBLACK);
+ SkSVGNumberType fStopOpacity = SkSVGNumberType(1);
+
+ typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGStop_DEFINED
diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h
index 18ec3ce3cf..b2343a15e4 100644
--- a/experimental/svg/model/SkSVGTypes.h
+++ b/experimental/svg/model/SkSVGTypes.h
@@ -13,6 +13,7 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkScalar.h"
+#include "SkString.h"
#include "SkTDArray.h"
#include "SkTypes.h"
@@ -42,6 +43,7 @@ private:
using SkSVGColorType = SkSVGPrimitiveTypeWrapper<SkColor >;
using SkSVGNumberType = SkSVGPrimitiveTypeWrapper<SkScalar>;
+using SkSVGStringType = SkSVGPrimitiveTypeWrapper<SkString>;
using SkSVGViewBoxType = SkSVGPrimitiveTypeWrapper<SkRect >;
using SkSVGTransformType = SkSVGPrimitiveTypeWrapper<SkMatrix>;
using SkSVGPointsType = SkSVGPrimitiveTypeWrapper<SkTDArray<SkPoint>>;
@@ -88,27 +90,33 @@ public:
kCurrentColor,
kColor,
kInherit,
+ kIRI,
};
- constexpr SkSVGPaint() : fType(Type::kInherit), fColor(SK_ColorBLACK) {}
- explicit constexpr SkSVGPaint(Type t) : fType(t), fColor(SK_ColorBLACK) {}
- explicit constexpr SkSVGPaint(const SkSVGColorType& c) : fType(Type::kColor), fColor(c) {}
+ SkSVGPaint() : fType(Type::kInherit), fColor(SK_ColorBLACK) {}
+ explicit SkSVGPaint(Type t) : fType(t), fColor(SK_ColorBLACK) {}
+ explicit SkSVGPaint(const SkSVGColorType& c) : fType(Type::kColor), fColor(c) {}
+ explicit SkSVGPaint(const SkString& iri)
+ : fType(Type::kIRI), fColor(SK_ColorBLACK), fIRI(iri) {}
SkSVGPaint(const SkSVGPaint&) = default;
SkSVGPaint& operator=(const SkSVGPaint&) = default;
bool operator==(const SkSVGPaint& other) const {
- return fType == other.fType && fColor == other.fColor;
+ return fType == other.fType && fColor == other.fColor && fIRI == other.fIRI;
}
bool operator!=(const SkSVGPaint& other) const { return !(*this == other); }
Type type() const { return fType; }
const SkSVGColorType& color() const { SkASSERT(fType == Type::kColor); return fColor; }
+ const SkString& iri() const { SkASSERT(fType == Type::kIRI); return fIRI; }
private:
Type fType;
+ // Logical union.
SkSVGColorType fColor;
+ SkString fIRI;
};
class SkSVGLineCap {
diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h
index 8f93bd8046..e4673e9150 100644
--- a/experimental/svg/model/SkSVGValue.h
+++ b/experimental/svg/model/SkSVGValue.h
@@ -25,6 +25,7 @@ public:
kPaint,
kPath,
kPoints,
+ kString,
kTransform,
kViewBox,
};
@@ -77,5 +78,6 @@ using SkSVGLineCapValue = SkSVGWrapperValue<SkSVGLineCap , SkSVGValue::Ty
using SkSVGLineJoinValue = SkSVGWrapperValue<SkSVGLineJoin , SkSVGValue::Type::kLineJoin >;
using SkSVGNumberValue = SkSVGWrapperValue<SkSVGNumberType , SkSVGValue::Type::kNumber >;
using SkSVGPointsValue = SkSVGWrapperValue<SkSVGPointsType , SkSVGValue::Type::kPoints >;
+using SkSVGStringValue = SkSVGWrapperValue<SkSVGStringType , SkSVGValue::Type::kString >;
#endif // SkSVGValue_DEFINED