blob: 9f3a3e6ec6989d522f5204d17d414e91df4eed41 [file] [log] [blame]
fmalita6ceef3d2016-07-26 18:46:34 -07001/*
2 * Copyright 2016 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkMatrix.h"
10#include "include/pathops/SkPathOps.h"
Mike Klein8aa0edf2020-10-16 11:04:18 -050011#include "include/private/SkTPin.h"
Florin Malitab3418102020-10-15 18:10:29 -040012#include "modules/svg/include/SkSVGNode.h"
13#include "modules/svg/include/SkSVGRenderContext.h"
14#include "modules/svg/include/SkSVGValue.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/core/SkTLazy.h"
fmalita6ceef3d2016-07-26 18:46:34 -070016
Tyler Denniston04e03bc2020-12-09 14:16:25 -050017SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) {
18 // Uninherited presentation attributes need a non-null default value.
19 fPresentationAttributes.fStopColor.set(SkSVGColor(SK_ColorBLACK));
20 fPresentationAttributes.fStopOpacity.set(SkSVGNumberType(1.0f));
Tyler Denniston8ed04432020-12-10 15:51:04 -050021 fPresentationAttributes.fFloodColor.set(SkSVGColor(SK_ColorBLACK));
22 fPresentationAttributes.fFloodOpacity.set(SkSVGNumberType(1.0f));
Tyler Denniston32b30892021-01-26 14:36:32 -050023 fPresentationAttributes.fLightingColor.set(SkSVGColor(SK_ColorWHITE));
Tyler Denniston04e03bc2020-12-09 14:16:25 -050024}
fmalita6ceef3d2016-07-26 18:46:34 -070025
26SkSVGNode::~SkSVGNode() { }
27
fmalita397a5172016-08-08 11:38:55 -070028void SkSVGNode::render(const SkSVGRenderContext& ctx) const {
Tyler Denniston53281c72020-10-22 15:54:24 -040029 SkSVGRenderContext localContext(ctx, this);
fmalita6ceef3d2016-07-26 18:46:34 -070030
fmalita397a5172016-08-08 11:38:55 -070031 if (this->onPrepareToRender(&localContext)) {
32 this->onRender(localContext);
fmalita6ceef3d2016-07-26 18:46:34 -070033 }
fmalita397a5172016-08-08 11:38:55 -070034}
fmalita6ceef3d2016-07-26 18:46:34 -070035
fmalita28d5b722016-09-12 17:06:47 -070036bool SkSVGNode::asPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
37 SkSVGRenderContext localContext(ctx);
38
39 return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint);
40}
41
Florin Malitace8840e2016-12-08 09:26:47 -050042SkPath SkSVGNode::asPath(const SkSVGRenderContext& ctx) const {
43 SkSVGRenderContext localContext(ctx);
Florin Malita7d529882016-12-08 16:04:24 -050044 if (!this->onPrepareToRender(&localContext)) {
45 return SkPath();
46 }
47
48 SkPath path = this->onAsPath(localContext);
49
50 if (const auto* clipPath = localContext.clipPath()) {
51 // There is a clip-path present on the current node.
52 Op(path, *clipPath, kIntersect_SkPathOp, &path);
53 }
54
55 return path;
Florin Malitace8840e2016-12-08 09:26:47 -050056}
57
Tyler Dennistonf548a022020-10-27 15:02:02 -040058SkRect SkSVGNode::objectBoundingBox(const SkSVGRenderContext& ctx) const {
59 return this->onObjectBoundingBox(ctx);
Tyler Denniston53281c72020-10-22 15:54:24 -040060}
61
fmalita397a5172016-08-08 11:38:55 -070062bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
fmalitabef51c22016-09-20 15:45:57 -070063 ctx->applyPresentationAttributes(fPresentationAttributes,
64 this->hasChildren() ? 0 : SkSVGRenderContext::kLeaf);
Florin Malitaffe6ae42017-10-12 11:33:28 -040065
66 // visibility:hidden disables rendering
John Stilesa008b0f2020-08-16 08:48:02 -040067 const auto visibility = ctx->presentationContext().fInherited.fVisibility->type();
Florin Malitaffe6ae42017-10-12 11:33:28 -040068 return visibility != SkSVGVisibility::Type::kHidden;
fmalita6ceef3d2016-07-26 18:46:34 -070069}
70
Florin Malitaf4403e72020-04-10 14:14:04 +000071void SkSVGNode::setAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
fmalita6ceef3d2016-07-26 18:46:34 -070072 this->onSetAttribute(attr, v);
73}
74
Florin Malita09294252020-04-09 08:54:29 -040075template <typename T>
76void SetInheritedByDefault(SkTLazy<T>& presentation_attribute, const T& value) {
77 if (value.type() != T::Type::kInherit) {
78 presentation_attribute.set(value);
79 } else {
80 // kInherited values are semantically equivalent to
81 // the absence of a local presentation attribute.
82 presentation_attribute.reset();
83 }
84}
85
Florin Malita8c425672020-11-06 13:49:37 -050086bool SkSVGNode::parseAndSetAttribute(const char* n, const char* v) {
Tyler Denniston79832e32020-11-18 09:34:07 -050087#define PARSE_AND_SET(svgName, attrName) \
88 this->set##attrName( \
89 SkSVGAttributeParser::parseProperty<decltype(fPresentationAttributes.f##attrName)>( \
90 svgName, n, v))
91
Tyler Denniston7bb85db2021-01-13 12:08:04 -050092 return PARSE_AND_SET( "clip-path" , ClipPath)
93 || PARSE_AND_SET("clip-rule" , ClipRule)
94 || PARSE_AND_SET("color" , Color)
Florin Malita73d57bf2021-01-15 08:58:09 -050095 || PARSE_AND_SET("color-interpolation" , ColorInterpolation)
Tyler Denniston7bb85db2021-01-13 12:08:04 -050096 || PARSE_AND_SET("color-interpolation-filters", ColorInterpolationFilters)
97 || PARSE_AND_SET("fill" , Fill)
98 || PARSE_AND_SET("fill-opacity" , FillOpacity)
99 || PARSE_AND_SET("fill-rule" , FillRule)
100 || PARSE_AND_SET("filter" , Filter)
101 || PARSE_AND_SET("flood-color" , FloodColor)
102 || PARSE_AND_SET("flood-opacity" , FloodOpacity)
103 || PARSE_AND_SET("font-family" , FontFamily)
104 || PARSE_AND_SET("font-size" , FontSize)
105 || PARSE_AND_SET("font-style" , FontStyle)
106 || PARSE_AND_SET("font-weight" , FontWeight)
Tyler Denniston32b30892021-01-26 14:36:32 -0500107 || PARSE_AND_SET("lighting-color" , LightingColor)
Tyler Denniston7bb85db2021-01-13 12:08:04 -0500108 || PARSE_AND_SET("mask" , Mask)
109 || PARSE_AND_SET("opacity" , Opacity)
110 || PARSE_AND_SET("stop-color" , StopColor)
111 || PARSE_AND_SET("stop-opacity" , StopOpacity)
112 || PARSE_AND_SET("stroke" , Stroke)
113 || PARSE_AND_SET("stroke-dasharray" , StrokeDashArray)
114 || PARSE_AND_SET("stroke-dashoffset" , StrokeDashOffset)
115 || PARSE_AND_SET("stroke-linecap" , StrokeLineCap)
116 || PARSE_AND_SET("stroke-linejoin" , StrokeLineJoin)
117 || PARSE_AND_SET("stroke-miterlimit" , StrokeMiterLimit)
118 || PARSE_AND_SET("stroke-opacity" , StrokeOpacity)
119 || PARSE_AND_SET("stroke-width" , StrokeWidth)
120 || PARSE_AND_SET("text-anchor" , TextAnchor)
121 || PARSE_AND_SET("visibility" , Visibility);
Tyler Denniston79832e32020-11-18 09:34:07 -0500122
123#undef PARSE_AND_SET
Tyler Denniston57154992020-11-04 16:08:30 -0500124}
Tyler Denniston1f4cd072021-02-05 09:08:33 -0500125
126// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
127SkMatrix SkSVGNode::ComputeViewboxMatrix(const SkRect& viewBox,
128 const SkRect& viewPort,
129 SkSVGPreserveAspectRatio par) {
130 SkASSERT(!viewBox.isEmpty());
131 SkASSERT(!viewPort.isEmpty());
132
133 auto compute_scale = [&]() -> SkV2 {
134 const auto sx = viewPort.width() / viewBox.width(),
135 sy = viewPort.height() / viewBox.height();
136
137 if (par.fAlign == SkSVGPreserveAspectRatio::kNone) {
138 // none -> anisotropic scaling, regardless of fScale
139 return {sx, sy};
140 }
141
142 // isotropic scaling
143 const auto s = par.fScale == SkSVGPreserveAspectRatio::kMeet
144 ? std::min(sx, sy)
145 : std::max(sx, sy);
146 return {s, s};
147 };
148
149 auto compute_trans = [&](const SkV2& scale) -> SkV2 {
150 static constexpr float gAlignCoeffs[] = {
151 0.0f, // Min
152 0.5f, // Mid
153 1.0f // Max
154 };
155
156 const size_t x_coeff = par.fAlign >> 0 & 0x03,
157 y_coeff = par.fAlign >> 2 & 0x03;
158
159 SkASSERT(x_coeff < SK_ARRAY_COUNT(gAlignCoeffs) &&
160 y_coeff < SK_ARRAY_COUNT(gAlignCoeffs));
161
162 const auto tx = -viewBox.x() * scale.x,
163 ty = -viewBox.y() * scale.y,
164 dx = viewPort.width() - viewBox.width() * scale.x,
165 dy = viewPort.height() - viewBox.height() * scale.y;
166
167 return {
168 tx + dx * gAlignCoeffs[x_coeff],
169 ty + dy * gAlignCoeffs[y_coeff]
170 };
171 };
172
173 const auto s = compute_scale(),
174 t = compute_trans(s);
175
176 return SkMatrix::Translate(t.x, t.y) *
177 SkMatrix::Scale(s.x, s.y);
178}