skia2/experimental/svg/model/SkSVGPattern.cpp
Mike Klein c0bd9f9fe5 rewrite includes to not need so much -Ifoo
Current strategy: everything from the top

Things to look at first are the manual changes:

   - added tools/rewrite_includes.py
   - removed -Idirectives from BUILD.gn
   - various compile.sh simplifications
   - tweak tools/embed_resources.py
   - update gn/find_headers.py to write paths from the top
   - update gn/gn_to_bp.py SkUserConfig.h layout
     so that #include "include/config/SkUserConfig.h" always
     gets the header we want.

No-Presubmit: true
Change-Id: I73a4b181654e0e38d229bc456c0d0854bae3363e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/209706
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Hal Canary <halcanary@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
2019-04-24 16:27:11 +00:00

171 lines
5.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "experimental/svg/model/SkSVGPattern.h"
#include "experimental/svg/model/SkSVGRenderContext.h"
#include "experimental/svg/model/SkSVGValue.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkShader.h"
SkSVGPattern::SkSVGPattern() : INHERITED(SkSVGTag::kPattern) {}
void SkSVGPattern::setX(const SkSVGLength& x) {
fAttributes.fX.set(x);
}
void SkSVGPattern::setY(const SkSVGLength& y) {
fAttributes.fY.set(y);
}
void SkSVGPattern::setWidth(const SkSVGLength& w) {
fAttributes.fWidth.set(w);
}
void SkSVGPattern::setHeight(const SkSVGLength& h) {
fAttributes.fHeight.set(h);
}
void SkSVGPattern::setHref(const SkSVGStringType& href) {
fHref = std::move(href);
}
void SkSVGPattern::setPatternTransform(const SkSVGTransformType& patternTransform) {
fAttributes.fPatternTransform.set(patternTransform);
}
void SkSVGPattern::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
switch (attr) {
case SkSVGAttribute::kX:
if (const auto* x = v.as<SkSVGLengthValue>()) {
this->setX(*x);
}
break;
case SkSVGAttribute::kY:
if (const auto* y = v.as<SkSVGLengthValue>()) {
this->setY(*y);
}
break;
case SkSVGAttribute::kWidth:
if (const auto* w = v.as<SkSVGLengthValue>()) {
this->setWidth(*w);
}
break;
case SkSVGAttribute::kHeight:
if (const auto* h = v.as<SkSVGLengthValue>()) {
this->setHeight(*h);
}
break;
case SkSVGAttribute::kHref:
if (const auto* href = v.as<SkSVGStringValue>()) {
this->setHref(*href);
}
break;
case SkSVGAttribute::kPatternTransform:
if (const auto* t = v.as<SkSVGTransformValue>()) {
this->setPatternTransform(*t);
}
break;
default:
this->INHERITED::onSetAttribute(attr, v);
}
}
const SkSVGPattern* SkSVGPattern::hrefTarget(const SkSVGRenderContext& ctx) const {
if (fHref.value().isEmpty()) {
return nullptr;
}
const auto* href = ctx.findNodeById(fHref);
if (!href || href->tag() != SkSVGTag::kPattern) {
return nullptr;
}
return static_cast<const SkSVGPattern*>(href);
}
template <typename T>
bool inherit_if_needed(const SkTLazy<T>& src, SkTLazy<T>& dst) {
if (!dst.isValid()) {
dst = src;
return true;
}
return false;
}
/* https://www.w3.org/TR/SVG/pservers.html#PatternElementHrefAttribute
*
* Any attributes which are defined on the referenced element which are not defined on this element
* are inherited by this element. If this element has no children, and the referenced element does
* (possibly due to its own xlink:href attribute), then this element inherits the children from
* the referenced element. Inheritance can be indirect to an arbitrary level; thus, if the
* referenced element inherits attributes or children due to its own xlink:href attribute, then
* the current element can inherit those attributes or children.
*/
const SkSVGPattern* SkSVGPattern::resolveHref(const SkSVGRenderContext& ctx,
PatternAttributes* attrs) const {
const SkSVGPattern *currentNode = this,
*contentNode = this;
do {
// Bitwise OR to avoid short-circuiting.
const bool didInherit =
inherit_if_needed(currentNode->fAttributes.fX , attrs->fX) |
inherit_if_needed(currentNode->fAttributes.fY , attrs->fY) |
inherit_if_needed(currentNode->fAttributes.fWidth , attrs->fWidth) |
inherit_if_needed(currentNode->fAttributes.fHeight , attrs->fHeight) |
inherit_if_needed(currentNode->fAttributes.fPatternTransform, attrs->fPatternTransform);
if (!contentNode->hasChildren()) {
contentNode = currentNode;
}
if (contentNode->hasChildren() && !didInherit) {
// All attributes have been resolved, and a valid content node has been found.
// We can terminate the href chain early.
break;
}
// TODO: reference loop mitigation.
currentNode = currentNode->hrefTarget(ctx);
} while (currentNode);
return contentNode;
}
bool SkSVGPattern::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
PatternAttributes attrs;
const auto* contentNode = this->resolveHref(ctx, &attrs);
const auto tile = ctx.lengthContext().resolveRect(
attrs.fX.isValid() ? *attrs.fX.get() : SkSVGLength(0),
attrs.fY.isValid() ? *attrs.fY.get() : SkSVGLength(0),
attrs.fWidth.isValid() ? *attrs.fWidth.get() : SkSVGLength(0),
attrs.fHeight.isValid() ? *attrs.fHeight.get() : SkSVGLength(0));
if (tile.isEmpty()) {
return false;
}
const SkMatrix* patternTransform = attrs.fPatternTransform.isValid()
? &attrs.fPatternTransform.get()->value()
: nullptr;
SkPictureRecorder recorder;
SkSVGRenderContext recordingContext(ctx, recorder.beginRecording(tile));
// Cannot call into INHERITED:: because SkSVGHiddenContainer skips rendering.
contentNode->SkSVGContainer::onRender(recordingContext);
paint->setShader(recorder.finishRecordingAsPicture()->makeShader(
SkTileMode::kRepeat,
SkTileMode::kRepeat,
patternTransform,
&tile));
return true;
}