From 1aa1bb65fd92e1a38c88bc792320a40a67f2a467 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Wed, 11 Oct 2017 14:34:33 -0400 Subject: [SVGDom] Initial support https://www.w3.org/TR/SVG/pservers.html#Patterns Change-Id: I80455c4ae04cf03526f8e8797f40b0b2d24e043f Reviewed-on: https://skia-review.googlesource.com/58461 Commit-Queue: Florin Malita Reviewed-by: Robert Phillips --- experimental/svg/model/SkSVGAttribute.h | 1 + experimental/svg/model/SkSVGDOM.cpp | 3 + experimental/svg/model/SkSVGNode.h | 1 + experimental/svg/model/SkSVGPattern.cpp | 170 ++++++++++++++++++++++++++ experimental/svg/model/SkSVGPattern.h | 59 +++++++++ experimental/svg/model/SkSVGRenderContext.cpp | 6 + experimental/svg/model/SkSVGRenderContext.h | 1 + 7 files changed, 241 insertions(+) create mode 100644 experimental/svg/model/SkSVGPattern.cpp create mode 100644 experimental/svg/model/SkSVGPattern.h (limited to 'experimental/svg/model') diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index 9ba38f944c..71eeb916b0 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -29,6 +29,7 @@ enum class SkSVGAttribute { kHref, kOffset, kOpacity, + kPatternTransform, kPoints, kR, // , : radius kRx, // ,: horizontal (corner) radius diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index 7d6b0b75cd..7bb524b48c 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -20,6 +20,7 @@ #include "SkSVGLinearGradient.h" #include "SkSVGNode.h" #include "SkSVGPath.h" +#include "SkSVGPattern.h" #include "SkSVGPoly.h" #include "SkSVGRadialGradient.h" #include "SkSVGRect.h" @@ -294,6 +295,7 @@ SortedDictionaryEntry gAttributeParseInfo[] = { { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, { "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }}, { "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }}, + { "patternTransform" , { SkSVGAttribute::kPatternTransform , SetTransformAttribute }}, { "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }}, { "r" , { SkSVGAttribute::kR , SetLengthAttribute }}, { "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }}, @@ -330,6 +332,7 @@ SortedDictionaryEntry(*)()> gTagFactories[] = { { "line" , []() -> sk_sp { return SkSVGLine::Make(); }}, { "linearGradient", []() -> sk_sp { return SkSVGLinearGradient::Make(); }}, { "path" , []() -> sk_sp { return SkSVGPath::Make(); }}, + { "pattern" , []() -> sk_sp { return SkSVGPattern::Make(); }}, { "polygon" , []() -> sk_sp { return SkSVGPoly::MakePolygon(); }}, { "polyline" , []() -> sk_sp { return SkSVGPoly::MakePolyline(); }}, { "radialGradient", []() -> sk_sp { return SkSVGRadialGradient::Make(); }}, diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h index c0190c4b0c..c9955d1638 100644 --- a/experimental/svg/model/SkSVGNode.h +++ b/experimental/svg/model/SkSVGNode.h @@ -27,6 +27,7 @@ enum class SkSVGTag { kLine, kLinearGradient, kPath, + kPattern, kPolygon, kPolyline, kRadialGradient, diff --git a/experimental/svg/model/SkSVGPattern.cpp b/experimental/svg/model/SkSVGPattern.cpp new file mode 100644 index 0000000000..ef334d7cb0 --- /dev/null +++ b/experimental/svg/model/SkSVGPattern.cpp @@ -0,0 +1,170 @@ +/* + * 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 "SkSVGPattern.h" + +#include "SkPictureRecorder.h" +#include "SkShader.h" +#include "SkSVGRenderContext.h" +#include "SkSVGValue.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()) { + this->setX(*x); + } + break; + case SkSVGAttribute::kY: + if (const auto* y = v.as()) { + this->setY(*y); + } + break; + case SkSVGAttribute::kWidth: + if (const auto* w = v.as()) { + this->setWidth(*w); + } + break; + case SkSVGAttribute::kHeight: + if (const auto* h = v.as()) { + this->setHeight(*h); + } + break; + case SkSVGAttribute::kHref: + if (const auto* href = v.as()) { + this->setHref(*href); + } + break; + case SkSVGAttribute::kPatternTransform: + if (const auto* t = v.as()) { + 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(href); +} + +template +bool inherit_if_needed(const SkTLazy& src, SkTLazy& 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(SkShader::MakePictureShader(recorder.finishRecordingAsPicture(), + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, + patternTransform, + &tile)); + return true; +} diff --git a/experimental/svg/model/SkSVGPattern.h b/experimental/svg/model/SkSVGPattern.h new file mode 100644 index 0000000000..6ae0fee039 --- /dev/null +++ b/experimental/svg/model/SkSVGPattern.h @@ -0,0 +1,59 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGPattern_DEFINED +#define SkSVGPattern_DEFINED + +#include "SkSVGHiddenContainer.h" +#include "SkSVGTypes.h" + +class SkSVGRenderContext; + +class SkSVGPattern final : public SkSVGHiddenContainer { +public: + ~SkSVGPattern() override = default; + + static sk_sp Make() { + return sk_sp(new SkSVGPattern()); + } + + void setX(const SkSVGLength&); + void setY(const SkSVGLength&); + void setWidth(const SkSVGLength&); + void setHeight(const SkSVGLength&); + void setHref(const SkSVGStringType&); + void setPatternTransform(const SkSVGTransformType&); + +protected: + SkSVGPattern(); + + void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + + bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const override; + +private: + struct PatternAttributes { + SkTLazy fX, + fY, + fWidth, + fHeight; + SkTLazy fPatternTransform; + } fAttributes; + + SkSVGStringType fHref; + + const SkSVGPattern* resolveHref(const SkSVGRenderContext&, PatternAttributes*) const; + const SkSVGPattern* hrefTarget(const SkSVGRenderContext&) const; + + // TODO: + // - patternUnits + // - patternContentUnits + + typedef SkSVGHiddenContainer INHERITED; +}; + +#endif // SkSVGPattern_DEFINED diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp index 7fa61d48a3..a3e37cb965 100644 --- a/experimental/svg/model/SkSVGRenderContext.cpp +++ b/experimental/svg/model/SkSVGRenderContext.cpp @@ -253,6 +253,12 @@ SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other) *other.fLengthContext, *other.fPresentationContext) {} +SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkCanvas* canvas) + : SkSVGRenderContext(canvas, + other.fIDMapper, + *other.fLengthContext, + *other.fPresentationContext) {} + SkSVGRenderContext::~SkSVGRenderContext() { fCanvas->restoreToCount(fCanvasSaveCount); } diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h index b5d8720ab3..69bf9f278f 100644 --- a/experimental/svg/model/SkSVGRenderContext.h +++ b/experimental/svg/model/SkSVGRenderContext.h @@ -61,6 +61,7 @@ public: SkSVGRenderContext(SkCanvas*, const SkSVGIDMapper&, const SkSVGLengthContext&, const SkSVGPresentationContext&); SkSVGRenderContext(const SkSVGRenderContext&); + SkSVGRenderContext(const SkSVGRenderContext&, SkCanvas*); ~SkSVGRenderContext(); const SkSVGLengthContext& lengthContext() const { return *fLengthContext; } -- cgit v1.2.3