2b301e95172373c5462ee0ed07e4900700e18afc
[WebKit.git] / Source / WebCore / css / WebKitCSSMatrix.cpp
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
40 namespace WebCore {
41
42 WTF_MAKE_ISO_ALLOCATED_IMPL(WebKitCSSMatrix);
43
44 inline WebKitCSSMatrix::WebKitCSSMatrix(const TransformationMatrix& matrix)
45     : m_matrix(matrix)
46 {
47 }
48
49 Ref<WebKitCSSMatrix> WebKitCSSMatrix::create(const TransformationMatrix& matrix)
50 {
51     return adoptRef(*new WebKitCSSMatrix(matrix));
52 }
53
54 ExceptionOr<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
63 WebKitCSSMatrix::~WebKitCSSMatrix() = default;
64
65 ExceptionOr<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)
96 RefPtr<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
106 ExceptionOr<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
114 Ref<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
128 Ref<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
142 Ref<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
163 Ref<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
181 Ref<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
191 Ref<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
201 ExceptionOr<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