blob: a1b7debc4b218b92c1f5f2b7fe218c6d032353b0 [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
Florin Malitab3418102020-10-15 18:10:29 -04008#include "modules/svg/include/SkSVGRenderContext.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkCanvas.h"
11#include "include/core/SkPath.h"
12#include "include/effects/SkDashPathEffect.h"
13#include "include/private/SkTo.h"
Florin Malitab3418102020-10-15 18:10:29 -040014#include "modules/svg/include/SkSVGAttribute.h"
15#include "modules/svg/include/SkSVGNode.h"
16#include "modules/svg/include/SkSVGTypes.h"
fmalita6ceef3d2016-07-26 18:46:34 -070017
fmalitabffc2562016-08-03 10:21:11 -070018namespace {
19
20SkScalar length_size_for_type(const SkSize& viewport, SkSVGLengthContext::LengthType t) {
21 switch (t) {
22 case SkSVGLengthContext::LengthType::kHorizontal:
23 return viewport.width();
24 case SkSVGLengthContext::LengthType::kVertical:
25 return viewport.height();
26 case SkSVGLengthContext::LengthType::kOther:
27 return SkScalarSqrt(viewport.width() * viewport.height());
28 }
29
30 SkASSERT(false); // Not reached.
31 return 0;
32}
33
fmalita280e2822016-08-17 14:51:03 -070034// Multipliers for DPI-relative units.
35constexpr SkScalar kINMultiplier = 1.00f;
36constexpr SkScalar kPTMultiplier = kINMultiplier / 72.272f;
37constexpr SkScalar kPCMultiplier = kPTMultiplier * 12;
38constexpr SkScalar kMMMultiplier = kINMultiplier / 25.4f;
39constexpr SkScalar kCMMultiplier = kMMMultiplier * 10;
40
John Stilesa6841be2020-08-06 14:11:56 -040041} // namespace
fmalitabffc2562016-08-03 10:21:11 -070042
43SkScalar SkSVGLengthContext::resolve(const SkSVGLength& l, LengthType t) const {
44 switch (l.unit()) {
45 case SkSVGLength::Unit::kNumber:
fmalita2d961e02016-08-11 09:16:29 -070046 // Fall through.
47 case SkSVGLength::Unit::kPX:
fmalitabffc2562016-08-03 10:21:11 -070048 return l.value();
fmalitabffc2562016-08-03 10:21:11 -070049 case SkSVGLength::Unit::kPercentage:
50 return l.value() * length_size_for_type(fViewport, t) / 100;
fmalita280e2822016-08-17 14:51:03 -070051 case SkSVGLength::Unit::kCM:
52 return l.value() * fDPI * kCMMultiplier;
53 case SkSVGLength::Unit::kMM:
54 return l.value() * fDPI * kMMMultiplier;
55 case SkSVGLength::Unit::kIN:
56 return l.value() * fDPI * kINMultiplier;
57 case SkSVGLength::Unit::kPT:
58 return l.value() * fDPI * kPTMultiplier;
59 case SkSVGLength::Unit::kPC:
60 return l.value() * fDPI * kPCMultiplier;
fmalitabffc2562016-08-03 10:21:11 -070061 default:
62 SkDebugf("unsupported unit type: <%d>\n", l.unit());
fmalita2d961e02016-08-11 09:16:29 -070063 return 0;
fmalitabffc2562016-08-03 10:21:11 -070064 }
fmalitabffc2562016-08-03 10:21:11 -070065}
66
fmalita397a5172016-08-08 11:38:55 -070067SkRect SkSVGLengthContext::resolveRect(const SkSVGLength& x, const SkSVGLength& y,
68 const SkSVGLength& w, const SkSVGLength& h) const {
69 return SkRect::MakeXYWH(
70 this->resolve(x, SkSVGLengthContext::LengthType::kHorizontal),
71 this->resolve(y, SkSVGLengthContext::LengthType::kVertical),
72 this->resolve(w, SkSVGLengthContext::LengthType::kHorizontal),
73 this->resolve(h, SkSVGLengthContext::LengthType::kVertical));
74}
fmalita6ceef3d2016-07-26 18:46:34 -070075
fmalita2d961e02016-08-11 09:16:29 -070076namespace {
fmalita397a5172016-08-08 11:38:55 -070077
fmalita2d961e02016-08-11 09:16:29 -070078SkPaint::Cap toSkCap(const SkSVGLineCap& cap) {
79 switch (cap.type()) {
80 case SkSVGLineCap::Type::kButt:
81 return SkPaint::kButt_Cap;
82 case SkSVGLineCap::Type::kRound:
83 return SkPaint::kRound_Cap;
84 case SkSVGLineCap::Type::kSquare:
85 return SkPaint::kSquare_Cap;
86 default:
87 SkASSERT(false);
88 return SkPaint::kButt_Cap;
fmalita6ceef3d2016-07-26 18:46:34 -070089 }
fmalita6ceef3d2016-07-26 18:46:34 -070090}
91
fmalita2d961e02016-08-11 09:16:29 -070092SkPaint::Join toSkJoin(const SkSVGLineJoin& join) {
93 switch (join.type()) {
94 case SkSVGLineJoin::Type::kMiter:
95 return SkPaint::kMiter_Join;
96 case SkSVGLineJoin::Type::kRound:
97 return SkPaint::kRound_Join;
98 case SkSVGLineJoin::Type::kBevel:
99 return SkPaint::kBevel_Join;
100 default:
101 SkASSERT(false);
102 return SkPaint::kMiter_Join;
fmalita6ceef3d2016-07-26 18:46:34 -0700103 }
fmalita6ceef3d2016-07-26 18:46:34 -0700104}
105
fmalita28d5b722016-09-12 17:06:47 -0700106void applySvgPaint(const SkSVGRenderContext& ctx, const SkSVGPaint& svgPaint, SkPaint* p) {
fmalita2d961e02016-08-11 09:16:29 -0700107 switch (svgPaint.type()) {
108 case SkSVGPaint::Type::kColor:
109 p->setColor(SkColorSetA(svgPaint.color(), p->getAlpha()));
110 break;
fmalita28d5b722016-09-12 17:06:47 -0700111 case SkSVGPaint::Type::kIRI: {
Florin Malita27aeb122020-04-08 15:02:02 -0400112 const auto node = ctx.findNodeById(svgPaint.iri());
fmalita28d5b722016-09-12 17:06:47 -0700113 if (!node || !node->asPaint(ctx, p)) {
114 p->setColor(SK_ColorTRANSPARENT);
115 }
116 break;
117 }
fmalita2d961e02016-08-11 09:16:29 -0700118 case SkSVGPaint::Type::kCurrentColor:
Tyler Denniston6c8314e2020-04-09 14:14:10 -0400119 p->setColor(*ctx.presentationContext().fInherited.fColor);
120 break;
fmalita2d961e02016-08-11 09:16:29 -0700121 case SkSVGPaint::Type::kNone:
122 // Fall through.
123 case SkSVGPaint::Type::kInherit:
124 break;
fmalita6ceef3d2016-07-26 18:46:34 -0700125 }
fmalita6ceef3d2016-07-26 18:46:34 -0700126}
127
fmalitaa26cab02016-08-29 05:54:42 -0700128inline uint8_t opacity_to_alpha(SkScalar o) {
129 return SkTo<uint8_t>(SkScalarRoundToInt(o * 255));
130}
131
fmalita2d961e02016-08-11 09:16:29 -0700132// Commit the selected attribute to the paint cache.
133template <SkSVGAttribute>
134void commitToPaint(const SkSVGPresentationAttributes&,
fmalita28d5b722016-09-12 17:06:47 -0700135 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700136 SkSVGPresentationContext*);
137
138template <>
139void commitToPaint<SkSVGAttribute::kFill>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700140 const SkSVGRenderContext& ctx,
fmalita2d961e02016-08-11 09:16:29 -0700141 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400142 const auto& fill = *attrs.fFill;
Florin Malita09294252020-04-09 08:54:29 -0400143 SkASSERT(fill.type() != SkSVGPaint::Type::kInherit);
144
145 applySvgPaint(ctx, fill, &pctx->fFillPaint);
fmalita6ceef3d2016-07-26 18:46:34 -0700146}
147
fmalita2d961e02016-08-11 09:16:29 -0700148template <>
149void commitToPaint<SkSVGAttribute::kStroke>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700150 const SkSVGRenderContext& ctx,
fmalita2d961e02016-08-11 09:16:29 -0700151 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400152 const auto& stroke = *attrs.fStroke;
Florin Malita09294252020-04-09 08:54:29 -0400153 SkASSERT(stroke.type() != SkSVGPaint::Type::kInherit);
154
155 applySvgPaint(ctx, stroke, &pctx->fStrokePaint);
fmalita2d961e02016-08-11 09:16:29 -0700156}
157
158template <>
159void commitToPaint<SkSVGAttribute::kFillOpacity>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700160 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700161 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400162 pctx->fFillPaint.setAlpha(opacity_to_alpha(*attrs.fFillOpacity));
fmalita2d961e02016-08-11 09:16:29 -0700163}
164
165template <>
Florin Malitaf543a602017-10-13 14:07:44 -0400166void commitToPaint<SkSVGAttribute::kStrokeDashArray>(const SkSVGPresentationAttributes& attrs,
167 const SkSVGRenderContext& ctx,
168 SkSVGPresentationContext* pctx) {
169 const auto& dashArray = attrs.fStrokeDashArray.get();
Florin Malita09294252020-04-09 08:54:29 -0400170 SkASSERT(dashArray->type() != SkSVGDashArray::Type::kInherit);
171
Florin Malitaf543a602017-10-13 14:07:44 -0400172 if (dashArray->type() != SkSVGDashArray::Type::kDashArray) {
173 return;
174 }
175
176 const auto count = dashArray->dashArray().count();
177 SkSTArray<128, SkScalar, true> intervals(count);
178 for (const auto& dash : dashArray->dashArray()) {
179 intervals.push_back(ctx.lengthContext().resolve(dash,
180 SkSVGLengthContext::LengthType::kOther));
181 }
182
183 if (count & 1) {
184 // If an odd number of values is provided, then the list of values
185 // is repeated to yield an even number of values.
186 intervals.push_back_n(count);
Florin Malita5076bd22020-04-09 07:41:52 -0400187 memcpy(intervals.begin() + count, intervals.begin(), count * sizeof(SkScalar));
Florin Malitaf543a602017-10-13 14:07:44 -0400188 }
189
190 SkASSERT((intervals.count() & 1) == 0);
191
John Stilesa008b0f2020-08-16 08:48:02 -0400192 const SkScalar phase = ctx.lengthContext().resolve(*pctx->fInherited.fStrokeDashOffset,
Florin Malitae1dadd72017-10-13 18:18:32 -0400193 SkSVGLengthContext::LengthType::kOther);
Florin Malitaf543a602017-10-13 14:07:44 -0400194 pctx->fStrokePaint.setPathEffect(SkDashPathEffect::Make(intervals.begin(),
195 intervals.count(),
196 phase));
197}
198
199template <>
Florin Malitae1dadd72017-10-13 18:18:32 -0400200void commitToPaint<SkSVGAttribute::kStrokeDashOffset>(const SkSVGPresentationAttributes&,
201 const SkSVGRenderContext&,
202 SkSVGPresentationContext*) {
203 // Applied via kStrokeDashArray.
204}
205
206template <>
fmalita2d961e02016-08-11 09:16:29 -0700207void commitToPaint<SkSVGAttribute::kStrokeLineCap>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700208 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700209 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400210 const auto& cap = *attrs.fStrokeLineCap;
Florin Malita09294252020-04-09 08:54:29 -0400211 SkASSERT(cap.type() != SkSVGLineCap::Type::kInherit);
212
213 pctx->fStrokePaint.setStrokeCap(toSkCap(cap));
fmalita2d961e02016-08-11 09:16:29 -0700214}
215
216template <>
217void commitToPaint<SkSVGAttribute::kStrokeLineJoin>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700218 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700219 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400220 const auto& join = *attrs.fStrokeLineJoin;
Florin Malita09294252020-04-09 08:54:29 -0400221 SkASSERT(join.type() != SkSVGLineJoin::Type::kInherit);
222
223 pctx->fStrokePaint.setStrokeJoin(toSkJoin(join));
fmalita2d961e02016-08-11 09:16:29 -0700224}
225
226template <>
Florin Malita4de426b2017-10-09 12:57:41 -0400227void commitToPaint<SkSVGAttribute::kStrokeMiterLimit>(const SkSVGPresentationAttributes& attrs,
228 const SkSVGRenderContext&,
229 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400230 pctx->fStrokePaint.setStrokeMiter(*attrs.fStrokeMiterLimit);
Florin Malita4de426b2017-10-09 12:57:41 -0400231}
232
233template <>
fmalita2d961e02016-08-11 09:16:29 -0700234void commitToPaint<SkSVGAttribute::kStrokeOpacity>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700235 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700236 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400237 pctx->fStrokePaint.setAlpha(opacity_to_alpha(*attrs.fStrokeOpacity));
fmalita2d961e02016-08-11 09:16:29 -0700238}
239
240template <>
241void commitToPaint<SkSVGAttribute::kStrokeWidth>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700242 const SkSVGRenderContext& ctx,
fmalita2d961e02016-08-11 09:16:29 -0700243 SkSVGPresentationContext* pctx) {
John Stilesa008b0f2020-08-16 08:48:02 -0400244 auto strokeWidth = ctx.lengthContext().resolve(*attrs.fStrokeWidth,
fmalita28d5b722016-09-12 17:06:47 -0700245 SkSVGLengthContext::LengthType::kOther);
fmalita2d961e02016-08-11 09:16:29 -0700246 pctx->fStrokePaint.setStrokeWidth(strokeWidth);
247}
248
Florin Malitae932d4b2016-12-01 13:35:11 -0500249template <>
250void commitToPaint<SkSVGAttribute::kFillRule>(const SkSVGPresentationAttributes&,
251 const SkSVGRenderContext&,
252 SkSVGPresentationContext*) {
253 // Not part of the SkPaint state; applied to the path at render time.
254}
255
Florin Malita57a0edf2017-10-10 11:22:08 -0400256template <>
257void commitToPaint<SkSVGAttribute::kClipRule>(const SkSVGPresentationAttributes&,
258 const SkSVGRenderContext&,
259 SkSVGPresentationContext*) {
260 // Not part of the SkPaint state; applied to the path at clip time.
261}
262
Florin Malitaffe6ae42017-10-12 11:33:28 -0400263template <>
264void commitToPaint<SkSVGAttribute::kVisibility>(const SkSVGPresentationAttributes&,
265 const SkSVGRenderContext&,
266 SkSVGPresentationContext*) {
267 // Not part of the SkPaint state; queried to veto rendering.
268}
269
Tyler Denniston6c8314e2020-04-09 14:14:10 -0400270template <>
271void commitToPaint<SkSVGAttribute::kColor>(const SkSVGPresentationAttributes&,
272 const SkSVGRenderContext&,
273 SkSVGPresentationContext*) {
274 // Not part of the SkPaint state; applied via 'currentColor' color value
275}
276
Florin Malita39fe8c82020-10-20 10:43:03 -0400277template <>
278void commitToPaint<SkSVGAttribute::kFontFamily>(const SkSVGPresentationAttributes&,
279 const SkSVGRenderContext&,
280 SkSVGPresentationContext*) {
281 // Not part of the SkPaint state; applied at render time.
282}
283
284template <>
285void commitToPaint<SkSVGAttribute::kFontSize>(const SkSVGPresentationAttributes&,
286 const SkSVGRenderContext&,
287 SkSVGPresentationContext*) {
288 // Not part of the SkPaint state; applied at render time.
289}
290
291template <>
292void commitToPaint<SkSVGAttribute::kFontStyle>(const SkSVGPresentationAttributes&,
293 const SkSVGRenderContext&,
294 SkSVGPresentationContext*) {
295 // Not part of the SkPaint state; applied at render time.
296}
297
298template <>
299void commitToPaint<SkSVGAttribute::kFontWeight>(const SkSVGPresentationAttributes&,
300 const SkSVGRenderContext&,
301 SkSVGPresentationContext*) {
302 // Not part of the SkPaint state; applied at render time.
303}
304
Florin Malita056385b2020-10-27 22:57:56 -0400305template <>
306void commitToPaint<SkSVGAttribute::kTextAnchor>(const SkSVGPresentationAttributes&,
307 const SkSVGRenderContext&,
308 SkSVGPresentationContext*) {
309 // Not part of the SkPaint state; applied at render time.
310}
311
John Stilesa6841be2020-08-06 14:11:56 -0400312} // namespace
fmalita2d961e02016-08-11 09:16:29 -0700313
314SkSVGPresentationContext::SkSVGPresentationContext()
315 : fInherited(SkSVGPresentationAttributes::MakeInitial()) {
316
317 fFillPaint.setStyle(SkPaint::kFill_Style);
318 fStrokePaint.setStyle(SkPaint::kStroke_Style);
319
320 // TODO: drive AA off presentation attrs also (shape-rendering?)
321 fFillPaint.setAntiAlias(true);
322 fStrokePaint.setAntiAlias(true);
323
324 // Commit initial values to the paint cache.
Leon Scroggins III577536a2020-07-31 13:47:50 -0400325 SkCanvas fakeCanvas(0, 0);
326 SkSVGRenderContext fake(&fakeCanvas, SkSVGIDMapper(), SkSVGLengthContext(SkSize::Make(0, 0)),
Tyler Denniston53281c72020-10-22 15:54:24 -0400327 *this, nullptr);
fmalita28d5b722016-09-12 17:06:47 -0700328
Leon Scroggins III577536a2020-07-31 13:47:50 -0400329 commitToPaint<SkSVGAttribute::kFill>(fInherited, fake, this);
330 commitToPaint<SkSVGAttribute::kFillOpacity>(fInherited, fake, this);
331 commitToPaint<SkSVGAttribute::kStroke>(fInherited, fake, this);
332 commitToPaint<SkSVGAttribute::kStrokeLineCap>(fInherited, fake, this);
333 commitToPaint<SkSVGAttribute::kStrokeLineJoin>(fInherited, fake, this);
334 commitToPaint<SkSVGAttribute::kStrokeMiterLimit>(fInherited, fake, this);
335 commitToPaint<SkSVGAttribute::kStrokeOpacity>(fInherited, fake, this);
336 commitToPaint<SkSVGAttribute::kStrokeWidth>(fInherited, fake, this);
fmalita6ceef3d2016-07-26 18:46:34 -0700337}
fmalita397a5172016-08-08 11:38:55 -0700338
339SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
fmalita28d5b722016-09-12 17:06:47 -0700340 const SkSVGIDMapper& mapper,
fmalita397a5172016-08-08 11:38:55 -0700341 const SkSVGLengthContext& lctx,
Tyler Denniston53281c72020-10-22 15:54:24 -0400342 const SkSVGPresentationContext& pctx,
343 const SkSVGNode* node)
fmalita28d5b722016-09-12 17:06:47 -0700344 : fIDMapper(mapper)
345 , fLengthContext(lctx)
fmalita397a5172016-08-08 11:38:55 -0700346 , fPresentationContext(pctx)
347 , fCanvas(canvas)
Tyler Denniston53281c72020-10-22 15:54:24 -0400348 , fCanvasSaveCount(canvas->getSaveCount())
349 , fNode(node) {}
fmalita397a5172016-08-08 11:38:55 -0700350
351SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
fmalita2d961e02016-08-11 09:16:29 -0700352 : SkSVGRenderContext(other.fCanvas,
fmalita28d5b722016-09-12 17:06:47 -0700353 other.fIDMapper,
fmalita2d961e02016-08-11 09:16:29 -0700354 *other.fLengthContext,
Tyler Denniston53281c72020-10-22 15:54:24 -0400355 *other.fPresentationContext,
356 other.fNode) {}
fmalita397a5172016-08-08 11:38:55 -0700357
Florin Malita1aa1bb62017-10-11 14:34:33 -0400358SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkCanvas* canvas)
359 : SkSVGRenderContext(canvas,
360 other.fIDMapper,
361 *other.fLengthContext,
Tyler Denniston53281c72020-10-22 15:54:24 -0400362 *other.fPresentationContext,
363 other.fNode) {}
364
365SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, const SkSVGNode* node)
366 : SkSVGRenderContext(other.fCanvas,
367 other.fIDMapper,
368 *other.fLengthContext,
369 *other.fPresentationContext,
370 node) {}
Florin Malita1aa1bb62017-10-11 14:34:33 -0400371
fmalita397a5172016-08-08 11:38:55 -0700372SkSVGRenderContext::~SkSVGRenderContext() {
373 fCanvas->restoreToCount(fCanvasSaveCount);
374}
fmalita2d961e02016-08-11 09:16:29 -0700375
Florin Malita27aeb122020-04-08 15:02:02 -0400376SkSVGRenderContext::BorrowedNode SkSVGRenderContext::findNodeById(const SkString& id) const {
377 return BorrowedNode(fIDMapper.find(id));
fmalita28d5b722016-09-12 17:06:47 -0700378}
379
fmalitabef51c22016-09-20 15:45:57 -0700380void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs,
381 uint32_t flags) {
fmalita2d961e02016-08-11 09:16:29 -0700382
383#define ApplyLazyInheritedAttribute(ATTR) \
384 do { \
385 /* All attributes should be defined on the inherited context. */ \
386 SkASSERT(fPresentationContext->fInherited.f ## ATTR.isValid()); \
387 const auto* value = attrs.f ## ATTR.getMaybeNull(); \
388 if (value && *value != *fPresentationContext->fInherited.f ## ATTR.get()) { \
389 /* Update the local attribute value */ \
390 fPresentationContext.writable()->fInherited.f ## ATTR.set(*value); \
391 /* Update the cached paints */ \
fmalita28d5b722016-09-12 17:06:47 -0700392 commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *this, \
fmalita2d961e02016-08-11 09:16:29 -0700393 fPresentationContext.writable()); \
394 } \
395 } while (false)
396
397 ApplyLazyInheritedAttribute(Fill);
398 ApplyLazyInheritedAttribute(FillOpacity);
Florin Malitae932d4b2016-12-01 13:35:11 -0500399 ApplyLazyInheritedAttribute(FillRule);
Florin Malita39fe8c82020-10-20 10:43:03 -0400400 ApplyLazyInheritedAttribute(FontFamily);
401 ApplyLazyInheritedAttribute(FontSize);
402 ApplyLazyInheritedAttribute(FontStyle);
403 ApplyLazyInheritedAttribute(FontWeight);
Florin Malita57a0edf2017-10-10 11:22:08 -0400404 ApplyLazyInheritedAttribute(ClipRule);
fmalita2d961e02016-08-11 09:16:29 -0700405 ApplyLazyInheritedAttribute(Stroke);
Florin Malitae1dadd72017-10-13 18:18:32 -0400406 ApplyLazyInheritedAttribute(StrokeDashOffset);
Florin Malitaf543a602017-10-13 14:07:44 -0400407 ApplyLazyInheritedAttribute(StrokeDashArray);
fmalita2d961e02016-08-11 09:16:29 -0700408 ApplyLazyInheritedAttribute(StrokeLineCap);
409 ApplyLazyInheritedAttribute(StrokeLineJoin);
Florin Malita4de426b2017-10-09 12:57:41 -0400410 ApplyLazyInheritedAttribute(StrokeMiterLimit);
fmalita2d961e02016-08-11 09:16:29 -0700411 ApplyLazyInheritedAttribute(StrokeOpacity);
412 ApplyLazyInheritedAttribute(StrokeWidth);
Florin Malita056385b2020-10-27 22:57:56 -0400413 ApplyLazyInheritedAttribute(TextAnchor);
Florin Malitaffe6ae42017-10-12 11:33:28 -0400414 ApplyLazyInheritedAttribute(Visibility);
Tyler Denniston6c8314e2020-04-09 14:14:10 -0400415 ApplyLazyInheritedAttribute(Color);
fmalita2d961e02016-08-11 09:16:29 -0700416
Tyler Dennistondf1b0142020-04-20 10:52:21 -0400417 // Local 'color' attribute: update paints for attributes that are set to 'currentColor'.
418 if (attrs.fColor.isValid()) {
419 updatePaintsWithCurrentColor(attrs);
420 }
421
fmalita2d961e02016-08-11 09:16:29 -0700422#undef ApplyLazyInheritedAttribute
fmalita6fb06482016-08-15 12:45:11 -0700423
424 // Uninherited attributes. Only apply to the current context.
425
fmalitabef51c22016-09-20 15:45:57 -0700426 if (auto* opacity = attrs.fOpacity.getMaybeNull()) {
Florin Malitaa3626692020-04-09 14:36:45 -0400427 this->applyOpacity(*opacity, flags);
fmalitabef51c22016-09-20 15:45:57 -0700428 }
Florin Malitace8840e2016-12-08 09:26:47 -0500429
430 if (auto* clip = attrs.fClipPath.getMaybeNull()) {
431 this->applyClip(*clip);
432 }
fmalitabef51c22016-09-20 15:45:57 -0700433}
434
435void SkSVGRenderContext::applyOpacity(SkScalar opacity, uint32_t flags) {
436 if (opacity >= 1) {
437 return;
438 }
439
440 const bool hasFill = SkToBool(this->fillPaint());
441 const bool hasStroke = SkToBool(this->strokePaint());
442
443 // We can apply the opacity as paint alpha iif it only affects one atomic draw.
444 // For now, this means a) the target node doesn't have any descendants, and
445 // b) it only has a stroke or a fill (but not both). Going forward, we may need
446 // to refine this heuristic (e.g. to accommodate markers).
447 if ((flags & kLeaf) && (hasFill ^ hasStroke)) {
448 auto* pctx = fPresentationContext.writable();
449 if (hasFill) {
450 pctx->fFillPaint.setAlpha(
451 SkScalarRoundToInt(opacity * pctx->fFillPaint.getAlpha()));
452 } else {
453 pctx->fStrokePaint.setAlpha(
454 SkScalarRoundToInt(opacity * pctx->fStrokePaint.getAlpha()));
455 }
456 } else {
457 // Expensive, layer-based fall back.
fmalita6fb06482016-08-15 12:45:11 -0700458 SkPaint opacityPaint;
fmalitabef51c22016-09-20 15:45:57 -0700459 opacityPaint.setAlpha(opacity_to_alpha(opacity));
fmalita6fb06482016-08-15 12:45:11 -0700460 // Balanced in the destructor, via restoreToCount().
461 fCanvas->saveLayer(nullptr, &opacityPaint);
462 }
fmalita2d961e02016-08-11 09:16:29 -0700463}
464
Florin Malitab36be142017-10-11 14:11:16 -0400465void SkSVGRenderContext::saveOnce() {
466 // The canvas only needs to be saved once, per local SkSVGRenderContext.
467 if (fCanvas->getSaveCount() == fCanvasSaveCount) {
468 fCanvas->save();
469 }
470
471 SkASSERT(fCanvas->getSaveCount() > fCanvasSaveCount);
472}
473
Florin Malitace8840e2016-12-08 09:26:47 -0500474void SkSVGRenderContext::applyClip(const SkSVGClip& clip) {
475 if (clip.type() != SkSVGClip::Type::kIRI) {
476 return;
477 }
478
Florin Malita27aeb122020-04-08 15:02:02 -0400479 const auto clipNode = this->findNodeById(clip.iri());
Florin Malitace8840e2016-12-08 09:26:47 -0500480 if (!clipNode || clipNode->tag() != SkSVGTag::kClipPath) {
481 return;
482 }
483
484 const SkPath clipPath = clipNode->asPath(*this);
485
Florin Malita7d529882016-12-08 16:04:24 -0500486 // We use the computed clip path in two ways:
487 //
488 // - apply to the current canvas, for drawing
489 // - track in the presentation context, for asPath() composition
490 //
491 // TODO: the two uses are exclusive, avoid canvas churn when non needed.
492
Florin Malitab36be142017-10-11 14:11:16 -0400493 this->saveOnce();
Florin Malitace8840e2016-12-08 09:26:47 -0500494
495 fCanvas->clipPath(clipPath, true);
Florin Malita7d529882016-12-08 16:04:24 -0500496 fClipPath.set(clipPath);
Florin Malitace8840e2016-12-08 09:26:47 -0500497}
498
Tyler Dennistondf1b0142020-04-20 10:52:21 -0400499void SkSVGRenderContext::updatePaintsWithCurrentColor(const SkSVGPresentationAttributes& attrs) {
500 // Of the attributes that can use currentColor:
501 // https://www.w3.org/TR/SVG11/color.html#ColorProperty
502 // Only fill and stroke require paint updates. The others are resolved at render time.
503
504 if (fPresentationContext->fInherited.fFill->type() == SkSVGPaint::Type::kCurrentColor) {
505 applySvgPaint(*this, *fPresentationContext->fInherited.fFill,
506 &fPresentationContext.writable()->fFillPaint);
507 }
508
509 if (fPresentationContext->fInherited.fStroke->type() == SkSVGPaint::Type::kCurrentColor) {
510 applySvgPaint(*this, *fPresentationContext->fInherited.fStroke,
511 &fPresentationContext.writable()->fStrokePaint);
512 }
513}
514
fmalita2d961e02016-08-11 09:16:29 -0700515const SkPaint* SkSVGRenderContext::fillPaint() const {
John Stilesa008b0f2020-08-16 08:48:02 -0400516 const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fFill->type();
fmalita2d961e02016-08-11 09:16:29 -0700517 return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fFillPaint : nullptr;
518}
519
520const SkPaint* SkSVGRenderContext::strokePaint() const {
John Stilesa008b0f2020-08-16 08:48:02 -0400521 const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fStroke->type();
fmalita2d961e02016-08-11 09:16:29 -0700522 return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fStrokePaint : nullptr;
523}