blob: 9de605a034a5ddc40d95c0b5aecb84e1a9e1a235 [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
fmalita397a5172016-08-08 11:38:55 -07008#include "SkCanvas.h"
Florin Malitae932d4b2016-12-01 13:35:11 -05009#include "SkPath.h"
fmalita2d961e02016-08-11 09:16:29 -070010#include "SkSVGAttribute.h"
fmalita28d5b722016-09-12 17:06:47 -070011#include "SkSVGNode.h"
fmalita6ceef3d2016-07-26 18:46:34 -070012#include "SkSVGRenderContext.h"
fmalitabffc2562016-08-03 10:21:11 -070013#include "SkSVGTypes.h"
fmalita6ceef3d2016-07-26 18:46:34 -070014
fmalitabffc2562016-08-03 10:21:11 -070015namespace {
16
17SkScalar length_size_for_type(const SkSize& viewport, SkSVGLengthContext::LengthType t) {
18 switch (t) {
19 case SkSVGLengthContext::LengthType::kHorizontal:
20 return viewport.width();
21 case SkSVGLengthContext::LengthType::kVertical:
22 return viewport.height();
23 case SkSVGLengthContext::LengthType::kOther:
24 return SkScalarSqrt(viewport.width() * viewport.height());
25 }
26
27 SkASSERT(false); // Not reached.
28 return 0;
29}
30
fmalita280e2822016-08-17 14:51:03 -070031// Multipliers for DPI-relative units.
32constexpr SkScalar kINMultiplier = 1.00f;
33constexpr SkScalar kPTMultiplier = kINMultiplier / 72.272f;
34constexpr SkScalar kPCMultiplier = kPTMultiplier * 12;
35constexpr SkScalar kMMMultiplier = kINMultiplier / 25.4f;
36constexpr SkScalar kCMMultiplier = kMMMultiplier * 10;
37
fmalitabffc2562016-08-03 10:21:11 -070038} // anonymous ns
39
40SkScalar SkSVGLengthContext::resolve(const SkSVGLength& l, LengthType t) const {
41 switch (l.unit()) {
42 case SkSVGLength::Unit::kNumber:
fmalita2d961e02016-08-11 09:16:29 -070043 // Fall through.
44 case SkSVGLength::Unit::kPX:
fmalitabffc2562016-08-03 10:21:11 -070045 return l.value();
fmalitabffc2562016-08-03 10:21:11 -070046 case SkSVGLength::Unit::kPercentage:
47 return l.value() * length_size_for_type(fViewport, t) / 100;
fmalita280e2822016-08-17 14:51:03 -070048 case SkSVGLength::Unit::kCM:
49 return l.value() * fDPI * kCMMultiplier;
50 case SkSVGLength::Unit::kMM:
51 return l.value() * fDPI * kMMMultiplier;
52 case SkSVGLength::Unit::kIN:
53 return l.value() * fDPI * kINMultiplier;
54 case SkSVGLength::Unit::kPT:
55 return l.value() * fDPI * kPTMultiplier;
56 case SkSVGLength::Unit::kPC:
57 return l.value() * fDPI * kPCMultiplier;
fmalitabffc2562016-08-03 10:21:11 -070058 default:
59 SkDebugf("unsupported unit type: <%d>\n", l.unit());
fmalita2d961e02016-08-11 09:16:29 -070060 return 0;
fmalitabffc2562016-08-03 10:21:11 -070061 }
fmalitabffc2562016-08-03 10:21:11 -070062}
63
fmalita397a5172016-08-08 11:38:55 -070064SkRect SkSVGLengthContext::resolveRect(const SkSVGLength& x, const SkSVGLength& y,
65 const SkSVGLength& w, const SkSVGLength& h) const {
66 return SkRect::MakeXYWH(
67 this->resolve(x, SkSVGLengthContext::LengthType::kHorizontal),
68 this->resolve(y, SkSVGLengthContext::LengthType::kVertical),
69 this->resolve(w, SkSVGLengthContext::LengthType::kHorizontal),
70 this->resolve(h, SkSVGLengthContext::LengthType::kVertical));
71}
fmalita6ceef3d2016-07-26 18:46:34 -070072
fmalita2d961e02016-08-11 09:16:29 -070073namespace {
fmalita397a5172016-08-08 11:38:55 -070074
fmalita2d961e02016-08-11 09:16:29 -070075SkPaint::Cap toSkCap(const SkSVGLineCap& cap) {
76 switch (cap.type()) {
77 case SkSVGLineCap::Type::kButt:
78 return SkPaint::kButt_Cap;
79 case SkSVGLineCap::Type::kRound:
80 return SkPaint::kRound_Cap;
81 case SkSVGLineCap::Type::kSquare:
82 return SkPaint::kSquare_Cap;
83 default:
84 SkASSERT(false);
85 return SkPaint::kButt_Cap;
fmalita6ceef3d2016-07-26 18:46:34 -070086 }
fmalita6ceef3d2016-07-26 18:46:34 -070087}
88
fmalita2d961e02016-08-11 09:16:29 -070089SkPaint::Join toSkJoin(const SkSVGLineJoin& join) {
90 switch (join.type()) {
91 case SkSVGLineJoin::Type::kMiter:
92 return SkPaint::kMiter_Join;
93 case SkSVGLineJoin::Type::kRound:
94 return SkPaint::kRound_Join;
95 case SkSVGLineJoin::Type::kBevel:
96 return SkPaint::kBevel_Join;
97 default:
98 SkASSERT(false);
99 return SkPaint::kMiter_Join;
fmalita6ceef3d2016-07-26 18:46:34 -0700100 }
fmalita6ceef3d2016-07-26 18:46:34 -0700101}
102
fmalita28d5b722016-09-12 17:06:47 -0700103void applySvgPaint(const SkSVGRenderContext& ctx, const SkSVGPaint& svgPaint, SkPaint* p) {
fmalita2d961e02016-08-11 09:16:29 -0700104 switch (svgPaint.type()) {
105 case SkSVGPaint::Type::kColor:
106 p->setColor(SkColorSetA(svgPaint.color(), p->getAlpha()));
107 break;
fmalita28d5b722016-09-12 17:06:47 -0700108 case SkSVGPaint::Type::kIRI: {
109 const auto* node = ctx.findNodeById(svgPaint.iri());
110 if (!node || !node->asPaint(ctx, p)) {
111 p->setColor(SK_ColorTRANSPARENT);
112 }
113 break;
114 }
fmalita2d961e02016-08-11 09:16:29 -0700115 case SkSVGPaint::Type::kCurrentColor:
116 SkDebugf("unimplemented 'currentColor' paint type");
117 // Fall through.
118 case SkSVGPaint::Type::kNone:
119 // Fall through.
120 case SkSVGPaint::Type::kInherit:
121 break;
fmalita6ceef3d2016-07-26 18:46:34 -0700122 }
fmalita6ceef3d2016-07-26 18:46:34 -0700123}
124
fmalitaa26cab02016-08-29 05:54:42 -0700125inline uint8_t opacity_to_alpha(SkScalar o) {
126 return SkTo<uint8_t>(SkScalarRoundToInt(o * 255));
127}
128
fmalita2d961e02016-08-11 09:16:29 -0700129// Commit the selected attribute to the paint cache.
130template <SkSVGAttribute>
131void commitToPaint(const SkSVGPresentationAttributes&,
fmalita28d5b722016-09-12 17:06:47 -0700132 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700133 SkSVGPresentationContext*);
134
135template <>
136void commitToPaint<SkSVGAttribute::kFill>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700137 const SkSVGRenderContext& ctx,
fmalita2d961e02016-08-11 09:16:29 -0700138 SkSVGPresentationContext* pctx) {
fmalita28d5b722016-09-12 17:06:47 -0700139 applySvgPaint(ctx, *attrs.fFill.get(), &pctx->fFillPaint);
fmalita6ceef3d2016-07-26 18:46:34 -0700140}
141
fmalita2d961e02016-08-11 09:16:29 -0700142template <>
143void commitToPaint<SkSVGAttribute::kStroke>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700144 const SkSVGRenderContext& ctx,
fmalita2d961e02016-08-11 09:16:29 -0700145 SkSVGPresentationContext* pctx) {
fmalita28d5b722016-09-12 17:06:47 -0700146 applySvgPaint(ctx, *attrs.fStroke.get(), &pctx->fStrokePaint);
fmalita2d961e02016-08-11 09:16:29 -0700147}
148
149template <>
150void commitToPaint<SkSVGAttribute::kFillOpacity>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700151 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700152 SkSVGPresentationContext* pctx) {
fmalitaa26cab02016-08-29 05:54:42 -0700153 pctx->fFillPaint.setAlpha(opacity_to_alpha(*attrs.fFillOpacity.get()));
fmalita2d961e02016-08-11 09:16:29 -0700154}
155
156template <>
157void commitToPaint<SkSVGAttribute::kStrokeLineCap>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700158 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700159 SkSVGPresentationContext* pctx) {
160 const auto& cap = *attrs.fStrokeLineCap.get();
161 if (cap.type() != SkSVGLineCap::Type::kInherit) {
162 pctx->fStrokePaint.setStrokeCap(toSkCap(cap));
163 }
164}
165
166template <>
167void commitToPaint<SkSVGAttribute::kStrokeLineJoin>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700168 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700169 SkSVGPresentationContext* pctx) {
170 const auto& join = *attrs.fStrokeLineJoin.get();
171 if (join.type() != SkSVGLineJoin::Type::kInherit) {
172 pctx->fStrokePaint.setStrokeJoin(toSkJoin(join));
173 }
174}
175
176template <>
177void commitToPaint<SkSVGAttribute::kStrokeOpacity>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700178 const SkSVGRenderContext&,
fmalita2d961e02016-08-11 09:16:29 -0700179 SkSVGPresentationContext* pctx) {
fmalitaa26cab02016-08-29 05:54:42 -0700180 pctx->fStrokePaint.setAlpha(opacity_to_alpha(*attrs.fStrokeOpacity.get()));
fmalita2d961e02016-08-11 09:16:29 -0700181}
182
183template <>
184void commitToPaint<SkSVGAttribute::kStrokeWidth>(const SkSVGPresentationAttributes& attrs,
fmalita28d5b722016-09-12 17:06:47 -0700185 const SkSVGRenderContext& ctx,
fmalita2d961e02016-08-11 09:16:29 -0700186 SkSVGPresentationContext* pctx) {
fmalita28d5b722016-09-12 17:06:47 -0700187 auto strokeWidth = ctx.lengthContext().resolve(*attrs.fStrokeWidth.get(),
188 SkSVGLengthContext::LengthType::kOther);
fmalita2d961e02016-08-11 09:16:29 -0700189 pctx->fStrokePaint.setStrokeWidth(strokeWidth);
190}
191
Florin Malitae932d4b2016-12-01 13:35:11 -0500192template <>
193void commitToPaint<SkSVGAttribute::kFillRule>(const SkSVGPresentationAttributes&,
194 const SkSVGRenderContext&,
195 SkSVGPresentationContext*) {
196 // Not part of the SkPaint state; applied to the path at render time.
197}
198
fmalita2d961e02016-08-11 09:16:29 -0700199} // anonymous ns
200
201SkSVGPresentationContext::SkSVGPresentationContext()
202 : fInherited(SkSVGPresentationAttributes::MakeInitial()) {
203
204 fFillPaint.setStyle(SkPaint::kFill_Style);
205 fStrokePaint.setStyle(SkPaint::kStroke_Style);
206
207 // TODO: drive AA off presentation attrs also (shape-rendering?)
208 fFillPaint.setAntiAlias(true);
209 fStrokePaint.setAntiAlias(true);
210
211 // Commit initial values to the paint cache.
fmalita28d5b722016-09-12 17:06:47 -0700212 SkCanvas dummyCanvas(0, 0);
213 SkSVGRenderContext dummy(&dummyCanvas, SkSVGIDMapper(), SkSVGLengthContext(SkSize::Make(0, 0)),
214 *this);
215
fmalita2d961e02016-08-11 09:16:29 -0700216 commitToPaint<SkSVGAttribute::kFill>(fInherited, dummy, this);
217 commitToPaint<SkSVGAttribute::kFillOpacity>(fInherited, dummy, this);
218 commitToPaint<SkSVGAttribute::kStroke>(fInherited, dummy, this);
219 commitToPaint<SkSVGAttribute::kStrokeLineCap>(fInherited, dummy, this);
220 commitToPaint<SkSVGAttribute::kStrokeLineJoin>(fInherited, dummy, this);
221 commitToPaint<SkSVGAttribute::kStrokeOpacity>(fInherited, dummy, this);
222 commitToPaint<SkSVGAttribute::kStrokeWidth>(fInherited, dummy, this);
fmalita6ceef3d2016-07-26 18:46:34 -0700223}
fmalita397a5172016-08-08 11:38:55 -0700224
225SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
fmalita28d5b722016-09-12 17:06:47 -0700226 const SkSVGIDMapper& mapper,
fmalita397a5172016-08-08 11:38:55 -0700227 const SkSVGLengthContext& lctx,
228 const SkSVGPresentationContext& pctx)
fmalita28d5b722016-09-12 17:06:47 -0700229 : fIDMapper(mapper)
230 , fLengthContext(lctx)
fmalita397a5172016-08-08 11:38:55 -0700231 , fPresentationContext(pctx)
232 , fCanvas(canvas)
233 , fCanvasSaveCount(canvas->getSaveCount()) {}
234
235SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
fmalita2d961e02016-08-11 09:16:29 -0700236 : SkSVGRenderContext(other.fCanvas,
fmalita28d5b722016-09-12 17:06:47 -0700237 other.fIDMapper,
fmalita2d961e02016-08-11 09:16:29 -0700238 *other.fLengthContext,
239 *other.fPresentationContext) {}
fmalita397a5172016-08-08 11:38:55 -0700240
241SkSVGRenderContext::~SkSVGRenderContext() {
242 fCanvas->restoreToCount(fCanvasSaveCount);
243}
fmalita2d961e02016-08-11 09:16:29 -0700244
fmalita28d5b722016-09-12 17:06:47 -0700245const SkSVGNode* SkSVGRenderContext::findNodeById(const SkString& id) const {
246 const auto* v = fIDMapper.find(id);
247 return v ? v->get() : nullptr;
248}
249
fmalitabef51c22016-09-20 15:45:57 -0700250void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs,
251 uint32_t flags) {
fmalita2d961e02016-08-11 09:16:29 -0700252
253#define ApplyLazyInheritedAttribute(ATTR) \
254 do { \
255 /* All attributes should be defined on the inherited context. */ \
256 SkASSERT(fPresentationContext->fInherited.f ## ATTR.isValid()); \
257 const auto* value = attrs.f ## ATTR.getMaybeNull(); \
258 if (value && *value != *fPresentationContext->fInherited.f ## ATTR.get()) { \
259 /* Update the local attribute value */ \
260 fPresentationContext.writable()->fInherited.f ## ATTR.set(*value); \
261 /* Update the cached paints */ \
fmalita28d5b722016-09-12 17:06:47 -0700262 commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *this, \
fmalita2d961e02016-08-11 09:16:29 -0700263 fPresentationContext.writable()); \
264 } \
265 } while (false)
266
267 ApplyLazyInheritedAttribute(Fill);
268 ApplyLazyInheritedAttribute(FillOpacity);
Florin Malitae932d4b2016-12-01 13:35:11 -0500269 ApplyLazyInheritedAttribute(FillRule);
fmalita2d961e02016-08-11 09:16:29 -0700270 ApplyLazyInheritedAttribute(Stroke);
271 ApplyLazyInheritedAttribute(StrokeLineCap);
272 ApplyLazyInheritedAttribute(StrokeLineJoin);
273 ApplyLazyInheritedAttribute(StrokeOpacity);
274 ApplyLazyInheritedAttribute(StrokeWidth);
275
276#undef ApplyLazyInheritedAttribute
fmalita6fb06482016-08-15 12:45:11 -0700277
278 // Uninherited attributes. Only apply to the current context.
279
fmalitabef51c22016-09-20 15:45:57 -0700280 if (auto* opacity = attrs.fOpacity.getMaybeNull()) {
281 this->applyOpacity(opacity->value(), flags);
282 }
Florin Malitace8840e2016-12-08 09:26:47 -0500283
284 if (auto* clip = attrs.fClipPath.getMaybeNull()) {
285 this->applyClip(*clip);
286 }
fmalitabef51c22016-09-20 15:45:57 -0700287}
288
289void SkSVGRenderContext::applyOpacity(SkScalar opacity, uint32_t flags) {
290 if (opacity >= 1) {
291 return;
292 }
293
294 const bool hasFill = SkToBool(this->fillPaint());
295 const bool hasStroke = SkToBool(this->strokePaint());
296
297 // We can apply the opacity as paint alpha iif it only affects one atomic draw.
298 // For now, this means a) the target node doesn't have any descendants, and
299 // b) it only has a stroke or a fill (but not both). Going forward, we may need
300 // to refine this heuristic (e.g. to accommodate markers).
301 if ((flags & kLeaf) && (hasFill ^ hasStroke)) {
302 auto* pctx = fPresentationContext.writable();
303 if (hasFill) {
304 pctx->fFillPaint.setAlpha(
305 SkScalarRoundToInt(opacity * pctx->fFillPaint.getAlpha()));
306 } else {
307 pctx->fStrokePaint.setAlpha(
308 SkScalarRoundToInt(opacity * pctx->fStrokePaint.getAlpha()));
309 }
310 } else {
311 // Expensive, layer-based fall back.
fmalita6fb06482016-08-15 12:45:11 -0700312 SkPaint opacityPaint;
fmalitabef51c22016-09-20 15:45:57 -0700313 opacityPaint.setAlpha(opacity_to_alpha(opacity));
fmalita6fb06482016-08-15 12:45:11 -0700314 // Balanced in the destructor, via restoreToCount().
315 fCanvas->saveLayer(nullptr, &opacityPaint);
316 }
fmalita2d961e02016-08-11 09:16:29 -0700317}
318
Florin Malitace8840e2016-12-08 09:26:47 -0500319void SkSVGRenderContext::applyClip(const SkSVGClip& clip) {
320 if (clip.type() != SkSVGClip::Type::kIRI) {
321 return;
322 }
323
324 const SkSVGNode* clipNode = this->findNodeById(clip.iri());
325 if (!clipNode || clipNode->tag() != SkSVGTag::kClipPath) {
326 return;
327 }
328
329 const SkPath clipPath = clipNode->asPath(*this);
330
Florin Malita7d529882016-12-08 16:04:24 -0500331 // We use the computed clip path in two ways:
332 //
333 // - apply to the current canvas, for drawing
334 // - track in the presentation context, for asPath() composition
335 //
336 // TODO: the two uses are exclusive, avoid canvas churn when non needed.
337
Florin Malitace8840e2016-12-08 09:26:47 -0500338 // Only save if needed
339 if (fCanvas->getSaveCount() == fCanvasSaveCount) {
340 fCanvas->save();
341 }
342
343 fCanvas->clipPath(clipPath, true);
Florin Malita7d529882016-12-08 16:04:24 -0500344 fClipPath.set(clipPath);
Florin Malitace8840e2016-12-08 09:26:47 -0500345}
346
fmalita2d961e02016-08-11 09:16:29 -0700347const SkPaint* SkSVGRenderContext::fillPaint() const {
348 const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fFill.get()->type();
349 return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fFillPaint : nullptr;
350}
351
352const SkPaint* SkSVGRenderContext::strokePaint() const {
353 const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fStroke.get()->type();
354 return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fStrokePaint : nullptr;
355}