1 | /* |
2 | * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org> |
3 | * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> |
4 | * Copyright (C) 2019 Apple Inc. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | */ |
21 | |
22 | #pragma once |
23 | |
24 | #include "FloatConversion.h" |
25 | #include "FloatPoint.h" |
26 | #include "FloatSize.h" |
27 | #include "SVGMatrix.h" |
28 | #include <wtf/HashMap.h> |
29 | #include <wtf/NeverDestroyed.h> |
30 | #include <wtf/text/StringBuilder.h> |
31 | |
32 | namespace WebCore { |
33 | |
34 | class FloatSize; |
35 | |
36 | class SVGTransformValue { |
37 | public: |
38 | enum SVGTransformType { |
39 | SVG_TRANSFORM_UNKNOWN = 0, |
40 | SVG_TRANSFORM_MATRIX = 1, |
41 | SVG_TRANSFORM_TRANSLATE = 2, |
42 | SVG_TRANSFORM_SCALE = 3, |
43 | SVG_TRANSFORM_ROTATE = 4, |
44 | SVG_TRANSFORM_SKEWX = 5, |
45 | SVG_TRANSFORM_SKEWY = 6 |
46 | }; |
47 | |
48 | enum ConstructionMode { |
49 | ConstructIdentityTransform, |
50 | ConstructZeroTransform |
51 | }; |
52 | |
53 | SVGTransformValue(SVGTransformType type = SVG_TRANSFORM_MATRIX, const AffineTransform& transform = { }) |
54 | : m_type(type) |
55 | , m_matrix(SVGMatrix::create(transform)) |
56 | { |
57 | } |
58 | |
59 | SVGTransformValue(const SVGTransformValue& other) |
60 | : m_type(other.m_type) |
61 | , m_matrix(SVGMatrix::create(other.matrix()->value())) |
62 | , m_angle(other.m_angle) |
63 | , m_rotationCenter(other.m_rotationCenter) |
64 | { |
65 | } |
66 | |
67 | SVGTransformValue(SVGTransformType type, Ref<SVGMatrix>&& matrix, float angle, const FloatPoint& rotationCenter) |
68 | : m_type(type) |
69 | , m_matrix(WTFMove(matrix)) |
70 | , m_angle(angle) |
71 | , m_rotationCenter(rotationCenter) |
72 | { |
73 | } |
74 | |
75 | SVGTransformValue(SVGTransformValue&& other) |
76 | : m_type(other.m_type) |
77 | , m_matrix(other.m_matrix.copyRef()) |
78 | , m_angle(other.m_angle) |
79 | , m_rotationCenter(other.m_rotationCenter) |
80 | { |
81 | } |
82 | |
83 | SVGTransformValue& operator=(const SVGTransformValue& other) |
84 | { |
85 | m_type = other.m_type; |
86 | m_matrix->setValue(other.m_matrix->value()); |
87 | m_angle = other.m_angle; |
88 | m_rotationCenter = other.m_rotationCenter; |
89 | return *this; |
90 | } |
91 | |
92 | SVGTransformType type() const { return m_type; } |
93 | const Ref<SVGMatrix>& matrix() const { return m_matrix; } |
94 | float angle() const { return m_angle; } |
95 | FloatPoint rotationCenter() const { return m_rotationCenter; } |
96 | |
97 | bool isValid() const { return m_type != SVG_TRANSFORM_UNKNOWN; } |
98 | |
99 | void setMatrix(const AffineTransform& matrix) |
100 | { |
101 | m_type = SVG_TRANSFORM_MATRIX; |
102 | m_angle = 0; |
103 | m_rotationCenter = FloatPoint(); |
104 | m_matrix->setValue(matrix); |
105 | } |
106 | |
107 | void matrixDidChange() |
108 | { |
109 | // The underlying matrix has been changed, alter the transformation type. |
110 | // Spec: In case the matrix object is changed directly (i.e., without using the methods on the SVGTransform interface itself) |
111 | // then the type of the SVGTransform changes to SVG_TRANSFORM_MATRIX. |
112 | m_type = SVG_TRANSFORM_MATRIX; |
113 | m_angle = 0; |
114 | m_rotationCenter = FloatPoint(); |
115 | } |
116 | |
117 | FloatPoint translate() const |
118 | { |
119 | return FloatPoint::narrowPrecision(m_matrix->e(), m_matrix->f()); |
120 | } |
121 | |
122 | void setTranslate(float tx, float ty) |
123 | { |
124 | m_type = SVG_TRANSFORM_TRANSLATE; |
125 | m_angle = 0; |
126 | m_rotationCenter = FloatPoint(); |
127 | |
128 | m_matrix->value().makeIdentity(); |
129 | m_matrix->value().translate(tx, ty); |
130 | } |
131 | |
132 | FloatSize scale() const |
133 | { |
134 | return FloatSize::narrowPrecision(m_matrix->a(), m_matrix->d()); |
135 | } |
136 | |
137 | void setScale(float sx, float sy) |
138 | { |
139 | m_type = SVG_TRANSFORM_SCALE; |
140 | m_angle = 0; |
141 | m_rotationCenter = FloatPoint(); |
142 | |
143 | m_matrix->value().makeIdentity(); |
144 | m_matrix->value().scaleNonUniform(sx, sy); |
145 | } |
146 | |
147 | void setRotate(float angle, float cx, float cy) |
148 | { |
149 | m_type = SVG_TRANSFORM_ROTATE; |
150 | m_angle = angle; |
151 | m_rotationCenter = FloatPoint(cx, cy); |
152 | |
153 | // TODO: toString() implementation, which can show cx, cy (need to be stored?) |
154 | m_matrix->value().makeIdentity(); |
155 | m_matrix->value().translate(cx, cy); |
156 | m_matrix->value().rotate(angle); |
157 | m_matrix->value().translate(-cx, -cy); |
158 | } |
159 | |
160 | void setSkewX(float angle) |
161 | { |
162 | m_type = SVG_TRANSFORM_SKEWX; |
163 | m_angle = angle; |
164 | m_rotationCenter = FloatPoint(); |
165 | |
166 | m_matrix->value().makeIdentity(); |
167 | m_matrix->value().skewX(angle); |
168 | } |
169 | |
170 | void setSkewY(float angle) |
171 | { |
172 | m_type = SVG_TRANSFORM_SKEWY; |
173 | m_angle = angle; |
174 | m_rotationCenter = FloatPoint(); |
175 | |
176 | m_matrix->value().makeIdentity(); |
177 | m_matrix->value().skewY(angle); |
178 | } |
179 | |
180 | String valueAsString() const |
181 | { |
182 | StringBuilder builder; |
183 | builder.append(prefixForTransfromType(m_type)); |
184 | switch (m_type) { |
185 | case SVG_TRANSFORM_UNKNOWN: |
186 | break; |
187 | case SVG_TRANSFORM_MATRIX: |
188 | appendMatrix(builder); |
189 | break; |
190 | case SVG_TRANSFORM_TRANSLATE: |
191 | appendTranslate(builder); |
192 | break; |
193 | case SVG_TRANSFORM_SCALE: |
194 | appendScale(builder); |
195 | break; |
196 | case SVG_TRANSFORM_ROTATE: |
197 | appendRotate(builder); |
198 | break; |
199 | case SVG_TRANSFORM_SKEWX: |
200 | appendSkewX(builder); |
201 | break; |
202 | case SVG_TRANSFORM_SKEWY: |
203 | appendSkewY(builder); |
204 | break; |
205 | } |
206 | return builder.toString(); |
207 | } |
208 | |
209 | static String prefixForTransfromType(SVGTransformType type) |
210 | { |
211 | switch (type) { |
212 | case SVG_TRANSFORM_UNKNOWN: |
213 | return emptyString(); |
214 | case SVG_TRANSFORM_MATRIX: |
215 | return "matrix("_s ; |
216 | case SVG_TRANSFORM_TRANSLATE: |
217 | return "translate("_s ; |
218 | case SVG_TRANSFORM_SCALE: |
219 | return "scale("_s ; |
220 | case SVG_TRANSFORM_ROTATE: |
221 | return "rotate("_s ; |
222 | case SVG_TRANSFORM_SKEWX: |
223 | return "skewX("_s ; |
224 | case SVG_TRANSFORM_SKEWY: |
225 | return "skewY("_s ; |
226 | } |
227 | ASSERT_NOT_REACHED(); |
228 | return emptyString(); |
229 | } |
230 | |
231 | private: |
232 | static void appendFixedPrecisionNumbers(StringBuilder& builder) |
233 | { |
234 | builder.append(')'); |
235 | } |
236 | |
237 | template<typename Number, typename... Numbers> |
238 | static void appendFixedPrecisionNumbers(StringBuilder& builder, Number number, Numbers... numbers) |
239 | { |
240 | if (builder.length() && builder[builder.length() - 1] != '(') |
241 | builder.append(' '); |
242 | builder.appendFixedPrecisionNumber(number); |
243 | appendFixedPrecisionNumbers(builder, numbers...); |
244 | } |
245 | |
246 | void appendMatrix(StringBuilder& builder) const |
247 | { |
248 | appendFixedPrecisionNumbers(builder, m_matrix->a(), m_matrix->b(), m_matrix->c(), m_matrix->d(), m_matrix->e(), m_matrix->f()); |
249 | } |
250 | |
251 | void appendTranslate(StringBuilder& builder) const |
252 | { |
253 | appendFixedPrecisionNumbers(builder, m_matrix->e(), m_matrix->f()); |
254 | } |
255 | |
256 | void appendScale(StringBuilder& builder) const |
257 | { |
258 | appendFixedPrecisionNumbers(builder, m_matrix->value().xScale(), m_matrix->value().yScale()); |
259 | } |
260 | |
261 | void appendRotate(StringBuilder& builder) const |
262 | { |
263 | double angleInRad = deg2rad(m_angle); |
264 | double cosAngle = std::cos(angleInRad); |
265 | double sinAngle = std::sin(angleInRad); |
266 | |
267 | float cx = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix->e() * (1 - cosAngle) - m_matrix->f() * sinAngle) / (1 - cosAngle) / 2 : 0); |
268 | float cy = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix->e() * sinAngle / (1 - cosAngle) + m_matrix->f()) / 2 : 0); |
269 | |
270 | if (cx || cy) |
271 | appendFixedPrecisionNumbers(builder, m_angle, cx, cy); |
272 | else |
273 | appendFixedPrecisionNumbers(builder, m_angle); |
274 | } |
275 | |
276 | void appendSkewX(StringBuilder& builder) const |
277 | { |
278 | appendFixedPrecisionNumbers(builder, m_angle); |
279 | } |
280 | |
281 | void appendSkewY(StringBuilder& builder) const |
282 | { |
283 | appendFixedPrecisionNumbers(builder, m_angle); |
284 | } |
285 | |
286 | SVGTransformType m_type { SVG_TRANSFORM_UNKNOWN }; |
287 | Ref<SVGMatrix> m_matrix; |
288 | float m_angle { 0 }; |
289 | FloatPoint m_rotationCenter; |
290 | }; |
291 | |
292 | } // namespace WebCore |
293 | |