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