2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 // Free Software Foundation, Inc
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program 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
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 // Original author: Thatcher Ulrich <tu@tulrich.com> 2003
23 #include "SWFMatrix.h"
29 #include "GnashNumeric.h"
34 // This class intentionally uses overflows, which are not allowed in
35 // signed types; apart from being UB always, in practice it produces
36 // different results on different platforms.
38 // To avoid this, all calculations where an overflow could occur
39 // should use only unsigned types, but assign to the signed SWFMatrix
40 // members using only signed types. This would be much easier
41 // if the matrix values were also unsigned but were converted to
42 // signed for external users.
48 rotationX(const SWFMatrix
& m
)
50 const double b
= m
.b();
51 const double a
= m
.a();
52 return std::atan2(b
, a
);
56 rotationY(const SWFMatrix
& m
)
58 const double c
= m
.c();
59 const double d
= m
.d();
60 return std::atan2(-c
, d
);
66 return truncateWithFactor
<65536>(a
);
70 multiplyFixed16(boost::int32_t a
, boost::int32_t b
)
72 return (static_cast<boost::int64_t>(a
) *
73 static_cast<boost::int64_t>(b
) + 0x8000) >> 16;
77 } // anonymous namepace
80 SWFMatrix::transform(geometry::Point2d
& p
) const
82 boost::int32_t t0
= multiplyFixed16(_a
, p
.x
) + multiplyFixed16(_c
, p
.y
) + _tx
;
83 boost::int32_t t1
= multiplyFixed16(_b
, p
.x
) + multiplyFixed16(_d
, p
.y
) + _ty
;
89 SWFMatrix::transform(boost::int32_t& x
, boost::int32_t& y
) const
91 const boost::int32_t t0
= multiplyFixed16(_a
, x
) + multiplyFixed16(_c
, y
) + _tx
;
92 const boost::int32_t t1
= multiplyFixed16(_b
,x
) + multiplyFixed16(_d
, y
) + _ty
;
98 SWFMatrix::transform(geometry::Range2d
<boost::int32_t>& r
) const
100 const boost::int32_t xmin
= r
.getMinX();
101 const boost::int32_t xmax
= r
.getMaxX();
102 const boost::int32_t ymin
= r
.getMinY();
103 const boost::int32_t ymax
= r
.getMaxY();
105 point
p0(xmin
, ymin
);
106 point
p1(xmin
, ymax
);
107 point
p2(xmax
, ymax
);
108 point
p3(xmax
, ymin
);
116 r
.expandTo(p1
.x
, p1
.y
);
117 r
.expandTo(p2
.x
, p2
.y
);
118 r
.expandTo(p3
.x
, p3
.y
);
122 SWFMatrix::set_identity()
125 _b
= _c
= _tx
= _ty
= 0;
129 SWFMatrix::concatenate(const SWFMatrix
& m
)
132 t
._a
= multiplyFixed16(_a
, m
._a
) + multiplyFixed16(_c
, m
._b
);
133 t
._b
= multiplyFixed16(_b
, m
._a
) + multiplyFixed16(_d
, m
._b
);
134 t
._c
= multiplyFixed16(_a
, m
._c
) + multiplyFixed16(_c
, m
._d
);
135 t
._d
= multiplyFixed16(_b
, m
._c
) + multiplyFixed16(_d
, m
._d
);
136 t
._tx
= multiplyFixed16(_a
, m
._tx
) + multiplyFixed16(_c
, m
._ty
) + _tx
;
137 t
._ty
= multiplyFixed16(_b
, m
._tx
) + multiplyFixed16(_d
, m
._ty
) + _ty
;
142 // Concatenate a translation onto the front of our
143 // SWFMatrix. When transforming points, the translation
144 // happens first, then our original xform.
146 SWFMatrix::concatenate_translation(int xoffset
, int yoffset
)
148 _tx
+= multiplyFixed16(_a
, xoffset
) + multiplyFixed16(_c
, yoffset
);
149 _ty
+= multiplyFixed16(_b
, xoffset
) + multiplyFixed16(_d
, yoffset
);
152 // Concatenate scales to our SWFMatrix. When transforming points, these
153 // scales happen first, then our matrix.
155 SWFMatrix::concatenate_scale(double xscale
, double yscale
)
157 _a
= multiplyFixed16(_a
, toFixed16(xscale
));
158 _c
= multiplyFixed16(_c
, toFixed16(yscale
));
159 _b
= multiplyFixed16(_b
, toFixed16(xscale
));
160 _d
= multiplyFixed16(_d
, toFixed16(yscale
));
163 // Set this SWFMatrix to a blend of m1 and m2, parameterized by t.
165 SWFMatrix::set_lerp(const SWFMatrix
& m1
, const SWFMatrix
& m2
, float t
)
167 _a
= lerp
<float>(m1
._a
, m2
._a
, t
);
168 _b
= lerp
<float>(m1
._b
, m2
._b
, t
);
169 _c
= lerp
<float>(m1
._c
, m2
._c
, t
);
170 _d
= lerp
<float>(m1
._d
, m2
._d
, t
);
171 _tx
= lerp
<float>(m1
._tx
, m2
._tx
, t
);
172 _ty
= lerp
<float>(m1
._ty
, m2
._ty
, t
);
175 // Set the scale & rotation part of the SWFMatrix.
178 SWFMatrix::set_scale_rotation(double x_scale
, double y_scale
, double angle
)
180 const double cos_angle
= std::cos(angle
);
181 const double sin_angle
= std::sin(angle
);
182 _a
= toFixed16(x_scale
* cos_angle
);
183 _c
= toFixed16(y_scale
* -sin_angle
);
184 _b
= toFixed16(x_scale
* sin_angle
);
185 _d
= toFixed16(y_scale
* cos_angle
);
189 SWFMatrix::set_x_scale(double xscale
)
191 const double rot_x
= rotationX(*this);
192 _a
= toFixed16(xscale
* std::cos(rot_x
));
193 _b
= toFixed16(xscale
* std::sin(rot_x
));
197 SWFMatrix::set_y_scale(double yscale
)
199 const double rot_y
= rotationY(*this);
201 _c
= -toFixed16(yscale
* std::sin(rot_y
));
202 _d
= toFixed16(yscale
* std::cos(rot_y
));
206 SWFMatrix::set_scale(double xscale
, double yscale
)
208 const double rotation
= get_rotation();
209 set_scale_rotation(xscale
, yscale
, rotation
);
213 SWFMatrix::set_rotation(double rotation
)
215 const double rot_x
= rotationX(*this);
216 const double rot_y
= rotationY(*this);
218 const double scale_x
= get_x_scale();
219 const double scale_y
= get_y_scale();
221 _a
= toFixed16(scale_x
* std::cos(rotation
));
222 _b
= toFixed16(scale_x
* std::sin(rotation
));
223 _c
= -toFixed16(scale_y
* std::sin(rot_y
- rot_x
+ rotation
));
224 _d
= toFixed16(scale_y
* std::cos(rot_y
- rot_x
+ rotation
));
227 // Transform point 'p' by our SWFMatrix. Put the result in *result.
229 SWFMatrix::transform(point
* result
, const point
& p
) const
233 result
->x
= multiplyFixed16(_a
, p
.x
) + multiplyFixed16(_c
, p
.y
) + _tx
;
234 result
->y
= multiplyFixed16(_b
, p
.x
) + multiplyFixed16(_d
, p
.y
) + _ty
;
238 SWFMatrix::transform(SWFRect
& r
) const
240 if (r
.is_null()) return;
242 const boost::int32_t x1
= r
.get_x_min();
243 const boost::int32_t y1
= r
.get_y_min();
244 const boost::int32_t x2
= r
.get_x_max();
245 const boost::int32_t y2
= r
.get_y_max();
257 r
.set_to_point(p0
.x
, p0
.y
);
258 r
.expand_to_point(p1
.x
, p1
.y
);
259 r
.expand_to_point(p2
.x
, p2
.y
);
260 r
.expand_to_point(p3
.x
, p3
.y
);
263 // invert this SWFMatrix and return the result.
267 const boost::int64_t det
= determinant();
274 const double dn
= 65536.0 * 65536.0 / det
;
276 const boost::int32_t t0
= (boost::int32_t)(d() * dn
);
277 _d
= (boost::int32_t)(a() * dn
);
278 _c
= (boost::int32_t)(-c() * dn
);
279 _b
= (boost::int32_t)(-b() * dn
);
281 const boost::int32_t t4
= -(multiplyFixed16(_tx
, t0
) + multiplyFixed16(_ty
, _c
));
282 _ty
= -(multiplyFixed16(_tx
, _b
) + multiplyFixed16(_ty
, _d
));
291 SWFMatrix::get_x_scale() const
293 const double a2
= std::pow(static_cast<double>(a()), 2);
294 const double b2
= std::pow(static_cast<double>(b()), 2);
295 return std::sqrt(a2
+ b2
) / 65536.0;
299 SWFMatrix::get_y_scale() const
301 const double d2
= std::pow(static_cast<double>(d()), 2);
302 const double c2
= std::pow(static_cast<double>(c()), 2);
303 return std::sqrt(d2
+ c2
) / 65536.0;
307 SWFMatrix::get_rotation() const
309 return rotationX(*this);
314 SWFMatrix::determinant() const
317 // | _b _d _ty | = T. Using the Leibniz formula:
320 // Det(T) = ( (_a * _d * 1 ) + (_c * _ty * 0) + (_tx * _b * 0) ) -
321 // ( (0 * _d * _tx) + (0 * _ty * _a) + (1 * _c * _b) )
322 // = _a * _d - _b * _c
323 return (boost::int64_t)a() * d() - (boost::int64_t)b() * c();
327 operator<<(std::ostream
& o
, const SWFMatrix
& m
)
329 // 8 digits and a decimal point.
330 const short fieldWidth
= 9;
332 o
<< std::endl
<< "|"
333 << std::setw(fieldWidth
) << std::fixed
<< std::setprecision(4)
334 << m
.a() / 65536.0 << " "
335 << std::setw(fieldWidth
) << std::fixed
<< std::setprecision(4)
336 << m
.c()/ 65536.0 << " "
337 << std::setw(fieldWidth
) << std::fixed
<< std::setprecision(4)
338 << twipsToPixels(m
.tx()) << " |"
340 << std::setw(fieldWidth
) << std::fixed
<< std::setprecision(4)
341 << m
.b()/ 65536.0 << " "
342 << std::setw(fieldWidth
) << std::fixed
<< std::setprecision(4)
343 << m
.d() / 65536.0 << " "
344 << std::setw(fieldWidth
) << std::fixed
<< std::setprecision(4)
345 << twipsToPixels(m
.ty()) << " |";
350 } // end namespace gnash
357 // indent-tabs-mode: t