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