• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkSVGPattern.h"
9 
10 #include "SkPictureRecorder.h"
11 #include "SkShader.h"
12 #include "SkSVGRenderContext.h"
13 #include "SkSVGValue.h"
14 
SkSVGPattern()15 SkSVGPattern::SkSVGPattern() : INHERITED(SkSVGTag::kPattern) {}
16 
setX(const SkSVGLength & x)17 void SkSVGPattern::setX(const SkSVGLength& x) {
18     fAttributes.fX.set(x);
19 }
20 
setY(const SkSVGLength & y)21 void SkSVGPattern::setY(const SkSVGLength& y) {
22     fAttributes.fY.set(y);
23 }
24 
setWidth(const SkSVGLength & w)25 void SkSVGPattern::setWidth(const SkSVGLength& w) {
26     fAttributes.fWidth.set(w);
27 }
28 
setHeight(const SkSVGLength & h)29 void SkSVGPattern::setHeight(const SkSVGLength& h) {
30     fAttributes.fHeight.set(h);
31 }
32 
setHref(const SkSVGStringType & href)33 void SkSVGPattern::setHref(const SkSVGStringType& href) {
34     fHref = std::move(href);
35 }
36 
setPatternTransform(const SkSVGTransformType & patternTransform)37 void SkSVGPattern::setPatternTransform(const SkSVGTransformType& patternTransform) {
38     fAttributes.fPatternTransform.set(patternTransform);
39 }
40 
onSetAttribute(SkSVGAttribute attr,const SkSVGValue & v)41 void SkSVGPattern::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
42     switch (attr) {
43     case SkSVGAttribute::kX:
44         if (const auto* x = v.as<SkSVGLengthValue>()) {
45             this->setX(*x);
46         }
47         break;
48     case SkSVGAttribute::kY:
49         if (const auto* y = v.as<SkSVGLengthValue>()) {
50             this->setY(*y);
51         }
52         break;
53     case SkSVGAttribute::kWidth:
54         if (const auto* w = v.as<SkSVGLengthValue>()) {
55             this->setWidth(*w);
56         }
57         break;
58     case SkSVGAttribute::kHeight:
59         if (const auto* h = v.as<SkSVGLengthValue>()) {
60             this->setHeight(*h);
61         }
62         break;
63     case SkSVGAttribute::kHref:
64         if (const auto* href = v.as<SkSVGStringValue>()) {
65             this->setHref(*href);
66         }
67         break;
68     case SkSVGAttribute::kPatternTransform:
69         if (const auto* t = v.as<SkSVGTransformValue>()) {
70             this->setPatternTransform(*t);
71         }
72         break;
73     default:
74         this->INHERITED::onSetAttribute(attr, v);
75     }
76 }
77 
hrefTarget(const SkSVGRenderContext & ctx) const78 const SkSVGPattern* SkSVGPattern::hrefTarget(const SkSVGRenderContext& ctx) const {
79     if (fHref.value().isEmpty()) {
80         return nullptr;
81     }
82 
83     const auto* href = ctx.findNodeById(fHref);
84     if (!href || href->tag() != SkSVGTag::kPattern) {
85         return nullptr;
86     }
87 
88     return static_cast<const SkSVGPattern*>(href);
89 }
90 
91 template <typename T>
inherit_if_needed(const SkTLazy<T> & src,SkTLazy<T> & dst)92 bool inherit_if_needed(const SkTLazy<T>& src, SkTLazy<T>& dst) {
93     if (!dst.isValid()) {
94         dst = src;
95         return true;
96     }
97 
98     return false;
99 }
100 
101 /* https://www.w3.org/TR/SVG/pservers.html#PatternElementHrefAttribute
102  *
103  * Any attributes which are defined on the referenced element which are not defined on this element
104  * are inherited by this element. If this element has no children, and the referenced element does
105  * (possibly due to its own ‘xlink:href’ attribute), then this element inherits the children from
106  * the referenced element. Inheritance can be indirect to an arbitrary level; thus, if the
107  * referenced element inherits attributes or children due to its own ‘xlink:href’ attribute, then
108  * the current element can inherit those attributes or children.
109  */
resolveHref(const SkSVGRenderContext & ctx,PatternAttributes * attrs) const110 const SkSVGPattern* SkSVGPattern::resolveHref(const SkSVGRenderContext& ctx,
111                                               PatternAttributes* attrs) const {
112     const SkSVGPattern *currentNode = this,
113                        *contentNode = this;
114     do {
115         // Bitwise OR to avoid short-circuiting.
116         const bool didInherit =
117             inherit_if_needed(currentNode->fAttributes.fX               , attrs->fX)      |
118             inherit_if_needed(currentNode->fAttributes.fY               , attrs->fY)      |
119             inherit_if_needed(currentNode->fAttributes.fWidth           , attrs->fWidth)  |
120             inherit_if_needed(currentNode->fAttributes.fHeight          , attrs->fHeight) |
121             inherit_if_needed(currentNode->fAttributes.fPatternTransform, attrs->fPatternTransform);
122 
123         if (!contentNode->hasChildren()) {
124             contentNode = currentNode;
125         }
126 
127         if (contentNode->hasChildren() && !didInherit) {
128             // All attributes have been resolved, and a valid content node has been found.
129             // We can terminate the href chain early.
130             break;
131         }
132 
133         // TODO: reference loop mitigation.
134         currentNode = currentNode->hrefTarget(ctx);
135     } while (currentNode);
136 
137     return contentNode;
138 }
139 
onAsPaint(const SkSVGRenderContext & ctx,SkPaint * paint) const140 bool SkSVGPattern::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
141     PatternAttributes attrs;
142     const auto* contentNode = this->resolveHref(ctx, &attrs);
143 
144     const auto tile = ctx.lengthContext().resolveRect(
145             attrs.fX.isValid()      ? *attrs.fX.get()      : SkSVGLength(0),
146             attrs.fY.isValid()      ? *attrs.fY.get()      : SkSVGLength(0),
147             attrs.fWidth.isValid()  ? *attrs.fWidth.get()  : SkSVGLength(0),
148             attrs.fHeight.isValid() ? *attrs.fHeight.get() : SkSVGLength(0));
149 
150     if (tile.isEmpty()) {
151         return false;
152     }
153 
154     const SkMatrix* patternTransform = attrs.fPatternTransform.isValid()
155             ? &attrs.fPatternTransform.get()->value()
156             : nullptr;
157 
158     SkPictureRecorder recorder;
159     SkSVGRenderContext recordingContext(ctx, recorder.beginRecording(tile));
160 
161     // Cannot call into INHERITED:: because SkSVGHiddenContainer skips rendering.
162     contentNode->SkSVGContainer::onRender(recordingContext);
163 
164     paint->setShader(SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
165                                                  SkShader::kRepeat_TileMode,
166                                                  SkShader::kRepeat_TileMode,
167                                                  patternTransform,
168                                                  &tile));
169     return true;
170 }
171