1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
8 #include "ui/gfx/transform.h"
12 #include "base/stringprintf.h"
13 #include "ui/gfx/point.h"
14 #include "ui/gfx/point3_f.h"
15 #include "ui/gfx/vector3d_f.h"
16 #include "ui/gfx/rect.h"
17 #include "ui/gfx/safe_integer_conversions.h"
18 #include "ui/gfx/skia_util.h"
19 #include "ui/gfx/transform_util.h"
25 // Taken from SkMatrix44.
26 const double kTooSmallForDeterminant
= 1e-8;
28 double TanDegrees(double degrees
) {
29 double radians
= degrees
* M_PI
/ 180;
30 return std::tan(radians
);
36 double col1row1
, double col2row1
, double col3row1
, double col4row1
,
37 double col1row2
, double col2row2
, double col3row2
, double col4row2
,
38 double col1row3
, double col2row3
, double col3row3
, double col4row3
,
39 double col1row4
, double col2row4
, double col3row4
, double col4row4
)
40 : matrix_(SkMatrix44::kUninitialized_Constructor
)
42 matrix_
.setDouble(0, 0, col1row1
);
43 matrix_
.setDouble(1, 0, col1row2
);
44 matrix_
.setDouble(2, 0, col1row3
);
45 matrix_
.setDouble(3, 0, col1row4
);
47 matrix_
.setDouble(0, 1, col2row1
);
48 matrix_
.setDouble(1, 1, col2row2
);
49 matrix_
.setDouble(2, 1, col2row3
);
50 matrix_
.setDouble(3, 1, col2row4
);
52 matrix_
.setDouble(0, 2, col3row1
);
53 matrix_
.setDouble(1, 2, col3row2
);
54 matrix_
.setDouble(2, 2, col3row3
);
55 matrix_
.setDouble(3, 2, col3row4
);
57 matrix_
.setDouble(0, 3, col4row1
);
58 matrix_
.setDouble(1, 3, col4row2
);
59 matrix_
.setDouble(2, 3, col4row3
);
60 matrix_
.setDouble(3, 3, col4row4
);
64 double col1row1
, double col2row1
,
65 double col1row2
, double col2row2
,
66 double x_translation
, double y_translation
)
67 : matrix_(SkMatrix44::kIdentity_Constructor
)
69 matrix_
.setDouble(0, 0, col1row1
);
70 matrix_
.setDouble(1, 0, col1row2
);
71 matrix_
.setDouble(0, 1, col2row1
);
72 matrix_
.setDouble(1, 1, col2row2
);
73 matrix_
.setDouble(0, 3, x_translation
);
74 matrix_
.setDouble(1, 3, y_translation
);
77 void Transform::RotateAboutXAxis(double degrees
) {
78 double radians
= degrees
* M_PI
/ 180;
79 double cosTheta
= std::cos(radians
);
80 double sinTheta
= std::sin(radians
);
81 if (matrix_
.isIdentity()) {
82 matrix_
.set3x3(1, 0, 0,
83 0, cosTheta
, sinTheta
,
84 0, -sinTheta
, cosTheta
);
86 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
88 0, cosTheta
, sinTheta
,
89 0, -sinTheta
, cosTheta
);
90 matrix_
.preConcat(rot
);
94 void Transform::RotateAboutYAxis(double degrees
) {
95 double radians
= degrees
* M_PI
/ 180;
96 double cosTheta
= std::cos(radians
);
97 double sinTheta
= std::sin(radians
);
98 if (matrix_
.isIdentity()) {
99 // Note carefully the placement of the -sinTheta for rotation about
100 // y-axis is different than rotation about x-axis or z-axis.
101 matrix_
.set3x3(cosTheta
, 0, -sinTheta
,
103 sinTheta
, 0, cosTheta
);
105 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
106 rot
.set3x3(cosTheta
, 0, -sinTheta
,
108 sinTheta
, 0, cosTheta
);
109 matrix_
.preConcat(rot
);
113 void Transform::RotateAboutZAxis(double degrees
) {
114 double radians
= degrees
* M_PI
/ 180;
115 double cosTheta
= std::cos(radians
);
116 double sinTheta
= std::sin(radians
);
117 if (matrix_
.isIdentity()) {
118 matrix_
.set3x3(cosTheta
, sinTheta
, 0,
119 -sinTheta
, cosTheta
, 0,
122 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
123 rot
.set3x3(cosTheta
, sinTheta
, 0,
124 -sinTheta
, cosTheta
, 0,
126 matrix_
.preConcat(rot
);
130 void Transform::RotateAbout(const Vector3dF
& axis
, double degrees
) {
131 if (matrix_
.isIdentity()) {
132 matrix_
.setRotateDegreesAbout(SkDoubleToMScalar(axis
.x()),
133 SkDoubleToMScalar(axis
.y()),
134 SkDoubleToMScalar(axis
.z()),
135 SkDoubleToMScalar(degrees
));
137 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
138 rot
.setRotateDegreesAbout(SkDoubleToMScalar(axis
.x()),
139 SkDoubleToMScalar(axis
.y()),
140 SkDoubleToMScalar(axis
.z()),
141 SkDoubleToMScalar(degrees
));
142 matrix_
.preConcat(rot
);
146 void Transform::Scale(double x
, double y
) {
147 matrix_
.preScale(SkDoubleToMScalar(x
), SkDoubleToMScalar(y
), 1);
150 void Transform::Scale3d(double x
, double y
, double z
) {
151 matrix_
.preScale(SkDoubleToMScalar(x
),
152 SkDoubleToMScalar(y
),
153 SkDoubleToMScalar(z
));
156 void Transform::Translate(double x
, double y
) {
157 matrix_
.preTranslate(SkDoubleToMScalar(x
), SkDoubleToMScalar(y
), 0);
160 void Transform::Translate3d(double x
, double y
, double z
) {
161 matrix_
.preTranslate(SkDoubleToMScalar(x
),
162 SkDoubleToMScalar(y
),
163 SkDoubleToMScalar(z
));
166 void Transform::SkewX(double angle_x
) {
167 if (matrix_
.isIdentity())
168 matrix_
.setDouble(0, 1, TanDegrees(angle_x
));
170 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
171 skew
.setDouble(0, 1, TanDegrees(angle_x
));
172 matrix_
.preConcat(skew
);
176 void Transform::SkewY(double angle_y
) {
177 if (matrix_
.isIdentity())
178 matrix_
.setDouble(1, 0, TanDegrees(angle_y
));
180 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
181 skew
.setDouble(1, 0, TanDegrees(angle_y
));
182 matrix_
.preConcat(skew
);
186 void Transform::ApplyPerspectiveDepth(double depth
) {
189 if (matrix_
.isIdentity())
190 matrix_
.setDouble(3, 2, -1.0 / depth
);
192 SkMatrix44
m(SkMatrix44::kIdentity_Constructor
);
193 m
.setDouble(3, 2, -1.0 / depth
);
194 matrix_
.preConcat(m
);
198 void Transform::PreconcatTransform(const Transform
& transform
) {
199 matrix_
.preConcat(transform
.matrix_
);
202 void Transform::ConcatTransform(const Transform
& transform
) {
203 matrix_
.postConcat(transform
.matrix_
);
206 bool Transform::IsIdentityOrIntegerTranslation() const {
207 if (!IsIdentityOrTranslation())
210 bool no_fractional_translation
=
211 static_cast<int>(matrix_
.getDouble(0, 3)) == matrix_
.getDouble(0, 3) &&
212 static_cast<int>(matrix_
.getDouble(1, 3)) == matrix_
.getDouble(1, 3) &&
213 static_cast<int>(matrix_
.getDouble(2, 3)) == matrix_
.getDouble(2, 3);
215 return no_fractional_translation
;
218 bool Transform::IsBackFaceVisible() const {
219 // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
220 // would have its back face visible after applying the transform.
221 if (matrix_
.isIdentity())
224 // This is done by transforming the normal and seeing if the resulting z
225 // value is positive or negative. However, note that transforming a normal
226 // actually requires using the inverse-transpose of the original transform.
228 // We can avoid inverting and transposing the matrix since we know we want
229 // to transform only the specific normal vector (0, 0, 1, 0). In this case,
230 // we only need the 3rd row, 3rd column of the inverse-transpose. We can
231 // calculate only the 3rd row 3rd column element of the inverse, skipping
234 // For more information, refer to:
235 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
238 double determinant
= matrix_
.determinant();
240 // If matrix was not invertible, then just assume back face is not visible.
241 if (std::abs(determinant
) <= kTooSmallForDeterminant
)
244 // Compute the cofactor of the 3rd row, 3rd column.
245 double cofactor_part_1
=
246 matrix_
.getDouble(0, 0) *
247 matrix_
.getDouble(1, 1) *
248 matrix_
.getDouble(3, 3);
250 double cofactor_part_2
=
251 matrix_
.getDouble(0, 1) *
252 matrix_
.getDouble(1, 3) *
253 matrix_
.getDouble(3, 0);
255 double cofactor_part_3
=
256 matrix_
.getDouble(0, 3) *
257 matrix_
.getDouble(1, 0) *
258 matrix_
.getDouble(3, 1);
260 double cofactor_part_4
=
261 matrix_
.getDouble(0, 0) *
262 matrix_
.getDouble(1, 3) *
263 matrix_
.getDouble(3, 1);
265 double cofactor_part_5
=
266 matrix_
.getDouble(0, 1) *
267 matrix_
.getDouble(1, 0) *
268 matrix_
.getDouble(3, 3);
270 double cofactor_part_6
=
271 matrix_
.getDouble(0, 3) *
272 matrix_
.getDouble(1, 1) *
273 matrix_
.getDouble(3, 0);
283 // Technically the transformed z component is cofactor33 / determinant. But
284 // we can avoid the costly division because we only care about the resulting
285 // +/- sign; we can check this equivalently by multiplication.
286 return cofactor33
* determinant
< 0;
289 bool Transform::GetInverse(Transform
* transform
) const {
290 if (!matrix_
.invert(&transform
->matrix_
)) {
291 // Initialize the return value to identity if this matrix turned
292 // out to be un-invertible.
293 transform
->MakeIdentity();
300 void Transform::Transpose() {
304 void Transform::FlattenTo2d() {
305 matrix_
.setDouble(2, 0, 0.0);
306 matrix_
.setDouble(2, 1, 0.0);
307 matrix_
.setDouble(0, 2, 0.0);
308 matrix_
.setDouble(1, 2, 0.0);
309 matrix_
.setDouble(2, 2, 1.0);
310 matrix_
.setDouble(3, 2, 0.0);
311 matrix_
.setDouble(2, 3, 0.0);
314 void Transform::TransformPoint(Point
& point
) const {
315 TransformPointInternal(matrix_
, point
);
318 void Transform::TransformPoint(Point3F
& point
) const {
319 TransformPointInternal(matrix_
, point
);
322 bool Transform::TransformPointReverse(Point
& point
) const {
323 // TODO(sad): Try to avoid trying to invert the matrix.
324 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
325 if (!matrix_
.invert(&inverse
))
328 TransformPointInternal(inverse
, point
);
332 bool Transform::TransformPointReverse(Point3F
& point
) const {
333 // TODO(sad): Try to avoid trying to invert the matrix.
334 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
335 if (!matrix_
.invert(&inverse
))
338 TransformPointInternal(inverse
, point
);
342 void Transform::TransformRect(RectF
* rect
) const {
343 if (matrix_
.isIdentity())
346 SkRect src
= RectFToSkRect(*rect
);
347 const SkMatrix
& matrix
= matrix_
;
348 matrix
.mapRect(&src
);
349 *rect
= SkRectToRectF(src
);
352 bool Transform::TransformRectReverse(RectF
* rect
) const {
353 if (matrix_
.isIdentity())
356 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
357 if (!matrix_
.invert(&inverse
))
360 const SkMatrix
& matrix
= inverse
;
361 SkRect src
= RectFToSkRect(*rect
);
362 matrix
.mapRect(&src
);
363 *rect
= SkRectToRectF(src
);
367 bool Transform::Blend(const Transform
& from
, double progress
) {
368 if (progress
<= 0.0) {
376 DecomposedTransform to_decomp
;
377 DecomposedTransform from_decomp
;
378 if (!DecomposeTransform(&to_decomp
, *this) ||
379 !DecomposeTransform(&from_decomp
, from
))
382 if (!BlendDecomposedTransforms(&to_decomp
, to_decomp
, from_decomp
, progress
))
385 matrix_
= ComposeTransform(to_decomp
).matrix();
389 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
390 Point3F
& point
) const {
391 if (xform
.isIdentity())
395 SkDoubleToMScalar(point
.x()),
396 SkDoubleToMScalar(point
.y()),
397 SkDoubleToMScalar(point
.z()),
401 xform
.mapMScalars(p
);
403 if (p
[3] != 1 && abs(p
[3]) > 0) {
404 point
.SetPoint(p
[0] / p
[3], p
[1] / p
[3], p
[2]/ p
[3]);
406 point
.SetPoint(p
[0], p
[1], p
[2]);
410 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
411 Point
& point
) const {
412 if (xform
.isIdentity())
416 SkDoubleToMScalar(point
.x()),
417 SkDoubleToMScalar(point
.y()),
418 SkDoubleToMScalar(0),
422 xform
.mapMScalars(p
);
424 point
.SetPoint(ToRoundedInt(p
[0]), ToRoundedInt(p
[1]));
427 std::string
Transform::ToString() const {
428 return base::StringPrintf(
429 "[ %+0.4f %+0.4f %+0.4f %+0.4f \n"
430 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
431 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
432 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
433 matrix_
.getDouble(0, 0),
434 matrix_
.getDouble(0, 1),
435 matrix_
.getDouble(0, 2),
436 matrix_
.getDouble(0, 3),
437 matrix_
.getDouble(1, 0),
438 matrix_
.getDouble(1, 1),
439 matrix_
.getDouble(1, 2),
440 matrix_
.getDouble(1, 3),
441 matrix_
.getDouble(2, 0),
442 matrix_
.getDouble(2, 1),
443 matrix_
.getDouble(2, 2),
444 matrix_
.getDouble(2, 3),
445 matrix_
.getDouble(3, 0),
446 matrix_
.getDouble(3, 1),
447 matrix_
.getDouble(3, 2),
448 matrix_
.getDouble(3, 3));