aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/svg/model/SkSVGPattern.cpp
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 /experimental/svg/model/SkSVGPattern.cpp
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>
Diffstat (limited to 'experimental/svg/model/SkSVGPattern.cpp')
-rw-r--r--experimental/svg/model/SkSVGPattern.cpp170
1 files changed, 170 insertions, 0 deletions
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;
+}