aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2017-10-11 14:34:33 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-10-11 18:56:38 +0000
commit1aa1bb65fd92e1a38c88bc792320a40a67f2a467 (patch)
treea5f7e4341a50f7abac93b435440a04d8f74f4ce5
parentb36be14c254b61510c13844311d6885775c96e8a (diff)
[SVGDom] Initial <pattern> support
https://www.w3.org/TR/SVG/pservers.html#Patterns Change-Id: I80455c4ae04cf03526f8e8797f40b0b2d24e043f Reviewed-on: https://skia-review.googlesource.com/58461 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Robert Phillips <robertphillips@google.com>
-rw-r--r--BUILD.gn1
-rw-r--r--experimental/svg/model/SkSVGAttribute.h1
-rw-r--r--experimental/svg/model/SkSVGDOM.cpp3
-rw-r--r--experimental/svg/model/SkSVGNode.h1
-rw-r--r--experimental/svg/model/SkSVGPattern.cpp170
-rw-r--r--experimental/svg/model/SkSVGPattern.h59
-rw-r--r--experimental/svg/model/SkSVGRenderContext.cpp6
-rw-r--r--experimental/svg/model/SkSVGRenderContext.h1
8 files changed, 242 insertions, 0 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 66ea0d82c8..0d99236781 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1177,6 +1177,7 @@ if (skia_enable_tools) {
"experimental/svg/model/SkSVGLinearGradient.cpp",
"experimental/svg/model/SkSVGNode.cpp",
"experimental/svg/model/SkSVGPath.cpp",
+ "experimental/svg/model/SkSVGPattern.cpp",
"experimental/svg/model/SkSVGPoly.cpp",
"experimental/svg/model/SkSVGRadialGradient.cpp",
"experimental/svg/model/SkSVGRect.cpp",
diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h
index 9ba38f944c..71eeb916b0 100644
--- a/experimental/svg/model/SkSVGAttribute.h
+++ b/experimental/svg/model/SkSVGAttribute.h
@@ -29,6 +29,7 @@ enum class SkSVGAttribute {
kHref,
kOffset,
kOpacity,
+ kPatternTransform,
kPoints,
kR, // <circle>, <radialGradient>: radius
kRx, // <ellipse>,<rect>: horizontal (corner) radius
diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp
index 7d6b0b75cd..7bb524b48c 100644
--- a/experimental/svg/model/SkSVGDOM.cpp
+++ b/experimental/svg/model/SkSVGDOM.cpp
@@ -20,6 +20,7 @@
#include "SkSVGLinearGradient.h"
#include "SkSVGNode.h"
#include "SkSVGPath.h"
+#include "SkSVGPattern.h"
#include "SkSVGPoly.h"
#include "SkSVGRadialGradient.h"
#include "SkSVGRect.h"
@@ -294,6 +295,7 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
{ "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }},
{ "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }},
{ "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }},
+ { "patternTransform" , { SkSVGAttribute::kPatternTransform , SetTransformAttribute }},
{ "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }},
{ "r" , { SkSVGAttribute::kR , SetLengthAttribute }},
{ "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }},
@@ -330,6 +332,7 @@ SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
{ "line" , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make(); }},
{ "linearGradient", []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make(); }},
{ "path" , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make(); }},
+ { "pattern" , []() -> sk_sp<SkSVGNode> { return SkSVGPattern::Make(); }},
{ "polygon" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon(); }},
{ "polyline" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline(); }},
{ "radialGradient", []() -> sk_sp<SkSVGNode> { return SkSVGRadialGradient::Make(); }},
diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h
index c0190c4b0c..c9955d1638 100644
--- a/experimental/svg/model/SkSVGNode.h
+++ b/experimental/svg/model/SkSVGNode.h
@@ -27,6 +27,7 @@ enum class SkSVGTag {
kLine,
kLinearGradient,
kPath,
+ kPattern,
kPolygon,
kPolyline,
kRadialGradient,
diff --git a/experimental/svg/model/SkSVGPattern.cpp b/experimental/svg/model/SkSVGPattern.cpp
new file mode 100644
index 0000000000..ef334d7cb0
--- /dev/null
+++ b/experimental/svg/model/SkSVGPattern.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSVGPattern.h"
+
+#include "SkPictureRecorder.h"
+#include "SkShader.h"
+#include "SkSVGRenderContext.h"
+#include "SkSVGValue.h"
+
+SkSVGPattern::SkSVGPattern() : INHERITED(SkSVGTag::kPattern) {}
+
+void SkSVGPattern::setX(const SkSVGLength& x) {
+ fAttributes.fX.set(x);
+}
+
+void SkSVGPattern::setY(const SkSVGLength& y) {
+ fAttributes.fY.set(y);
+}
+
+void SkSVGPattern::setWidth(const SkSVGLength& w) {
+ fAttributes.fWidth.set(w);
+}
+
+void SkSVGPattern::setHeight(const SkSVGLength& h) {
+ fAttributes.fHeight.set(h);
+}
+
+void SkSVGPattern::setHref(const SkSVGStringType& href) {
+ fHref = std::move(href);
+}
+
+void SkSVGPattern::setPatternTransform(const SkSVGTransformType& patternTransform) {
+ fAttributes.fPatternTransform.set(patternTransform);
+}
+
+void SkSVGPattern::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+ switch (attr) {
+ case SkSVGAttribute::kX:
+ if (const auto* x = v.as<SkSVGLengthValue>()) {
+ this->setX(*x);
+ }
+ break;
+ case SkSVGAttribute::kY:
+ if (const auto* y = v.as<SkSVGLengthValue>()) {
+ this->setY(*y);
+ }
+ break;
+ case SkSVGAttribute::kWidth:
+ if (const auto* w = v.as<SkSVGLengthValue>()) {
+ this->setWidth(*w);
+ }
+ break;
+ case SkSVGAttribute::kHeight:
+ if (const auto* h = v.as<SkSVGLengthValue>()) {
+ this->setHeight(*h);
+ }
+ break;
+ case SkSVGAttribute::kHref:
+ if (const auto* href = v.as<SkSVGStringValue>()) {
+ this->setHref(*href);
+ }
+ break;
+ case SkSVGAttribute::kPatternTransform:
+ if (const auto* t = v.as<SkSVGTransformValue>()) {
+ this->setPatternTransform(*t);
+ }
+ break;
+ default:
+ this->INHERITED::onSetAttribute(attr, v);
+ }
+}
+
+const SkSVGPattern* SkSVGPattern::hrefTarget(const SkSVGRenderContext& ctx) const {
+ if (fHref.value().isEmpty()) {
+ return nullptr;
+ }
+
+ const auto* href = ctx.findNodeById(fHref);
+ if (!href || href->tag() != SkSVGTag::kPattern) {
+ return nullptr;
+ }
+
+ return static_cast<const SkSVGPattern*>(href);
+}
+
+template <typename T>
+bool inherit_if_needed(const SkTLazy<T>& src, SkTLazy<T>& dst) {
+ if (!dst.isValid()) {
+ dst = src;
+ return true;
+ }
+
+ return false;
+}
+
+/* https://www.w3.org/TR/SVG/pservers.html#PatternElementHrefAttribute
+ *
+ * Any attributes which are defined on the referenced element which are not defined on this element
+ * are inherited by this element. If this element has no children, and the referenced element does
+ * (possibly due to its own ‘xlink:href’ attribute), then this element inherits the children from
+ * the referenced element. Inheritance can be indirect to an arbitrary level; thus, if the
+ * referenced element inherits attributes or children due to its own ‘xlink:href’ attribute, then
+ * the current element can inherit those attributes or children.
+ */
+const SkSVGPattern* SkSVGPattern::resolveHref(const SkSVGRenderContext& ctx,
+ PatternAttributes* attrs) const {
+ const SkSVGPattern *currentNode = this,
+ *contentNode = this;
+ do {
+ // Bitwise OR to avoid short-circuiting.
+ const bool didInherit =
+ inherit_if_needed(currentNode->fAttributes.fX , attrs->fX) |
+ inherit_if_needed(currentNode->fAttributes.fY , attrs->fY) |
+ inherit_if_needed(currentNode->fAttributes.fWidth , attrs->fWidth) |
+ inherit_if_needed(currentNode->fAttributes.fHeight , attrs->fHeight) |
+ inherit_if_needed(currentNode->fAttributes.fPatternTransform, attrs->fPatternTransform);
+
+ if (!contentNode->hasChildren()) {
+ contentNode = currentNode;
+ }
+
+ if (contentNode->hasChildren() && !didInherit) {
+ // All attributes have been resolved, and a valid content node has been found.
+ // We can terminate the href chain early.
+ break;
+ }
+
+ // TODO: reference loop mitigation.
+ currentNode = currentNode->hrefTarget(ctx);
+ } while (currentNode);
+
+ return contentNode;
+}
+
+bool SkSVGPattern::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
+ PatternAttributes attrs;
+ const auto* contentNode = this->resolveHref(ctx, &attrs);
+
+ const auto tile = ctx.lengthContext().resolveRect(
+ attrs.fX.isValid() ? *attrs.fX.get() : SkSVGLength(0),
+ attrs.fY.isValid() ? *attrs.fY.get() : SkSVGLength(0),
+ attrs.fWidth.isValid() ? *attrs.fWidth.get() : SkSVGLength(0),
+ attrs.fHeight.isValid() ? *attrs.fHeight.get() : SkSVGLength(0));
+
+ if (tile.isEmpty()) {
+ return false;
+ }
+
+ const SkMatrix* patternTransform = attrs.fPatternTransform.isValid()
+ ? &attrs.fPatternTransform.get()->value()
+ : nullptr;
+
+ SkPictureRecorder recorder;
+ SkSVGRenderContext recordingContext(ctx, recorder.beginRecording(tile));
+
+ // Cannot call into INHERITED:: because SkSVGHiddenContainer skips rendering.
+ contentNode->SkSVGContainer::onRender(recordingContext);
+
+ paint->setShader(SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode,
+ patternTransform,
+ &tile));
+ return true;
+}
diff --git a/experimental/svg/model/SkSVGPattern.h b/experimental/svg/model/SkSVGPattern.h
new file mode 100644
index 0000000000..6ae0fee039
--- /dev/null
+++ b/experimental/svg/model/SkSVGPattern.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGPattern_DEFINED
+#define SkSVGPattern_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+#include "SkSVGTypes.h"
+
+class SkSVGRenderContext;
+
+class SkSVGPattern final : public SkSVGHiddenContainer {
+public:
+ ~SkSVGPattern() override = default;
+
+ static sk_sp<SkSVGPattern> Make() {
+ return sk_sp<SkSVGPattern>(new SkSVGPattern());
+ }
+
+ void setX(const SkSVGLength&);
+ void setY(const SkSVGLength&);
+ void setWidth(const SkSVGLength&);
+ void setHeight(const SkSVGLength&);
+ void setHref(const SkSVGStringType&);
+ void setPatternTransform(const SkSVGTransformType&);
+
+protected:
+ SkSVGPattern();
+
+ void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+ bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const override;
+
+private:
+ struct PatternAttributes {
+ SkTLazy<SkSVGLength> fX,
+ fY,
+ fWidth,
+ fHeight;
+ SkTLazy<SkSVGTransformType> fPatternTransform;
+ } fAttributes;
+
+ SkSVGStringType fHref;
+
+ const SkSVGPattern* resolveHref(const SkSVGRenderContext&, PatternAttributes*) const;
+ const SkSVGPattern* hrefTarget(const SkSVGRenderContext&) const;
+
+ // TODO:
+ // - patternUnits
+ // - patternContentUnits
+
+ typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGPattern_DEFINED
diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp
index 7fa61d48a3..a3e37cb965 100644
--- a/experimental/svg/model/SkSVGRenderContext.cpp
+++ b/experimental/svg/model/SkSVGRenderContext.cpp
@@ -253,6 +253,12 @@ SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
*other.fLengthContext,
*other.fPresentationContext) {}
+SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkCanvas* canvas)
+ : SkSVGRenderContext(canvas,
+ other.fIDMapper,
+ *other.fLengthContext,
+ *other.fPresentationContext) {}
+
SkSVGRenderContext::~SkSVGRenderContext() {
fCanvas->restoreToCount(fCanvasSaveCount);
}
diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h
index b5d8720ab3..69bf9f278f 100644
--- a/experimental/svg/model/SkSVGRenderContext.h
+++ b/experimental/svg/model/SkSVGRenderContext.h
@@ -61,6 +61,7 @@ public:
SkSVGRenderContext(SkCanvas*, const SkSVGIDMapper&, const SkSVGLengthContext&,
const SkSVGPresentationContext&);
SkSVGRenderContext(const SkSVGRenderContext&);
+ SkSVGRenderContext(const SkSVGRenderContext&, SkCanvas*);
~SkSVGRenderContext();
const SkSVGLengthContext& lengthContext() const { return *fLengthContext; }