1/*
2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "SVGTransform.h"
23
24#include "FloatConversion.h"
25#include "FloatPoint.h"
26#include "FloatSize.h"
27#include "SVGAngle.h"
28#include "SVGSVGElement.h"
29#include <wtf/MathExtras.h>
30#include <wtf/text/StringBuilder.h>
31#include <wtf/text/WTFString.h>
32
33namespace WebCore {
34
35SVGTransform::SVGTransform()
36    : m_type(SVG_TRANSFORM_UNKNOWN)
37    , m_angle(0)
38{
39}
40
41SVGTransform::SVGTransform(SVGTransformType type, ConstructionMode mode)
42    : m_type(type)
43    , m_angle(0)
44{
45    if (mode == ConstructZeroTransform)
46        m_matrix = AffineTransform(0, 0, 0, 0, 0, 0);
47}
48
49SVGTransform::SVGTransform(const AffineTransform& matrix)
50    : m_type(SVG_TRANSFORM_MATRIX)
51    , m_angle(0)
52    , m_matrix(matrix)
53{
54}
55
56void SVGTransform::setMatrix(const AffineTransform& matrix)
57{
58    m_type = SVG_TRANSFORM_MATRIX;
59    m_angle = 0;
60    m_matrix = matrix;
61}
62
63void SVGTransform::updateSVGMatrix()
64{
65    // The underlying matrix has been changed, alter the transformation type.
66    // Spec: In case the matrix object is changed directly (i.e., without using the methods on the SVGTransform interface itself)
67    // then the type of the SVGTransform changes to SVG_TRANSFORM_MATRIX.
68    m_type = SVG_TRANSFORM_MATRIX;
69    m_angle = 0;
70}
71
72void SVGTransform::setTranslate(float tx, float ty)
73{
74    m_type = SVG_TRANSFORM_TRANSLATE;
75    m_angle = 0;
76
77    m_matrix.makeIdentity();
78    m_matrix.translate(tx, ty);
79}
80
81FloatPoint SVGTransform::translate() const
82{
83    return FloatPoint::narrowPrecision(m_matrix.e(), m_matrix.f());
84}
85
86void SVGTransform::setScale(float sx, float sy)
87{
88    m_type = SVG_TRANSFORM_SCALE;
89    m_angle = 0;
90    m_center = FloatPoint();
91
92    m_matrix.makeIdentity();
93    m_matrix.scaleNonUniform(sx, sy);
94}
95
96FloatSize SVGTransform::scale() const
97{
98    return FloatSize::narrowPrecision(m_matrix.a(), m_matrix.d());
99}
100
101void SVGTransform::setRotate(float angle, float cx, float cy)
102{
103    m_type = SVG_TRANSFORM_ROTATE;
104    m_angle = angle;
105    m_center = FloatPoint(cx, cy);
106
107    // TODO: toString() implementation, which can show cx, cy (need to be stored?)
108    m_matrix.makeIdentity();
109    m_matrix.translate(cx, cy);
110    m_matrix.rotate(angle);
111    m_matrix.translate(-cx, -cy);
112}
113
114void SVGTransform::setSkewX(float angle)
115{
116    m_type = SVG_TRANSFORM_SKEWX;
117    m_angle = angle;
118
119    m_matrix.makeIdentity();
120    m_matrix.skewX(angle);
121}
122
123void SVGTransform::setSkewY(float angle)
124{
125    m_type = SVG_TRANSFORM_SKEWY;
126    m_angle = angle;
127
128    m_matrix.makeIdentity();
129    m_matrix.skewY(angle);
130}
131
132const String& SVGTransform::transformTypePrefixForParsing(SVGTransformType type)
133{
134    switch (type) {
135    case SVG_TRANSFORM_UNKNOWN:
136        return emptyString();
137    case SVG_TRANSFORM_MATRIX: {
138        DEPRECATED_DEFINE_STATIC_LOCAL(String, matrixString, (ASCIILiteral("matrix(")));
139        return matrixString;
140    }
141    case SVG_TRANSFORM_TRANSLATE: {
142        DEPRECATED_DEFINE_STATIC_LOCAL(String, translateString, (ASCIILiteral("translate(")));
143        return translateString;
144    }
145    case SVG_TRANSFORM_SCALE: {
146        DEPRECATED_DEFINE_STATIC_LOCAL(String, scaleString, (ASCIILiteral("scale(")));
147        return scaleString;
148    }
149    case SVG_TRANSFORM_ROTATE: {
150        DEPRECATED_DEFINE_STATIC_LOCAL(String, rotateString, (ASCIILiteral("rotate(")));
151        return rotateString;
152    }
153    case SVG_TRANSFORM_SKEWX: {
154        DEPRECATED_DEFINE_STATIC_LOCAL(String, skewXString, (ASCIILiteral("skewX(")));
155        return skewXString;
156    }
157    case SVG_TRANSFORM_SKEWY: {
158        DEPRECATED_DEFINE_STATIC_LOCAL(String, skewYString, (ASCIILiteral("skewY(")));
159        return skewYString;
160    }
161    }
162
163    ASSERT_NOT_REACHED();
164    return emptyString();
165}
166
167String SVGTransform::valueAsString() const
168{
169    const String& prefix = transformTypePrefixForParsing(m_type);
170    switch (m_type) {
171    case SVG_TRANSFORM_UNKNOWN:
172        return prefix;
173    case SVG_TRANSFORM_MATRIX: {
174        StringBuilder builder;
175        builder.append(prefix);
176        builder.appendNumber(m_matrix.a());
177        builder.append(' ');
178        builder.appendNumber(m_matrix.b());
179        builder.append(' ');
180        builder.appendNumber(m_matrix.c());
181        builder.append(' ');
182        builder.appendNumber(m_matrix.d());
183        builder.append(' ');
184        builder.appendNumber(m_matrix.e());
185        builder.append(' ');
186        builder.appendNumber(m_matrix.f());
187        builder.append(')');
188        return builder.toString();
189    }
190    case SVG_TRANSFORM_TRANSLATE: {
191        StringBuilder builder;
192        builder.append(prefix);
193        builder.appendNumber(m_matrix.e());
194        builder.append(' ');
195        builder.appendNumber(m_matrix.f());
196        builder.append(')');
197        return builder.toString();
198    }
199    case SVG_TRANSFORM_SCALE: {
200        StringBuilder builder;
201        builder.append(prefix);
202        builder.appendNumber(m_matrix.xScale());
203        builder.append(' ');
204        builder.appendNumber(m_matrix.yScale());
205        builder.append(')');
206        return builder.toString();
207    }
208    case SVG_TRANSFORM_ROTATE: {
209        double angleInRad = deg2rad(m_angle);
210        double cosAngle = cos(angleInRad);
211        double sinAngle = sin(angleInRad);
212        float cx = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix.e() * (1 - cosAngle) - m_matrix.f() * sinAngle) / (1 - cosAngle) / 2 : 0);
213        float cy = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix.e() * sinAngle / (1 - cosAngle) + m_matrix.f()) / 2 : 0);
214        StringBuilder builder;
215        builder.append(prefix);
216        builder.appendNumber(m_angle);
217        if (cx || cy) {
218            builder.append(' ');
219            builder.appendNumber(cx);
220            builder.append(' ');
221            builder.appendNumber(cy);
222        }
223        builder.append(')');
224        return builder.toString();
225    }
226    case SVG_TRANSFORM_SKEWX:
227    case SVG_TRANSFORM_SKEWY: {
228        StringBuilder builder;
229        builder.append(prefix);
230        builder.appendNumber(m_angle);
231        builder.append(')');
232        return builder.toString();
233    }
234    }
235
236    ASSERT_NOT_REACHED();
237    return emptyString();
238}
239
240} // namespace WebCore
241