skia2/experimental/svg/model/SkSVGPattern.cpp
Florin Malita f4403e7a78 Revert "[SVG] Replace custom attribute value wrappers with std::variant"
This reverts commit 97e0861b67.

Reason for revert: not all builders support C++17 STL

Original change's description:
> [SVG] Replace custom attribute value wrappers with std::variant
> 
> Less boilerplate, more STL.
> 
> Change-Id: I601f75877d60085cbf3d39f401543fbe9c086f90
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/282836
> Reviewed-by: Mike Reed <reed@google.com>
> Commit-Queue: Florin Malita <fmalita@chromium.org>

TBR=fmalita@chromium.org,reed@google.com,tdenniston@google.com

Change-Id: If5816f11e9cf85dcec2452229c5675037303b220
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/282847
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
2020-04-10 14:14:22 +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.isEmpty()) {
return nullptr;
}
const auto href = ctx.findNodeById(fHref);
if (!href || href->tag() != SkSVGTag::kPattern) {
return nullptr;
}
return static_cast<const SkSVGPattern*>(href.get());
}
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/SVG11/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()
: 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;
}