blob: 4f2d5734b187ff337a96aa7a7cfac35fc4eca5f2 [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
fmalita58649cc2016-07-29 08:52:03 -070013SkSVGSVG::SkSVGSVG() : INHERITED(SkSVGTag::kSvg) { }
fmalitabffc2562016-08-03 10:21:11 -070014
Florin Malita385e7442020-10-21 16:55:46 -040015static SkMatrix ViewboxMatrix(const SkRect& view_box, const SkRect& view_port,
16 const SkSVGPreserveAspectRatio& par) {
17 SkASSERT(!view_box.isEmpty());
18 SkASSERT(!view_port.isEmpty());
19
20 auto compute_scale = [&]() -> SkV2 {
21 const auto sx = view_port.width() / view_box.width(),
22 sy = view_port.height() / view_box.height();
23
24 if (par.fAlign == SkSVGPreserveAspectRatio::kNone) {
25 // none -> anisotropic scaling, regardless of fScale
26 return {sx,sy};
27 }
28
29 // isotropic scaling
30 const auto s = par.fScale == SkSVGPreserveAspectRatio::kMeet
31 ? std::min(sx, sy)
32 : std::max(sx, sy);
33 return {s,s};
34 };
35
36 auto compute_trans = [&](const SkV2& scale) -> SkV2 {
37 static constexpr float gAlignCoeffs[] = {
38 0.0f, // Min
39 0.5f, // Mid
40 1.0f // Max
41 };
42
43 const size_t x_coeff = par.fAlign>>0 & 0x03,
44 y_coeff = par.fAlign>>2 & 0x03;
45
46 SkASSERT(x_coeff < SK_ARRAY_COUNT(gAlignCoeffs) &&
47 y_coeff < SK_ARRAY_COUNT(gAlignCoeffs));
48
49 const auto tx = -view_box.x() * scale.x,
50 ty = -view_box.y() * scale.y,
51 dx = view_port.width() - view_box.width() * scale.x,
52 dy = view_port.height() - view_box.height()* scale.y;
53
54 return {
55 tx + dx * gAlignCoeffs[x_coeff],
56 ty + dy * gAlignCoeffs[y_coeff]
57 };
58 };
59
60 const auto s = compute_scale(),
61 t = compute_trans(s);
62
63 return SkMatrix::Translate(t.x, t.y) *
64 SkMatrix::Scale(s.x, s.y);
65}
66
fmalita397a5172016-08-08 11:38:55 -070067bool SkSVGSVG::onPrepareToRender(SkSVGRenderContext* ctx) const {
68 auto viewPortRect = ctx->lengthContext().resolveRect(fX, fY, fWidth, fHeight);
Mike Reed1f607332020-05-21 12:11:27 -040069 auto contentMatrix = SkMatrix::Translate(viewPortRect.x(), viewPortRect.y());
fmalita397a5172016-08-08 11:38:55 -070070 auto viewPort = SkSize::Make(viewPortRect.width(), viewPortRect.height());
71
72 if (fViewBox.isValid()) {
John Stilesa008b0f2020-08-16 08:48:02 -040073 const SkRect& viewBox = *fViewBox;
fmalita397a5172016-08-08 11:38:55 -070074
75 // An empty viewbox disables rendering.
76 if (viewBox.isEmpty()) {
77 return false;
78 }
79
80 // A viewBox overrides the intrinsic viewport.
81 viewPort = SkSize::Make(viewBox.width(), viewBox.height());
82
Florin Malita385e7442020-10-21 16:55:46 -040083 contentMatrix.preConcat(ViewboxMatrix(viewBox, viewPortRect, fPreserveAspectRatio));
fmalita397a5172016-08-08 11:38:55 -070084 }
85
86 if (!contentMatrix.isIdentity()) {
Florin Malitab36be142017-10-11 14:11:16 -040087 ctx->saveOnce();
fmalita397a5172016-08-08 11:38:55 -070088 ctx->canvas()->concat(contentMatrix);
89 }
90
91 if (viewPort != ctx->lengthContext().viewPort()) {
92 ctx->writableLengthContext()->setViewPort(viewPort);
93 }
94
95 return this->INHERITED::onPrepareToRender(ctx);
96}
97
fmalita397a5172016-08-08 11:38:55 -070098void SkSVGSVG::setViewBox(const SkSVGViewBoxType& vb) {
99 fViewBox.set(vb);
100}
101
Florin Malitaf4403e72020-04-10 14:14:04 +0000102void SkSVGSVG::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
fmalitabffc2562016-08-03 10:21:11 -0700103 switch (attr) {
104 case SkSVGAttribute::kX:
Florin Malitaf4403e72020-04-10 14:14:04 +0000105 if (const auto* x = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700106 this->setX(*x);
107 }
108 break;
109 case SkSVGAttribute::kY:
Florin Malitaf4403e72020-04-10 14:14:04 +0000110 if (const auto* y = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700111 this->setY(*y);
112 }
113 break;
114 case SkSVGAttribute::kWidth:
Florin Malitaf4403e72020-04-10 14:14:04 +0000115 if (const auto* w = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700116 this->setWidth(*w);
117 }
118 break;
119 case SkSVGAttribute::kHeight:
Florin Malitaf4403e72020-04-10 14:14:04 +0000120 if (const auto* h = v.as<SkSVGLengthValue>()) {
fmalitabffc2562016-08-03 10:21:11 -0700121 this->setHeight(*h);
122 }
123 break;
fmalita397a5172016-08-08 11:38:55 -0700124 case SkSVGAttribute::kViewBox:
Florin Malitaf4403e72020-04-10 14:14:04 +0000125 if (const auto* vb = v.as<SkSVGViewBoxValue>()) {
fmalita397a5172016-08-08 11:38:55 -0700126 this->setViewBox(*vb);
127 }
128 break;
Florin Malita385e7442020-10-21 16:55:46 -0400129 case SkSVGAttribute::kPreserveAspectRatio:
130 if (const auto* par = v.as<SkSVGPreserveAspectRatioValue>()) {
131 this->setPreserveAspectRatio(*par);
132 }
133 break;
fmalitabffc2562016-08-03 10:21:11 -0700134 default:
135 this->INHERITED::onSetAttribute(attr, v);
136 }
137}
fmalitae1baa7c2016-09-14 12:04:30 -0700138
Florin Malitaf005c252020-04-08 10:10:53 -0400139// https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing
fmalitae1baa7c2016-09-14 12:04:30 -0700140SkSize SkSVGSVG::intrinsicSize(const SkSVGLengthContext& lctx) const {
141 // Percentage values do not provide an intrinsic size.
142 if (fWidth.unit() == SkSVGLength::Unit::kPercentage ||
143 fHeight.unit() == SkSVGLength::Unit::kPercentage) {
144 return SkSize::Make(0, 0);
145 }
146
147 return SkSize::Make(lctx.resolve(fWidth, SkSVGLengthContext::LengthType::kHorizontal),
148 lctx.resolve(fHeight, SkSVGLengthContext::LengthType::kVertical));
149}