Home | History | Annotate | Download | only in model
      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