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/logging.h"
13 #include "base/strings/stringprintf.h"
14 #include "ui/gfx/geometry/box_f.h"
15 #include "ui/gfx/geometry/point.h"
16 #include "ui/gfx/geometry/point3_f.h"
17 #include "ui/gfx/geometry/rect.h"
18 #include "ui/gfx/geometry/safe_integer_conversions.h"
19 #include "ui/gfx/geometry/vector3d_f.h"
20 #include "ui/gfx/skia_util.h"
21 #include "ui/gfx/transform_util.h"
27 // Taken from SkMatrix44.
28 const SkMScalar kEpsilon
= 1e-8f
;
30 SkMScalar
TanDegrees(double degrees
) {
31 double radians
= degrees
* M_PI
/ 180;
32 return SkDoubleToMScalar(std::tan(radians
));
35 inline bool ApproximatelyZero(SkMScalar x
, SkMScalar tolerance
) {
36 return std::abs(x
) <= tolerance
;
39 inline bool ApproximatelyOne(SkMScalar x
, SkMScalar tolerance
) {
40 return std::abs(x
- SkDoubleToMScalar(1.0)) <= tolerance
;
45 Transform::Transform(SkMScalar col1row1
,
61 : matrix_(SkMatrix44::kUninitialized_Constructor
) {
62 matrix_
.set(0, 0, col1row1
);
63 matrix_
.set(1, 0, col1row2
);
64 matrix_
.set(2, 0, col1row3
);
65 matrix_
.set(3, 0, col1row4
);
67 matrix_
.set(0, 1, col2row1
);
68 matrix_
.set(1, 1, col2row2
);
69 matrix_
.set(2, 1, col2row3
);
70 matrix_
.set(3, 1, col2row4
);
72 matrix_
.set(0, 2, col3row1
);
73 matrix_
.set(1, 2, col3row2
);
74 matrix_
.set(2, 2, col3row3
);
75 matrix_
.set(3, 2, col3row4
);
77 matrix_
.set(0, 3, col4row1
);
78 matrix_
.set(1, 3, col4row2
);
79 matrix_
.set(2, 3, col4row3
);
80 matrix_
.set(3, 3, col4row4
);
83 Transform::Transform(SkMScalar col1row1
,
87 SkMScalar x_translation
,
88 SkMScalar y_translation
)
89 : matrix_(SkMatrix44::kIdentity_Constructor
) {
90 matrix_
.set(0, 0, col1row1
);
91 matrix_
.set(1, 0, col1row2
);
92 matrix_
.set(0, 1, col2row1
);
93 matrix_
.set(1, 1, col2row2
);
94 matrix_
.set(0, 3, x_translation
);
95 matrix_
.set(1, 3, y_translation
);
98 void Transform::RotateAboutXAxis(double degrees
) {
99 double radians
= degrees
* M_PI
/ 180;
100 SkMScalar cosTheta
= SkDoubleToMScalar(std::cos(radians
));
101 SkMScalar sinTheta
= SkDoubleToMScalar(std::sin(radians
));
102 if (matrix_
.isIdentity()) {
103 matrix_
.set3x3(1, 0, 0,
104 0, cosTheta
, sinTheta
,
105 0, -sinTheta
, cosTheta
);
107 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
109 0, cosTheta
, sinTheta
,
110 0, -sinTheta
, cosTheta
);
111 matrix_
.preConcat(rot
);
115 void Transform::RotateAboutYAxis(double degrees
) {
116 double radians
= degrees
* M_PI
/ 180;
117 SkMScalar cosTheta
= SkDoubleToMScalar(std::cos(radians
));
118 SkMScalar sinTheta
= SkDoubleToMScalar(std::sin(radians
));
119 if (matrix_
.isIdentity()) {
120 // Note carefully the placement of the -sinTheta for rotation about
121 // y-axis is different than rotation about x-axis or z-axis.
122 matrix_
.set3x3(cosTheta
, 0, -sinTheta
,
124 sinTheta
, 0, cosTheta
);
126 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
127 rot
.set3x3(cosTheta
, 0, -sinTheta
,
129 sinTheta
, 0, cosTheta
);
130 matrix_
.preConcat(rot
);
134 void Transform::RotateAboutZAxis(double degrees
) {
135 double radians
= degrees
* M_PI
/ 180;
136 SkMScalar cosTheta
= SkDoubleToMScalar(std::cos(radians
));
137 SkMScalar sinTheta
= SkDoubleToMScalar(std::sin(radians
));
138 if (matrix_
.isIdentity()) {
139 matrix_
.set3x3(cosTheta
, sinTheta
, 0,
140 -sinTheta
, cosTheta
, 0,
143 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
144 rot
.set3x3(cosTheta
, sinTheta
, 0,
145 -sinTheta
, cosTheta
, 0,
147 matrix_
.preConcat(rot
);
151 void Transform::RotateAbout(const Vector3dF
& axis
, double degrees
) {
152 if (matrix_
.isIdentity()) {
153 matrix_
.setRotateDegreesAbout(SkFloatToMScalar(axis
.x()),
154 SkFloatToMScalar(axis
.y()),
155 SkFloatToMScalar(axis
.z()),
156 SkDoubleToMScalar(degrees
));
158 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
159 rot
.setRotateDegreesAbout(SkFloatToMScalar(axis
.x()),
160 SkFloatToMScalar(axis
.y()),
161 SkFloatToMScalar(axis
.z()),
162 SkDoubleToMScalar(degrees
));
163 matrix_
.preConcat(rot
);
167 void Transform::Scale(SkMScalar x
, SkMScalar y
) { matrix_
.preScale(x
, y
, 1); }
169 void Transform::Scale3d(SkMScalar x
, SkMScalar y
, SkMScalar z
) {
170 matrix_
.preScale(x
, y
, z
);
173 void Transform::Translate(SkMScalar x
, SkMScalar y
) {
174 matrix_
.preTranslate(x
, y
, 0);
177 void Transform::Translate3d(SkMScalar x
, SkMScalar y
, SkMScalar z
) {
178 matrix_
.preTranslate(x
, y
, z
);
181 void Transform::SkewX(double angle_x
) {
182 if (matrix_
.isIdentity()) {
183 matrix_
.set(0, 1, TanDegrees(angle_x
));
185 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
186 skew
.set(0, 1, TanDegrees(angle_x
));
187 matrix_
.preConcat(skew
);
191 void Transform::SkewY(double angle_y
) {
192 if (matrix_
.isIdentity()) {
193 matrix_
.set(1, 0, TanDegrees(angle_y
));
195 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
196 skew
.set(1, 0, TanDegrees(angle_y
));
197 matrix_
.preConcat(skew
);
201 void Transform::ApplyPerspectiveDepth(SkMScalar depth
) {
204 if (matrix_
.isIdentity()) {
205 matrix_
.set(3, 2, -SK_MScalar1
/ depth
);
207 SkMatrix44
m(SkMatrix44::kIdentity_Constructor
);
208 m
.set(3, 2, -SK_MScalar1
/ depth
);
209 matrix_
.preConcat(m
);
213 void Transform::PreconcatTransform(const Transform
& transform
) {
214 matrix_
.preConcat(transform
.matrix_
);
217 void Transform::ConcatTransform(const Transform
& transform
) {
218 matrix_
.postConcat(transform
.matrix_
);
221 bool Transform::IsApproximatelyIdentityOrTranslation(
222 SkMScalar tolerance
) const {
223 DCHECK_GE(tolerance
, 0);
225 ApproximatelyOne(matrix_
.get(0, 0), tolerance
) &&
226 ApproximatelyZero(matrix_
.get(1, 0), tolerance
) &&
227 ApproximatelyZero(matrix_
.get(2, 0), tolerance
) &&
228 matrix_
.get(3, 0) == 0 &&
229 ApproximatelyZero(matrix_
.get(0, 1), tolerance
) &&
230 ApproximatelyOne(matrix_
.get(1, 1), tolerance
) &&
231 ApproximatelyZero(matrix_
.get(2, 1), tolerance
) &&
232 matrix_
.get(3, 1) == 0 &&
233 ApproximatelyZero(matrix_
.get(0, 2), tolerance
) &&
234 ApproximatelyZero(matrix_
.get(1, 2), tolerance
) &&
235 ApproximatelyOne(matrix_
.get(2, 2), tolerance
) &&
236 matrix_
.get(3, 2) == 0 &&
237 matrix_
.get(3, 3) == 1;
240 bool Transform::IsIdentityOrIntegerTranslation() const {
241 if (!IsIdentityOrTranslation())
244 bool no_fractional_translation
=
245 static_cast<int>(matrix_
.get(0, 3)) == matrix_
.get(0, 3) &&
246 static_cast<int>(matrix_
.get(1, 3)) == matrix_
.get(1, 3) &&
247 static_cast<int>(matrix_
.get(2, 3)) == matrix_
.get(2, 3);
249 return no_fractional_translation
;
252 bool Transform::IsBackFaceVisible() const {
253 // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
254 // would have its back face visible after applying the transform.
255 if (matrix_
.isIdentity())
258 // This is done by transforming the normal and seeing if the resulting z
259 // value is positive or negative. However, note that transforming a normal
260 // actually requires using the inverse-transpose of the original transform.
262 // We can avoid inverting and transposing the matrix since we know we want
263 // to transform only the specific normal vector (0, 0, 1, 0). In this case,
264 // we only need the 3rd row, 3rd column of the inverse-transpose. We can
265 // calculate only the 3rd row 3rd column element of the inverse, skipping
268 // For more information, refer to:
269 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
272 double determinant
= matrix_
.determinant();
274 // If matrix was not invertible, then just assume back face is not visible.
275 if (std::abs(determinant
) <= kEpsilon
)
278 // Compute the cofactor of the 3rd row, 3rd column.
279 double cofactor_part_1
=
280 matrix_
.get(0, 0) * matrix_
.get(1, 1) * matrix_
.get(3, 3);
282 double cofactor_part_2
=
283 matrix_
.get(0, 1) * matrix_
.get(1, 3) * matrix_
.get(3, 0);
285 double cofactor_part_3
=
286 matrix_
.get(0, 3) * matrix_
.get(1, 0) * matrix_
.get(3, 1);
288 double cofactor_part_4
=
289 matrix_
.get(0, 0) * matrix_
.get(1, 3) * matrix_
.get(3, 1);
291 double cofactor_part_5
=
292 matrix_
.get(0, 1) * matrix_
.get(1, 0) * matrix_
.get(3, 3);
294 double cofactor_part_6
=
295 matrix_
.get(0, 3) * matrix_
.get(1, 1) * matrix_
.get(3, 0);
305 // Technically the transformed z component is cofactor33 / determinant. But
306 // we can avoid the costly division because we only care about the resulting
307 // +/- sign; we can check this equivalently by multiplication.
308 return cofactor33
* determinant
< 0;
311 bool Transform::GetInverse(Transform
* transform
) const {
312 if (!matrix_
.invert(&transform
->matrix_
)) {
313 // Initialize the return value to identity if this matrix turned
314 // out to be un-invertible.
315 transform
->MakeIdentity();
322 bool Transform::Preserves2dAxisAlignment() const {
323 // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
324 // after being transformed by this matrix (and implicitly projected by
325 // dropping any non-zero z-values).
327 // The 4th column can be ignored because translations don't affect axis
328 // alignment. The 3rd column can be ignored because we are assuming 2d
329 // inputs, where z-values will be zero. The 3rd row can also be ignored
330 // because we are assuming 2d outputs, and any resulting z-value is dropped
331 // anyway. For the inner 2x2 portion, the only effects that keep a rect axis
332 // aligned are (1) swapping axes and (2) scaling axes. This can be checked by
333 // verifying only 1 element of every column and row is non-zero. Degenerate
334 // cases that project the x or y dimension to zero are considered to preserve
337 // If the matrix does have perspective component that is affected by x or y
338 // values: The current implementation conservatively assumes that axis
339 // alignment is not preserved.
341 bool has_x_or_y_perspective
=
342 matrix_
.get(3, 0) != 0 || matrix_
.get(3, 1) != 0;
344 int num_non_zero_in_row_0
= 0;
345 int num_non_zero_in_row_1
= 0;
346 int num_non_zero_in_col_0
= 0;
347 int num_non_zero_in_col_1
= 0;
349 if (std::abs(matrix_
.get(0, 0)) > kEpsilon
) {
350 num_non_zero_in_row_0
++;
351 num_non_zero_in_col_0
++;
354 if (std::abs(matrix_
.get(0, 1)) > kEpsilon
) {
355 num_non_zero_in_row_0
++;
356 num_non_zero_in_col_1
++;
359 if (std::abs(matrix_
.get(1, 0)) > kEpsilon
) {
360 num_non_zero_in_row_1
++;
361 num_non_zero_in_col_0
++;
364 if (std::abs(matrix_
.get(1, 1)) > kEpsilon
) {
365 num_non_zero_in_row_1
++;
366 num_non_zero_in_col_1
++;
370 num_non_zero_in_row_0
<= 1 &&
371 num_non_zero_in_row_1
<= 1 &&
372 num_non_zero_in_col_0
<= 1 &&
373 num_non_zero_in_col_1
<= 1 &&
374 !has_x_or_y_perspective
;
377 void Transform::Transpose() {
381 void Transform::FlattenTo2d() {
382 matrix_
.set(2, 0, 0.0);
383 matrix_
.set(2, 1, 0.0);
384 matrix_
.set(0, 2, 0.0);
385 matrix_
.set(1, 2, 0.0);
386 matrix_
.set(2, 2, 1.0);
387 matrix_
.set(3, 2, 0.0);
388 matrix_
.set(2, 3, 0.0);
391 Vector2dF
Transform::To2dTranslation() const {
392 return gfx::Vector2dF(SkMScalarToFloat(matrix_
.get(0, 3)),
393 SkMScalarToFloat(matrix_
.get(1, 3)));
396 void Transform::TransformPoint(Point
* point
) const {
398 TransformPointInternal(matrix_
, point
);
401 void Transform::TransformPoint(Point3F
* point
) const {
403 TransformPointInternal(matrix_
, point
);
406 bool Transform::TransformPointReverse(Point
* point
) const {
409 // TODO(sad): Try to avoid trying to invert the matrix.
410 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
411 if (!matrix_
.invert(&inverse
))
414 TransformPointInternal(inverse
, point
);
418 bool Transform::TransformPointReverse(Point3F
* point
) const {
421 // TODO(sad): Try to avoid trying to invert the matrix.
422 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
423 if (!matrix_
.invert(&inverse
))
426 TransformPointInternal(inverse
, point
);
430 void Transform::TransformRect(RectF
* rect
) const {
431 if (matrix_
.isIdentity())
434 SkRect src
= RectFToSkRect(*rect
);
435 const SkMatrix
& matrix
= matrix_
;
436 matrix
.mapRect(&src
);
437 *rect
= SkRectToRectF(src
);
440 bool Transform::TransformRectReverse(RectF
* rect
) const {
441 if (matrix_
.isIdentity())
444 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
445 if (!matrix_
.invert(&inverse
))
448 const SkMatrix
& matrix
= inverse
;
449 SkRect src
= RectFToSkRect(*rect
);
450 matrix
.mapRect(&src
);
451 *rect
= SkRectToRectF(src
);
455 void Transform::TransformBox(BoxF
* box
) const {
457 bool first_point
= true;
458 for (int corner
= 0; corner
< 8; ++corner
) {
459 gfx::Point3F point
= box
->origin();
460 point
+= gfx::Vector3dF(corner
& 1 ? box
->width() : 0.f
,
461 corner
& 2 ? box
->height() : 0.f
,
462 corner
& 4 ? box
->depth() : 0.f
);
463 TransformPoint(&point
);
465 bounds
.set_origin(point
);
468 bounds
.ExpandTo(point
);
474 bool Transform::TransformBoxReverse(BoxF
* box
) const {
475 gfx::Transform inverse
= *this;
476 if (!GetInverse(&inverse
))
478 inverse
.TransformBox(box
);
482 bool Transform::Blend(const Transform
& from
, double progress
) {
483 DecomposedTransform to_decomp
;
484 DecomposedTransform from_decomp
;
485 if (!DecomposeTransform(&to_decomp
, *this) ||
486 !DecomposeTransform(&from_decomp
, from
))
489 if (!BlendDecomposedTransforms(&to_decomp
, to_decomp
, from_decomp
, progress
))
492 matrix_
= ComposeTransform(to_decomp
).matrix();
496 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
497 Point3F
* point
) const {
498 if (xform
.isIdentity())
501 SkMScalar p
[4] = {SkFloatToMScalar(point
->x()), SkFloatToMScalar(point
->y()),
502 SkFloatToMScalar(point
->z()), 1};
504 xform
.mapMScalars(p
);
506 if (p
[3] != SK_MScalar1
&& p
[3] != 0.f
) {
507 float w_inverse
= SK_MScalar1
/ p
[3];
508 point
->SetPoint(p
[0] * w_inverse
, p
[1] * w_inverse
, p
[2] * w_inverse
);
510 point
->SetPoint(p
[0], p
[1], p
[2]);
514 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
515 Point
* point
) const {
516 if (xform
.isIdentity())
519 SkMScalar p
[4] = {SkIntToMScalar(point
->x()), SkIntToMScalar(point
->y()),
522 xform
.mapMScalars(p
);
524 point
->SetPoint(ToRoundedInt(p
[0]), ToRoundedInt(p
[1]));
527 std::string
Transform::ToString() const {
528 return base::StringPrintf(
529 "[ %+0.4f %+0.4f %+0.4f %+0.4f \n"
530 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
531 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
532 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",