• 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 "modules/svg/include/SkSVGPattern.h"
9 
10 #include "include/core/SkPicture.h"
11 #include "include/core/SkPictureRecorder.h"
12 #include "include/core/SkShader.h"
13 #include "include/core/SkTileMode.h"
14 #include "modules/svg/include/SkSVGRenderContext.h"
15 #include "modules/svg/include/SkSVGValue.h"
16 
SkSVGPattern()17 SkSVGPattern::SkSVGPattern() : INHERITED(SkSVGTag::kPattern) {}
18 
parseAndSetAttribute(const char * name,const char * value)19 bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) {
20     return INHERITED::parseAndSetAttribute(name, value) ||
21            this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
22            this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value)) ||
23            this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", name, value)) ||
24            this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", name, value)) ||
25            this->setPatternTransform(SkSVGAttributeParser::parse<SkSVGTransformType>(
26                    "patternTransform", name, value)) ||
27            this->setHref(SkSVGAttributeParser::parse<SkSVGIRI>("xlink:href", name, value));
28 }
29 
hrefTarget(const SkSVGRenderContext & ctx) const30 const SkSVGPattern* SkSVGPattern::hrefTarget(const SkSVGRenderContext& ctx) const {
31     if (fHref.iri().isEmpty()) {
32         return nullptr;
33     }
34 
35     const auto href = ctx.findNodeById(fHref);
36     if (!href || href->tag() != SkSVGTag::kPattern) {
37         return nullptr;
38     }
39 
40     return static_cast<const SkSVGPattern*>(href.get());
41 }
42 
43 template <typename T>
inherit_if_needed(const SkTLazy<T> & src,SkTLazy<T> & dst)44 int inherit_if_needed(const SkTLazy<T>& src, SkTLazy<T>& dst) {
45     if (!dst.isValid()) {
46         dst = src;
47         return 1;
48     }
49 
50     return 0;
51 }
52 
53 /* https://www.w3.org/TR/SVG11/pservers.html#PatternElementHrefAttribute
54  *
55  * Any attributes which are defined on the referenced element which are not defined on this element
56  * are inherited by this element. If this element has no children, and the referenced element does
57  * (possibly due to its own ‘xlink:href’ attribute), then this element inherits the children from
58  * the referenced element. Inheritance can be indirect to an arbitrary level; thus, if the
59  * referenced element inherits attributes or children due to its own ‘xlink:href’ attribute, then
60  * the current element can inherit those attributes or children.
61  */
resolveHref(const SkSVGRenderContext & ctx,PatternAttributes * attrs) const62 const SkSVGPattern* SkSVGPattern::resolveHref(const SkSVGRenderContext& ctx,
63                                               PatternAttributes* attrs) const {
64     const SkSVGPattern *currentNode = this,
65                        *contentNode = this;
66     do {
67         // Bitwise OR to avoid short-circuiting.
68         const bool didInherit =
69             inherit_if_needed(currentNode->fX               , attrs->fX)      |
70             inherit_if_needed(currentNode->fY               , attrs->fY)      |
71             inherit_if_needed(currentNode->fWidth           , attrs->fWidth)  |
72             inherit_if_needed(currentNode->fHeight          , attrs->fHeight) |
73             inherit_if_needed(currentNode->fPatternTransform, attrs->fPatternTransform);
74 
75         if (!contentNode->hasChildren()) {
76             contentNode = currentNode;
77         }
78 
79         if (contentNode->hasChildren() && !didInherit) {
80             // All attributes have been resolved, and a valid content node has been found.
81             // We can terminate the href chain early.
82             break;
83         }
84 
85         // TODO: reference loop mitigation.
86         currentNode = currentNode->hrefTarget(ctx);
87     } while (currentNode);
88 
89     return contentNode;
90 }
91 
onAsPaint(const SkSVGRenderContext & ctx,SkPaint * paint) const92 bool SkSVGPattern::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
93     PatternAttributes attrs;
94     const auto* contentNode = this->resolveHref(ctx, &attrs);
95 
96     const auto tile = ctx.lengthContext().resolveRect(
97             attrs.fX.isValid()      ? *attrs.fX      : SkSVGLength(0),
98             attrs.fY.isValid()      ? *attrs.fY      : SkSVGLength(0),
99             attrs.fWidth.isValid()  ? *attrs.fWidth  : SkSVGLength(0),
100             attrs.fHeight.isValid() ? *attrs.fHeight : SkSVGLength(0));
101 
102     if (tile.isEmpty()) {
103         return false;
104     }
105 
106     const SkMatrix* patternTransform = attrs.fPatternTransform.isValid()
107             ? attrs.fPatternTransform.get()
108             : nullptr;
109 
110     SkPictureRecorder recorder;
111     SkSVGRenderContext recordingContext(ctx, recorder.beginRecording(tile));
112 
113     // Cannot call into INHERITED:: because SkSVGHiddenContainer skips rendering.
114     contentNode->SkSVGContainer::onRender(recordingContext);
115 
116     paint->setShader(recorder.finishRecordingAsPicture()->makeShader(
117                                                  SkTileMode::kRepeat,
118                                                  SkTileMode::kRepeat,
119                                                  SkFilterMode::kLinear,
120                                                  patternTransform,
121                                                  &tile));
122     return true;
123 }
124