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 "core/svg/SVGTransform.h"
23
24#include "platform/FloatConversion.h"
25#include "platform/geometry/FloatSize.h"
26#include "wtf/MathExtras.h"
27#include "wtf/text/StringBuilder.h"
28
29namespace blink {
30
31SVGTransform::SVGTransform()
32    : SVGPropertyBase(classType())
33    , m_transformType(SVG_TRANSFORM_UNKNOWN)
34    , m_angle(0)
35{
36}
37
38SVGTransform::SVGTransform(SVGTransformType transformType, ConstructionMode mode)
39    : SVGPropertyBase(classType())
40    , m_transformType(transformType)
41    , m_angle(0)
42{
43    if (mode == ConstructZeroTransform)
44        m_matrix = AffineTransform(0, 0, 0, 0, 0, 0);
45}
46
47SVGTransform::SVGTransform(const AffineTransform& matrix)
48    : SVGPropertyBase(classType())
49    , m_transformType(SVG_TRANSFORM_MATRIX)
50    , m_angle(0)
51    , m_matrix(matrix)
52{
53}
54
55SVGTransform::SVGTransform(SVGTransformType transformType, float angle, const FloatPoint& center, const AffineTransform& matrix)
56    : SVGPropertyBase(classType())
57    , m_transformType(transformType)
58    , m_angle(angle)
59    , m_center(center)
60    , m_matrix(matrix)
61{
62}
63
64SVGTransform::~SVGTransform()
65{
66}
67
68PassRefPtr<SVGTransform> SVGTransform::clone() const
69{
70    return adoptRef(new SVGTransform(m_transformType, m_angle, m_center, m_matrix));
71}
72
73PassRefPtr<SVGPropertyBase> SVGTransform::cloneForAnimation(const String&) const
74{
75    // SVGTransform is never animated.
76    ASSERT_NOT_REACHED();
77    return nullptr;
78}
79
80void SVGTransform::setMatrix(const AffineTransform& matrix)
81{
82    onMatrixChange();
83    m_matrix = matrix;
84}
85
86void SVGTransform::onMatrixChange()
87{
88    m_transformType = SVG_TRANSFORM_MATRIX;
89    m_angle = 0;
90}
91
92void SVGTransform::setTranslate(float tx, float ty)
93{
94    m_transformType = SVG_TRANSFORM_TRANSLATE;
95    m_angle = 0;
96
97    m_matrix.makeIdentity();
98    m_matrix.translate(tx, ty);
99}
100
101FloatPoint SVGTransform::translate() const
102{
103    return FloatPoint::narrowPrecision(m_matrix.e(), m_matrix.f());
104}
105
106void SVGTransform::setScale(float sx, float sy)
107{
108    m_transformType = SVG_TRANSFORM_SCALE;
109    m_angle = 0;
110    m_center = FloatPoint();
111
112    m_matrix.makeIdentity();
113    m_matrix.scaleNonUniform(sx, sy);
114}
115
116FloatSize SVGTransform::scale() const
117{
118    return FloatSize::narrowPrecision(m_matrix.a(), m_matrix.d());
119}
120
121void SVGTransform::setRotate(float angle, float cx, float cy)
122{
123    m_transformType = SVG_TRANSFORM_ROTATE;
124    m_angle = angle;
125    m_center = FloatPoint(cx, cy);
126
127    // TODO: toString() implementation, which can show cx, cy (need to be stored?)
128    m_matrix.makeIdentity();
129    m_matrix.translate(cx, cy);
130    m_matrix.rotate(angle);
131    m_matrix.translate(-cx, -cy);
132}
133
134void SVGTransform::setSkewX(float angle)
135{
136    m_transformType = SVG_TRANSFORM_SKEWX;
137    m_angle = angle;
138
139    m_matrix.makeIdentity();
140    m_matrix.skewX(angle);
141}
142
143void SVGTransform::setSkewY(float angle)
144{
145    m_transformType = SVG_TRANSFORM_SKEWY;
146    m_angle = angle;
147
148    m_matrix.makeIdentity();
149    m_matrix.skewY(angle);
150}
151
152namespace {
153
154const String& transformTypePrefixForParsing(SVGTransformType type)
155{
156    switch (type) {
157    case SVG_TRANSFORM_UNKNOWN:
158        return emptyString();
159    case SVG_TRANSFORM_MATRIX: {
160        DEFINE_STATIC_LOCAL(String, matrixString, ("matrix("));
161        return matrixString;
162    }
163    case SVG_TRANSFORM_TRANSLATE: {
164        DEFINE_STATIC_LOCAL(String, translateString, ("translate("));
165        return translateString;
166    }
167    case SVG_TRANSFORM_SCALE: {
168        DEFINE_STATIC_LOCAL(String, scaleString, ("scale("));
169        return scaleString;
170    }
171    case SVG_TRANSFORM_ROTATE: {
172        DEFINE_STATIC_LOCAL(String, rotateString, ("rotate("));
173        return rotateString;
174    }
175    case SVG_TRANSFORM_SKEWX: {
176        DEFINE_STATIC_LOCAL(String, skewXString, ("skewX("));
177        return skewXString;
178    }
179    case SVG_TRANSFORM_SKEWY: {
180        DEFINE_STATIC_LOCAL(String, skewYString, ("skewY("));
181        return skewYString;
182    }
183    }
184
185    ASSERT_NOT_REACHED();
186    return emptyString();
187}
188
189}
190
191String SVGTransform::valueAsString() const
192{
193    const String& prefix = transformTypePrefixForParsing(m_transformType);
194    switch (m_transformType) {
195    case SVG_TRANSFORM_UNKNOWN:
196        return prefix;
197    case SVG_TRANSFORM_MATRIX: {
198        StringBuilder builder;
199        builder.append(prefix + String::number(m_matrix.a()) + ' ' + String::number(m_matrix.b()) + ' ' + String::number(m_matrix.c()) + ' ' +
200                       String::number(m_matrix.d()) + ' ' + String::number(m_matrix.e()) + ' ' + String::number(m_matrix.f()) + ')');
201        return builder.toString();
202    }
203    case SVG_TRANSFORM_TRANSLATE:
204        return prefix + String::number(m_matrix.e()) + ' ' + String::number(m_matrix.f()) + ')';
205    case SVG_TRANSFORM_SCALE:
206        return prefix + String::number(m_matrix.a()) + ' ' + String::number(m_matrix.d()) + ')';
207    case SVG_TRANSFORM_ROTATE: {
208        double angleInRad = deg2rad(m_angle);
209        double cosAngle = cos(angleInRad);
210        double sinAngle = sin(angleInRad);
211        float cx = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix.e() * (1 - cosAngle) - m_matrix.f() * sinAngle) / (1 - cosAngle) / 2 : 0);
212        float cy = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix.e() * sinAngle / (1 - cosAngle) + m_matrix.f()) / 2 : 0);
213        if (cx || cy)
214            return prefix + String::number(m_angle) + ' ' + String::number(cx) + ' ' + String::number(cy) + ')';
215        return prefix + String::number(m_angle) + ')';
216    }
217    case SVG_TRANSFORM_SKEWX:
218        return prefix + String::number(m_angle) + ')';
219    case SVG_TRANSFORM_SKEWY:
220        return prefix + String::number(m_angle) + ')';
221    }
222
223    ASSERT_NOT_REACHED();
224    return emptyString();
225}
226
227void SVGTransform::add(PassRefPtrWillBeRawPtr<SVGPropertyBase>, SVGElement*)
228{
229    // SVGTransform is not animated by itself.
230    ASSERT_NOT_REACHED();
231}
232
233void SVGTransform::calculateAnimatedValue(SVGAnimationElement*, float, unsigned, PassRefPtr<SVGPropertyBase>, PassRefPtr<SVGPropertyBase>, PassRefPtr<SVGPropertyBase>, SVGElement*)
234{
235    // SVGTransform is not animated by itself.
236    ASSERT_NOT_REACHED();
237}
238
239float SVGTransform::calculateDistance(PassRefPtr<SVGPropertyBase>, SVGElement*)
240{
241    // SVGTransform is not animated by itself.
242    ASSERT_NOT_REACHED();
243
244    return -1;
245}
246
247} // namespace blink
248