blob: 484245de58c3e7f5dc227cefd2384c5cf3f6e86e [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"
Florin Malitab3418102020-10-15 18:10:29 -04009#include "modules/svg/include/SkSVGRenderContext.h"
10#include "modules/svg/include/SkSVGSVG.h"
11#include "modules/svg/include/SkSVGValue.h"
fmalita6ceef3d2016-07-26 18:46:34 -070012
Florin Malita385e7442020-10-21 16:55:46 -040013static SkMatrix ViewboxMatrix(const SkRect& view_box, const SkRect& view_port,
14 const SkSVGPreserveAspectRatio& par) {
15 SkASSERT(!view_box.isEmpty());
16 SkASSERT(!view_port.isEmpty());
17
18 auto compute_scale = [&]() -> SkV2 {
19 const auto sx = view_port.width() / view_box.width(),
20 sy = view_port.height() / view_box.height();
21
22 if (par.fAlign == SkSVGPreserveAspectRatio::kNone) {
23 // none -> anisotropic scaling, regardless of fScale
24 return {sx,sy};
25 }
26
27 // isotropic scaling
28 const auto s = par.fScale == SkSVGPreserveAspectRatio::kMeet
29 ? std::min(sx, sy)
30 : std::max(sx, sy);
31 return {s,s};
32 };
33
34 auto compute_trans = [&](const SkV2& scale) -> SkV2 {
35 static constexpr float gAlignCoeffs[] = {
36 0.0f, // Min
37 0.5f, // Mid
38 1.0f // Max
39 };
40
41 const size_t x_coeff = par.fAlign>>0 & 0x03,
42 y_coeff = par.fAlign>>2 & 0x03;
43
44 SkASSERT(x_coeff < SK_ARRAY_COUNT(gAlignCoeffs) &&
45 y_coeff < SK_ARRAY_COUNT(gAlignCoeffs));
46
47 const auto tx = -view_box.x() * scale.x,
48 ty = -view_box.y() * scale.y,
49 dx = view_port.width() - view_box.width() * scale.x,
50 dy = view_port.height() - view_box.height()* scale.y;
51
52 return {
53 tx + dx * gAlignCoeffs[x_coeff],
54 ty + dy * gAlignCoeffs[y_coeff]
55 };
56 };
57
58 const auto s = compute_scale(),
59 t = compute_trans(s);
60
61 return SkMatrix::Translate(t.x, t.y) *
62 SkMatrix::Scale(s.x, s.y);
63}
64
fmalita397a5172016-08-08 11:38:55 -070065bool SkSVGSVG::onPrepareToRender(SkSVGRenderContext* ctx) const {
Florin Malitacdeabca2021-01-20 13:21:20 -050066 // x/y are ignored for outermost svg elements
67 const auto x = fType == Type::kInner ? fX : SkSVGLength(0);
68 const auto y = fType == Type::kInner ? fY : SkSVGLength(0);
69
70 auto viewPortRect = ctx->lengthContext().resolveRect(x, y, fWidth, fHeight);
Mike Reed1f607332020-05-21 12:11:27 -040071 auto contentMatrix = SkMatrix::Translate(viewPortRect.x(), viewPortRect.y());
fmalita397a5172016-08-08 11:38:55 -070072 auto viewPort = SkSize::Make(viewPortRect.width(), viewPortRect.height());
73
74 if (fViewBox.isValid()) {
John Stilesa008b0f2020-08-16 08:48:02 -040075 const SkRect& viewBox = *fViewBox;
fmalita397a5172016-08-08 11:38:55 -070076
77 // An empty viewbox disables rendering.
78 if (viewBox.isEmpty()) {
79 return false;
80 }
81
82 // A viewBox overrides the intrinsic viewport.
83 viewPort = SkSize::Make(viewBox.width(), viewBox.height());
84
Florin Malita385e7442020-10-21 16:55:46 -040085 contentMatrix.preConcat(ViewboxMatrix(viewBox, viewPortRect, fPreserveAspectRatio));
fmalita397a5172016-08-08 11:38:55 -070086 }
87
88 if (!contentMatrix.isIdentity()) {
Florin Malitab36be142017-10-11 14:11:16 -040089 ctx->saveOnce();
fmalita397a5172016-08-08 11:38:55 -070090 ctx->canvas()->concat(contentMatrix);
91 }
92
93 if (viewPort != ctx->lengthContext().viewPort()) {
94 ctx->writableLengthContext()->setViewPort(viewPort);
95 }
96
97 return this->INHERITED::onPrepareToRender(ctx);
98}
99
Florin Malitaf4403e72020-04-10 14:14:04 +0000100void SkSVGSVG::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
fmalitabffc2562016-08-03 10:21:11 -0700101 switch (attr) {
102 case SkSVGAttribute::kX:
Florin Malitaf4403e72020-04-10 14:14:04 +0000103 if (const auto* x = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700104 this->setX(*x);
105 }
106 break;
107 case SkSVGAttribute::kY:
Florin Malitaf4403e72020-04-10 14:14:04 +0000108 if (const auto* y = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700109 this->setY(*y);
110 }
111 break;
112 case SkSVGAttribute::kWidth:
Florin Malitaf4403e72020-04-10 14:14:04 +0000113 if (const auto* w = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700114 this->setWidth(*w);
115 }
116 break;
117 case SkSVGAttribute::kHeight:
Florin Malitaf4403e72020-04-10 14:14:04 +0000118 if (const auto* h = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700119 this->setHeight(*h);
120 }
121 break;
fmalita397a5172016-08-08 11:38:55 -0700122 case SkSVGAttribute::kViewBox:
Florin Malitaf4403e72020-04-10 14:14:04 +0000123 if (const auto* vb = v.as<SkSVGViewBoxValue>()) {
fmalita397a5172016-08-08 11:38:55 -0700124 this->setViewBox(*vb);
125 }
126 break;
Florin Malita385e7442020-10-21 16:55:46 -0400127 case SkSVGAttribute::kPreserveAspectRatio:
128 if (const auto* par = v.as<SkSVGPreserveAspectRatioValue>()) {
129 this->setPreserveAspectRatio(*par);
130 }
131 break;
fmalitabffc2562016-08-03 10:21:11 -0700132 default:
133 this->INHERITED::onSetAttribute(attr, v);
134 }
135}
fmalitae1baa7c2016-09-14 12:04:30 -0700136
Florin Malitaf005c252020-04-08 10:10:53 -0400137// https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing
fmalitae1baa7c2016-09-14 12:04:30 -0700138SkSize SkSVGSVG::intrinsicSize(const SkSVGLengthContext& lctx) const {
139 // Percentage values do not provide an intrinsic size.
140 if (fWidth.unit() == SkSVGLength::Unit::kPercentage ||
141 fHeight.unit() == SkSVGLength::Unit::kPercentage) {
142 return SkSize::Make(0, 0);
143 }
144
145 return SkSize::Make(lctx.resolve(fWidth, SkSVGLengthContext::LengthType::kHorizontal),
146 lctx.resolve(fHeight, SkSVGLengthContext::LengthType::kVertical));
147}