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 "CSSPropertyNames.h"
31#include "CSSValueKeywords.h"
32#include "ExceptionCode.h"
33#include "StylePropertySet.h"
34#include "TransformFunctions.h"
35#include <wtf/MathExtras.h>
36
37namespace WebCore {
38
39WebKitCSSMatrix::WebKitCSSMatrix(const TransformationMatrix& m)
40    : m_matrix(m)
41{
42}
43
44WebKitCSSMatrix::WebKitCSSMatrix(const String& s, ExceptionCode& ec)
45{
46    setMatrixValue(s, ec);
47}
48
49WebKitCSSMatrix::~WebKitCSSMatrix()
50{
51}
52
53void WebKitCSSMatrix::setMatrixValue(const String& string, ExceptionCode& ec)
54{
55    if (string.isEmpty())
56        return;
57
58    RefPtr<MutableStylePropertySet> styleDeclaration = MutableStylePropertySet::create();
59    if (CSSParser::parseValue(styleDeclaration.get(), CSSPropertyWebkitTransform, string, true, CSSStrictMode, 0)) {
60        // Convert to TransformOperations. This can fail if a property
61        // requires style (i.e., param uses 'ems' or 'exs')
62        RefPtr<CSSValue> value = styleDeclaration->getPropertyCSSValue(CSSPropertyWebkitTransform);
63
64        // Check for a "none" or empty transform. In these cases we can use the default identity matrix.
65        if (!value || (value->isPrimitiveValue() && (static_cast<CSSPrimitiveValue*>(value.get()))->getIdent() == CSSValueNone))
66            return;
67
68        TransformOperations operations;
69        if (!transformsForValue(0, 0, value.get(), operations)) {
70            ec = SYNTAX_ERR;
71            return;
72        }
73
74        // Convert transform operations to a TransformationMatrix. This can fail
75        // if a param has a percentage ('%')
76        TransformationMatrix t;
77        for (unsigned i = 0; i < operations.operations().size(); ++i) {
78            if (operations.operations()[i].get()->apply(t, IntSize(0, 0))) {
79                ec = SYNTAX_ERR;
80                return;
81            }
82        }
83
84        // set the matrix
85        m_matrix = t;
86    } else // There is something there but parsing failed.
87        ec = SYNTAX_ERR;
88}
89
90// Perform a concatenation of the matrices (this * secondMatrix)
91PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::multiply(WebKitCSSMatrix* secondMatrix) const
92{
93    if (!secondMatrix)
94        return 0;
95
96    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).multiply(secondMatrix->m_matrix));
97}
98
99PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::inverse(ExceptionCode& ec) const
100{
101    if (!m_matrix.isInvertible()) {
102        ec = NOT_SUPPORTED_ERR;
103        return 0;
104    }
105
106    return WebKitCSSMatrix::create(m_matrix.inverse());
107}
108
109PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::translate(double x, double y, double z) const
110{
111    if (std::isnan(x))
112        x = 0;
113    if (std::isnan(y))
114        y = 0;
115    if (std::isnan(z))
116        z = 0;
117    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).translate3d(x, y, z));
118}
119
120PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::scale(double scaleX, double scaleY, double scaleZ) const
121{
122    if (std::isnan(scaleX))
123        scaleX = 1;
124    if (std::isnan(scaleY))
125        scaleY = scaleX;
126    if (std::isnan(scaleZ))
127        scaleZ = 1;
128    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).scale3d(scaleX, scaleY, scaleZ));
129}
130
131PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::rotate(double rotX, double rotY, double rotZ) const
132{
133    if (std::isnan(rotX))
134        rotX = 0;
135
136    if (std::isnan(rotY) && std::isnan(rotZ)) {
137        rotZ = rotX;
138        rotX = 0;
139        rotY = 0;
140    }
141
142    if (std::isnan(rotY))
143        rotY = 0;
144    if (std::isnan(rotZ))
145        rotZ = 0;
146    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).rotate3d(rotX, rotY, rotZ));
147}
148
149PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::rotateAxisAngle(double x, double y, double z, double angle) const
150{
151    if (std::isnan(x))
152        x = 0;
153    if (std::isnan(y))
154        y = 0;
155    if (std::isnan(z))
156        z = 0;
157    if (std::isnan(angle))
158        angle = 0;
159    if (x == 0 && y == 0 && z == 0)
160        z = 1;
161    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).rotate3d(x, y, z, angle));
162}
163
164PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::skewX(double angle) const
165{
166    if (std::isnan(angle))
167        angle = 0;
168    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).skewX(angle));
169}
170
171PassRefPtr<WebKitCSSMatrix> WebKitCSSMatrix::skewY(double angle) const
172{
173    if (std::isnan(angle))
174        angle = 0;
175    return WebKitCSSMatrix::create(TransformationMatrix(m_matrix).skewY(angle));
176}
177
178String WebKitCSSMatrix::toString() const
179{
180    // FIXME - Need to ensure valid CSS floating point values (https://bugs.webkit.org/show_bug.cgi?id=20674)
181    if (m_matrix.isAffine())
182        return String::format("matrix(%f, %f, %f, %f, %f, %f)",
183                                m_matrix.a(), m_matrix.b(), m_matrix.c(), m_matrix.d(), m_matrix.e(), m_matrix.f());
184    return String::format("matrix3d(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)",
185                            m_matrix.m11(), m_matrix.m12(), m_matrix.m13(), m_matrix.m14(),
186                            m_matrix.m21(), m_matrix.m22(), m_matrix.m23(), m_matrix.m24(),
187                            m_matrix.m31(), m_matrix.m32(), m_matrix.m33(), m_matrix.m34(),
188                            m_matrix.m41(), m_matrix.m42(), m_matrix.m43(), m_matrix.m44());
189}
190
191} // namespace WebCore
192