1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebKitCSSMatrix.h"
28
29#include "CSSParser.h"
30#include "CSSPrimitiveValue.h"
31#include "CSSPropertyNames.h"
32#include "CSSToLengthConversionData.h"
33#include "CSSValueKeywords.h"
34#include "StyleProperties.h"
35#include "TransformFunctions.h"
36#include <wtf/IsoMallocInlines.h>
37#include <wtf/MathExtras.h>
38#include <wtf/text/StringBuilder.h>
39
40namespace WebCore {
41
42WTF_MAKE_ISO_ALLOCATED_IMPL(WebKitCSSMatrix);
43
44inline WebKitCSSMatrix::WebKitCSSMatrix(const TransformationMatrix& matrix)
45 : m_matrix(matrix)
46{
47}
48
49Ref<WebKitCSSMatrix> WebKitCSSMatrix::create(const TransformationMatrix& matrix)
50{
51 return adoptRef(*new WebKitCSSMatrix(matrix));
52}
53
54ExceptionOr<Ref<WebKitCSSMatrix>> WebKitCSSMatrix::create(const String& string)
55{
56 auto result = adoptRef(*new WebKitCSSMatrix);
57 auto setMatrixValueResult = result->setMatrixValue(string);
58 if (setMatrixValueResult.hasException())
59 return setMatrixValueResult.releaseException();
60 return result;
61}
62
63WebKitCSSMatrix::~WebKitCSSMatrix() = default;
64
65ExceptionOr<void> WebKitCSSMatrix::setMatrixValue(const String& string)
66{
67 if (string.isEmpty())
68 return { };
69
70 auto styleDeclaration = MutableStyleProperties::create();
71 if (CSSParser::parseValue(styleDeclaration, CSSPropertyTransform, string, true, HTMLStandardMode) == CSSParser::ParseResult::Error)
72 return Exception { SyntaxError };
73
74 // Convert to TransformOperations. This can fail if a property requires style (i.e., param uses 'ems' or 'exs')
75 auto value = styleDeclaration->getPropertyCSSValue(CSSPropertyTransform);
76
77 // Check for a "none" or empty transform. In these cases we can use the default identity matrix.
78 if (!value || (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).valueID() == CSSValueNone))
79 return { };
80
81 TransformOperations operations;
82 if (!transformsForValue(*value, CSSToLengthConversionData(), operations))
83 return Exception { SyntaxError };
84
85 // Convert transform operations to a TransformationMatrix. This can fail if a parameter has a percentage ('%').
86 TransformationMatrix matrix;
87 for (auto& operation : operations.operations()) {
88 if (operation->apply(matrix, IntSize(0, 0)))
89 return Exception { SyntaxError };
90 }
91 m_matrix = matrix;
92 return { };
93}
94
95// Perform a concatenation of the matrices (this * secondMatrix)
96RefPtr<WebKitCSSMatrix> WebKitCSSMatrix::multiply(WebKitCSSMatrix* secondMatrix) const
97{
98 if (!secondMatrix)
99 return nullptr;
100
101 auto matrix = create(m_matrix);
102 matrix->m_matrix.multiply(secondMatrix->m_matrix);
103 return matrix;
104}
105
106ExceptionOr<Ref<WebKitCSSMatrix>> WebKitCSSMatrix::inverse() const
107{
108 auto inverse = m_matrix.inverse();
109 if (!inverse)
110 return Exception { NotSupportedError };
111 return create(inverse.value());
112}
113
114Ref<WebKitCSSMatrix> WebKitCSSMatrix::translate(double x, double y, double z) const
115{
116 if (std::isnan(x))
117 x = 0;
118 if (std::isnan(y))
119 y = 0;
120 if (std::isnan(z))
121 z = 0;
122
123 auto matrix = create(m_matrix);
124 matrix->m_matrix.translate3d(x, y, z);
125 return matrix;
126}
127
128Ref<WebKitCSSMatrix> WebKitCSSMatrix::scale(double scaleX, double scaleY, double scaleZ) const
129{
130 if (std::isnan(scaleX))
131 scaleX = 1;
132 if (std::isnan(scaleY))
133 scaleY = scaleX;
134 if (std::isnan(scaleZ))
135 scaleZ = 1;
136
137 auto matrix = create(m_matrix);
138 matrix->m_matrix.scale3d(scaleX, scaleY, scaleZ);
139 return matrix;
140}
141
142Ref<WebKitCSSMatrix> WebKitCSSMatrix::rotate(double rotX, double rotY, double rotZ) const
143{
144 if (std::isnan(rotX))
145 rotX = 0;
146
147 if (std::isnan(rotY) && std::isnan(rotZ)) {
148 rotZ = rotX;
149 rotX = 0;
150 rotY = 0;
151 }
152
153 if (std::isnan(rotY))
154 rotY = 0;
155 if (std::isnan(rotZ))
156 rotZ = 0;
157
158 auto matrix = create(m_matrix);
159 matrix->m_matrix.rotate3d(rotX, rotY, rotZ);
160 return matrix;
161}
162
163Ref<WebKitCSSMatrix> WebKitCSSMatrix::rotateAxisAngle(double x, double y, double z, double angle) const
164{
165 if (std::isnan(x))
166 x = 0;
167 if (std::isnan(y))
168 y = 0;
169 if (std::isnan(z))
170 z = 0;
171 if (std::isnan(angle))
172 angle = 0;
173 if (x == 0 && y == 0 && z == 0)
174 z = 1;
175
176 auto matrix = create(m_matrix);
177 matrix->m_matrix.rotate3d(x, y, z, angle);
178 return matrix;
179}
180
181Ref<WebKitCSSMatrix> WebKitCSSMatrix::skewX(double angle) const
182{
183 if (std::isnan(angle))
184 angle = 0;
185
186 auto matrix = create(m_matrix);
187 matrix->m_matrix.skewX(angle);
188 return matrix;
189}
190
191Ref<WebKitCSSMatrix> WebKitCSSMatrix::skewY(double angle) const
192{
193 if (std::isnan(angle))
194 angle = 0;
195
196 auto matrix = create(m_matrix);
197 matrix->m_matrix.skewY(angle);
198 return matrix;
199}
200
201ExceptionOr<String> WebKitCSSMatrix::toString() const
202{
203 if (!m_matrix.containsOnlyFiniteValues())
204 return Exception { InvalidStateError, "Matrix contains non-finite values"_s };
205
206 StringBuilder builder;
207 if (m_matrix.isAffine()) {
208 builder.appendLiteral("matrix(");
209 builder.appendECMAScriptNumber(m_matrix.a());
210 builder.appendLiteral(", ");
211 builder.appendECMAScriptNumber(m_matrix.b());
212 builder.appendLiteral(", ");
213 builder.appendECMAScriptNumber(m_matrix.c());
214 builder.appendLiteral(", ");
215 builder.appendECMAScriptNumber(m_matrix.d());
216 builder.appendLiteral(", ");
217 builder.appendECMAScriptNumber(m_matrix.e());
218 builder.appendLiteral(", ");
219 builder.appendECMAScriptNumber(m_matrix.f());
220 } else {
221 builder.appendLiteral("matrix3d(");
222 builder.appendECMAScriptNumber(m_matrix.m11());
223 builder.appendLiteral(", ");
224 builder.appendECMAScriptNumber(m_matrix.m12());
225 builder.appendLiteral(", ");
226 builder.appendECMAScriptNumber(m_matrix.m13());
227 builder.appendLiteral(", ");
228 builder.appendECMAScriptNumber(m_matrix.m14());
229 builder.appendLiteral(", ");
230 builder.appendECMAScriptNumber(m_matrix.m21());
231 builder.appendLiteral(", ");
232 builder.appendECMAScriptNumber(m_matrix.m22());
233 builder.appendLiteral(", ");
234 builder.appendECMAScriptNumber(m_matrix.m23());
235 builder.appendLiteral(", ");
236 builder.appendECMAScriptNumber(m_matrix.m24());
237 builder.appendLiteral(", ");
238 builder.appendECMAScriptNumber(m_matrix.m31());
239 builder.appendLiteral(", ");
240 builder.appendECMAScriptNumber(m_matrix.m32());
241 builder.appendLiteral(", ");
242 builder.appendECMAScriptNumber(m_matrix.m33());
243 builder.appendLiteral(", ");
244 builder.appendECMAScriptNumber(m_matrix.m34());
245 builder.appendLiteral(", ");
246 builder.appendECMAScriptNumber(m_matrix.m41());
247 builder.appendLiteral(", ");
248 builder.appendECMAScriptNumber(m_matrix.m42());
249 builder.appendLiteral(", ");
250 builder.appendECMAScriptNumber(m_matrix.m43());
251 builder.appendLiteral(", ");
252 builder.appendECMAScriptNumber(m_matrix.m44());
253 }
254 builder.append(')');
255 return builder.toString();
256}
257
258} // namespace WebCore
259