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
32namespace WebCore {
33
34class FloatSize;
35
36class SVGTransformValue {
37public:
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
231private:
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