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 15 SkSVGPattern::SkSVGPattern() : INHERITED(SkSVGTag::kPattern) {} 16 17 void SkSVGPattern::setX(const SkSVGLength& x) { 18 fAttributes.fX.set(x); 19 } 20 21 void SkSVGPattern::setY(const SkSVGLength& y) { 22 fAttributes.fY.set(y); 23 } 24 25 void SkSVGPattern::setWidth(const SkSVGLength& w) { 26 fAttributes.fWidth.set(w); 27 } 28 29 void SkSVGPattern::setHeight(const SkSVGLength& h) { 30 fAttributes.fHeight.set(h); 31 } 32 33 void SkSVGPattern::setHref(const SkSVGStringType& href) { 34 fHref = std::move(href); 35 } 36 37 void SkSVGPattern::setPatternTransform(const SkSVGTransformType& patternTransform) { 38 fAttributes.fPatternTransform.set(patternTransform); 39 } 40 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 78 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> 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 */ 110 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 140 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