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
;
43 static float Round(float f
) {
46 return (f
> 0.f
) ? std::floor(f
+ 0.5f
) : std::ceil(f
- 0.5f
);
51 Transform::Transform(SkMScalar col1row1
,
67 : matrix_(SkMatrix44::kUninitialized_Constructor
) {
68 matrix_
.set(0, 0, col1row1
);
69 matrix_
.set(1, 0, col1row2
);
70 matrix_
.set(2, 0, col1row3
);
71 matrix_
.set(3, 0, col1row4
);
73 matrix_
.set(0, 1, col2row1
);
74 matrix_
.set(1, 1, col2row2
);
75 matrix_
.set(2, 1, col2row3
);
76 matrix_
.set(3, 1, col2row4
);
78 matrix_
.set(0, 2, col3row1
);
79 matrix_
.set(1, 2, col3row2
);
80 matrix_
.set(2, 2, col3row3
);
81 matrix_
.set(3, 2, col3row4
);
83 matrix_
.set(0, 3, col4row1
);
84 matrix_
.set(1, 3, col4row2
);
85 matrix_
.set(2, 3, col4row3
);
86 matrix_
.set(3, 3, col4row4
);
89 Transform::Transform(SkMScalar col1row1
,
93 SkMScalar x_translation
,
94 SkMScalar y_translation
)
95 : matrix_(SkMatrix44::kIdentity_Constructor
) {
96 matrix_
.set(0, 0, col1row1
);
97 matrix_
.set(1, 0, col1row2
);
98 matrix_
.set(0, 1, col2row1
);
99 matrix_
.set(1, 1, col2row2
);
100 matrix_
.set(0, 3, x_translation
);
101 matrix_
.set(1, 3, y_translation
);
104 void Transform::RotateAboutXAxis(double degrees
) {
105 double radians
= degrees
* M_PI
/ 180;
106 SkMScalar cosTheta
= SkDoubleToMScalar(std::cos(radians
));
107 SkMScalar sinTheta
= SkDoubleToMScalar(std::sin(radians
));
108 if (matrix_
.isIdentity()) {
109 matrix_
.set3x3(1, 0, 0,
110 0, cosTheta
, sinTheta
,
111 0, -sinTheta
, cosTheta
);
113 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
115 0, cosTheta
, sinTheta
,
116 0, -sinTheta
, cosTheta
);
117 matrix_
.preConcat(rot
);
121 void Transform::RotateAboutYAxis(double degrees
) {
122 double radians
= degrees
* M_PI
/ 180;
123 SkMScalar cosTheta
= SkDoubleToMScalar(std::cos(radians
));
124 SkMScalar sinTheta
= SkDoubleToMScalar(std::sin(radians
));
125 if (matrix_
.isIdentity()) {
126 // Note carefully the placement of the -sinTheta for rotation about
127 // y-axis is different than rotation about x-axis or z-axis.
128 matrix_
.set3x3(cosTheta
, 0, -sinTheta
,
130 sinTheta
, 0, cosTheta
);
132 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
133 rot
.set3x3(cosTheta
, 0, -sinTheta
,
135 sinTheta
, 0, cosTheta
);
136 matrix_
.preConcat(rot
);
140 void Transform::RotateAboutZAxis(double degrees
) {
141 double radians
= degrees
* M_PI
/ 180;
142 SkMScalar cosTheta
= SkDoubleToMScalar(std::cos(radians
));
143 SkMScalar sinTheta
= SkDoubleToMScalar(std::sin(radians
));
144 if (matrix_
.isIdentity()) {
145 matrix_
.set3x3(cosTheta
, sinTheta
, 0,
146 -sinTheta
, cosTheta
, 0,
149 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
150 rot
.set3x3(cosTheta
, sinTheta
, 0,
151 -sinTheta
, cosTheta
, 0,
153 matrix_
.preConcat(rot
);
157 void Transform::RotateAbout(const Vector3dF
& axis
, double degrees
) {
158 if (matrix_
.isIdentity()) {
159 matrix_
.setRotateDegreesAbout(SkFloatToMScalar(axis
.x()),
160 SkFloatToMScalar(axis
.y()),
161 SkFloatToMScalar(axis
.z()),
162 SkDoubleToMScalar(degrees
));
164 SkMatrix44
rot(SkMatrix44::kUninitialized_Constructor
);
165 rot
.setRotateDegreesAbout(SkFloatToMScalar(axis
.x()),
166 SkFloatToMScalar(axis
.y()),
167 SkFloatToMScalar(axis
.z()),
168 SkDoubleToMScalar(degrees
));
169 matrix_
.preConcat(rot
);
173 void Transform::Scale(SkMScalar x
, SkMScalar y
) { matrix_
.preScale(x
, y
, 1); }
175 void Transform::Scale3d(SkMScalar x
, SkMScalar y
, SkMScalar z
) {
176 matrix_
.preScale(x
, y
, z
);
179 void Transform::Translate(SkMScalar x
, SkMScalar y
) {
180 matrix_
.preTranslate(x
, y
, 0);
183 void Transform::Translate3d(SkMScalar x
, SkMScalar y
, SkMScalar z
) {
184 matrix_
.preTranslate(x
, y
, z
);
187 void Transform::SkewX(double angle_x
) {
188 if (matrix_
.isIdentity()) {
189 matrix_
.set(0, 1, TanDegrees(angle_x
));
191 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
192 skew
.set(0, 1, TanDegrees(angle_x
));
193 matrix_
.preConcat(skew
);
197 void Transform::SkewY(double angle_y
) {
198 if (matrix_
.isIdentity()) {
199 matrix_
.set(1, 0, TanDegrees(angle_y
));
201 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
202 skew
.set(1, 0, TanDegrees(angle_y
));
203 matrix_
.preConcat(skew
);
207 void Transform::ApplyPerspectiveDepth(SkMScalar depth
) {
210 if (matrix_
.isIdentity()) {
211 matrix_
.set(3, 2, -SK_MScalar1
/ depth
);
213 SkMatrix44
m(SkMatrix44::kIdentity_Constructor
);
214 m
.set(3, 2, -SK_MScalar1
/ depth
);
215 matrix_
.preConcat(m
);
219 void Transform::PreconcatTransform(const Transform
& transform
) {
220 matrix_
.preConcat(transform
.matrix_
);
223 void Transform::ConcatTransform(const Transform
& transform
) {
224 matrix_
.postConcat(transform
.matrix_
);
227 bool Transform::IsApproximatelyIdentityOrTranslation(
228 SkMScalar tolerance
) const {
229 DCHECK_GE(tolerance
, 0);
231 ApproximatelyOne(matrix_
.get(0, 0), tolerance
) &&
232 ApproximatelyZero(matrix_
.get(1, 0), tolerance
) &&
233 ApproximatelyZero(matrix_
.get(2, 0), tolerance
) &&
234 matrix_
.get(3, 0) == 0 &&
235 ApproximatelyZero(matrix_
.get(0, 1), tolerance
) &&
236 ApproximatelyOne(matrix_
.get(1, 1), tolerance
) &&
237 ApproximatelyZero(matrix_
.get(2, 1), tolerance
) &&
238 matrix_
.get(3, 1) == 0 &&
239 ApproximatelyZero(matrix_
.get(0, 2), tolerance
) &&
240 ApproximatelyZero(matrix_
.get(1, 2), tolerance
) &&
241 ApproximatelyOne(matrix_
.get(2, 2), tolerance
) &&
242 matrix_
.get(3, 2) == 0 &&
243 matrix_
.get(3, 3) == 1;
246 bool Transform::IsIdentityOrIntegerTranslation() const {
247 if (!IsIdentityOrTranslation())
250 bool no_fractional_translation
=
251 static_cast<int>(matrix_
.get(0, 3)) == matrix_
.get(0, 3) &&
252 static_cast<int>(matrix_
.get(1, 3)) == matrix_
.get(1, 3) &&
253 static_cast<int>(matrix_
.get(2, 3)) == matrix_
.get(2, 3);
255 return no_fractional_translation
;
258 bool Transform::IsBackFaceVisible() const {
259 // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
260 // would have its back face visible after applying the transform.
261 if (matrix_
.isIdentity())
264 // This is done by transforming the normal and seeing if the resulting z
265 // value is positive or negative. However, note that transforming a normal
266 // actually requires using the inverse-transpose of the original transform.
268 // We can avoid inverting and transposing the matrix since we know we want
269 // to transform only the specific normal vector (0, 0, 1, 0). In this case,
270 // we only need the 3rd row, 3rd column of the inverse-transpose. We can
271 // calculate only the 3rd row 3rd column element of the inverse, skipping
274 // For more information, refer to:
275 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
278 double determinant
= matrix_
.determinant();
280 // If matrix was not invertible, then just assume back face is not visible.
281 if (std::abs(determinant
) <= kEpsilon
)
284 // Compute the cofactor of the 3rd row, 3rd column.
285 double cofactor_part_1
=
286 matrix_
.get(0, 0) * matrix_
.get(1, 1) * matrix_
.get(3, 3);
288 double cofactor_part_2
=
289 matrix_
.get(0, 1) * matrix_
.get(1, 3) * matrix_
.get(3, 0);
291 double cofactor_part_3
=
292 matrix_
.get(0, 3) * matrix_
.get(1, 0) * matrix_
.get(3, 1);
294 double cofactor_part_4
=
295 matrix_
.get(0, 0) * matrix_
.get(1, 3) * matrix_
.get(3, 1);
297 double cofactor_part_5
=
298 matrix_
.get(0, 1) * matrix_
.get(1, 0) * matrix_
.get(3, 3);
300 double cofactor_part_6
=
301 matrix_
.get(0, 3) * matrix_
.get(1, 1) * matrix_
.get(3, 0);
311 // Technically the transformed z component is cofactor33 / determinant. But
312 // we can avoid the costly division because we only care about the resulting
313 // +/- sign; we can check this equivalently by multiplication.
314 return cofactor33
* determinant
< 0;
317 bool Transform::GetInverse(Transform
* transform
) const {
318 if (!matrix_
.invert(&transform
->matrix_
)) {
319 // Initialize the return value to identity if this matrix turned
320 // out to be un-invertible.
321 transform
->MakeIdentity();
328 bool Transform::Preserves2dAxisAlignment() const {
329 // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
330 // after being transformed by this matrix (and implicitly projected by
331 // dropping any non-zero z-values).
333 // The 4th column can be ignored because translations don't affect axis
334 // alignment. The 3rd column can be ignored because we are assuming 2d
335 // inputs, where z-values will be zero. The 3rd row can also be ignored
336 // because we are assuming 2d outputs, and any resulting z-value is dropped
337 // anyway. For the inner 2x2 portion, the only effects that keep a rect axis
338 // aligned are (1) swapping axes and (2) scaling axes. This can be checked by
339 // verifying only 1 element of every column and row is non-zero. Degenerate
340 // cases that project the x or y dimension to zero are considered to preserve
343 // If the matrix does have perspective component that is affected by x or y
344 // values: The current implementation conservatively assumes that axis
345 // alignment is not preserved.
347 bool has_x_or_y_perspective
=
348 matrix_
.get(3, 0) != 0 || matrix_
.get(3, 1) != 0;
350 int num_non_zero_in_row_0
= 0;
351 int num_non_zero_in_row_1
= 0;
352 int num_non_zero_in_col_0
= 0;
353 int num_non_zero_in_col_1
= 0;
355 if (std::abs(matrix_
.get(0, 0)) > kEpsilon
) {
356 num_non_zero_in_row_0
++;
357 num_non_zero_in_col_0
++;
360 if (std::abs(matrix_
.get(0, 1)) > kEpsilon
) {
361 num_non_zero_in_row_0
++;
362 num_non_zero_in_col_1
++;
365 if (std::abs(matrix_
.get(1, 0)) > kEpsilon
) {
366 num_non_zero_in_row_1
++;
367 num_non_zero_in_col_0
++;
370 if (std::abs(matrix_
.get(1, 1)) > kEpsilon
) {
371 num_non_zero_in_row_1
++;
372 num_non_zero_in_col_1
++;
376 num_non_zero_in_row_0
<= 1 &&
377 num_non_zero_in_row_1
<= 1 &&
378 num_non_zero_in_col_0
<= 1 &&
379 num_non_zero_in_col_1
<= 1 &&
380 !has_x_or_y_perspective
;
383 void Transform::Transpose() {
387 void Transform::FlattenTo2d() {
388 matrix_
.set(2, 0, 0.0);
389 matrix_
.set(2, 1, 0.0);
390 matrix_
.set(0, 2, 0.0);
391 matrix_
.set(1, 2, 0.0);
392 matrix_
.set(2, 2, 1.0);
393 matrix_
.set(3, 2, 0.0);
394 matrix_
.set(2, 3, 0.0);
397 bool Transform::IsFlat() const {
398 return matrix_
.get(2, 0) == 0.0 && matrix_
.get(2, 1) == 0.0 &&
399 matrix_
.get(0, 2) == 0.0 && matrix_
.get(1, 2) == 0.0 &&
400 matrix_
.get(2, 2) == 1.0 && matrix_
.get(3, 2) == 0.0 &&
401 matrix_
.get(2, 3) == 0.0;
404 Vector2dF
Transform::To2dTranslation() const {
405 return gfx::Vector2dF(SkMScalarToFloat(matrix_
.get(0, 3)),
406 SkMScalarToFloat(matrix_
.get(1, 3)));
409 void Transform::TransformPoint(Point
* point
) const {
411 TransformPointInternal(matrix_
, point
);
414 void Transform::TransformPoint(Point3F
* point
) const {
416 TransformPointInternal(matrix_
, point
);
419 bool Transform::TransformPointReverse(Point
* point
) const {
422 // TODO(sad): Try to avoid trying to invert the matrix.
423 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
424 if (!matrix_
.invert(&inverse
))
427 TransformPointInternal(inverse
, point
);
431 bool Transform::TransformPointReverse(Point3F
* point
) const {
434 // TODO(sad): Try to avoid trying to invert the matrix.
435 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
436 if (!matrix_
.invert(&inverse
))
439 TransformPointInternal(inverse
, point
);
443 void Transform::TransformRect(RectF
* rect
) const {
444 if (matrix_
.isIdentity())
447 SkRect src
= RectFToSkRect(*rect
);
448 const SkMatrix
& matrix
= matrix_
;
449 matrix
.mapRect(&src
);
450 *rect
= SkRectToRectF(src
);
453 bool Transform::TransformRectReverse(RectF
* rect
) const {
454 if (matrix_
.isIdentity())
457 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
458 if (!matrix_
.invert(&inverse
))
461 const SkMatrix
& matrix
= inverse
;
462 SkRect src
= RectFToSkRect(*rect
);
463 matrix
.mapRect(&src
);
464 *rect
= SkRectToRectF(src
);
468 void Transform::TransformBox(BoxF
* box
) const {
470 bool first_point
= true;
471 for (int corner
= 0; corner
< 8; ++corner
) {
472 gfx::Point3F point
= box
->origin();
473 point
+= gfx::Vector3dF(corner
& 1 ? box
->width() : 0.f
,
474 corner
& 2 ? box
->height() : 0.f
,
475 corner
& 4 ? box
->depth() : 0.f
);
476 TransformPoint(&point
);
478 bounds
.set_origin(point
);
481 bounds
.ExpandTo(point
);
487 bool Transform::TransformBoxReverse(BoxF
* box
) const {
488 gfx::Transform inverse
= *this;
489 if (!GetInverse(&inverse
))
491 inverse
.TransformBox(box
);
495 bool Transform::Blend(const Transform
& from
, double progress
) {
496 DecomposedTransform to_decomp
;
497 DecomposedTransform from_decomp
;
498 if (!DecomposeTransform(&to_decomp
, *this) ||
499 !DecomposeTransform(&from_decomp
, from
))
502 if (!BlendDecomposedTransforms(&to_decomp
, to_decomp
, from_decomp
, progress
))
505 matrix_
= ComposeTransform(to_decomp
).matrix();
509 void Transform::RoundTranslationComponents() {
510 matrix_
.set(0, 3, Round(matrix_
.get(0, 3)));
511 matrix_
.set(1, 3, Round(matrix_
.get(1, 3)));
514 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
515 Point3F
* point
) const {
516 if (xform
.isIdentity())
519 SkMScalar p
[4] = {SkFloatToMScalar(point
->x()), SkFloatToMScalar(point
->y()),
520 SkFloatToMScalar(point
->z()), 1};
522 xform
.mapMScalars(p
);
524 if (p
[3] != SK_MScalar1
&& p
[3] != 0.f
) {
525 float w_inverse
= SK_MScalar1
/ p
[3];
526 point
->SetPoint(p
[0] * w_inverse
, p
[1] * w_inverse
, p
[2] * w_inverse
);
528 point
->SetPoint(p
[0], p
[1], p
[2]);
532 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
533 Point
* point
) const {
534 if (xform
.isIdentity())
537 SkMScalar p
[4] = {SkIntToMScalar(point
->x()), SkIntToMScalar(point
->y()),
540 xform
.mapMScalars(p
);
542 point
->SetPoint(ToRoundedInt(p
[0]), ToRoundedInt(p
[1]));
545 std::string
Transform::ToString() const {
546 return base::StringPrintf(
547 "[ %+0.4f %+0.4f %+0.4f %+0.4f \n"
548 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
549 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
550 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",