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::Skew(double angle_x
, double angle_y
) {
188 if (matrix_
.isIdentity()) {
189 matrix_
.set(0, 1, TanDegrees(angle_x
));
190 matrix_
.set(1, 0, TanDegrees(angle_y
));
192 SkMatrix44
skew(SkMatrix44::kIdentity_Constructor
);
193 skew
.set(0, 1, TanDegrees(angle_x
));
194 skew
.set(1, 0, TanDegrees(angle_y
));
195 matrix_
.preConcat(skew
);
199 void Transform::ApplyPerspectiveDepth(SkMScalar depth
) {
202 if (matrix_
.isIdentity()) {
203 matrix_
.set(3, 2, -SK_MScalar1
/ depth
);
205 SkMatrix44
m(SkMatrix44::kIdentity_Constructor
);
206 m
.set(3, 2, -SK_MScalar1
/ depth
);
207 matrix_
.preConcat(m
);
211 void Transform::PreconcatTransform(const Transform
& transform
) {
212 matrix_
.preConcat(transform
.matrix_
);
215 void Transform::ConcatTransform(const Transform
& transform
) {
216 matrix_
.postConcat(transform
.matrix_
);
219 bool Transform::IsApproximatelyIdentityOrTranslation(
220 SkMScalar tolerance
) const {
221 DCHECK_GE(tolerance
, 0);
223 ApproximatelyOne(matrix_
.get(0, 0), tolerance
) &&
224 ApproximatelyZero(matrix_
.get(1, 0), tolerance
) &&
225 ApproximatelyZero(matrix_
.get(2, 0), tolerance
) &&
226 matrix_
.get(3, 0) == 0 &&
227 ApproximatelyZero(matrix_
.get(0, 1), tolerance
) &&
228 ApproximatelyOne(matrix_
.get(1, 1), tolerance
) &&
229 ApproximatelyZero(matrix_
.get(2, 1), tolerance
) &&
230 matrix_
.get(3, 1) == 0 &&
231 ApproximatelyZero(matrix_
.get(0, 2), tolerance
) &&
232 ApproximatelyZero(matrix_
.get(1, 2), tolerance
) &&
233 ApproximatelyOne(matrix_
.get(2, 2), tolerance
) &&
234 matrix_
.get(3, 2) == 0 &&
235 matrix_
.get(3, 3) == 1;
238 bool Transform::IsIdentityOrIntegerTranslation() const {
239 if (!IsIdentityOrTranslation())
242 bool no_fractional_translation
=
243 static_cast<int>(matrix_
.get(0, 3)) == matrix_
.get(0, 3) &&
244 static_cast<int>(matrix_
.get(1, 3)) == matrix_
.get(1, 3) &&
245 static_cast<int>(matrix_
.get(2, 3)) == matrix_
.get(2, 3);
247 return no_fractional_translation
;
250 bool Transform::IsBackFaceVisible() const {
251 // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
252 // would have its back face visible after applying the transform.
253 if (matrix_
.isIdentity())
256 // This is done by transforming the normal and seeing if the resulting z
257 // value is positive or negative. However, note that transforming a normal
258 // actually requires using the inverse-transpose of the original transform.
260 // We can avoid inverting and transposing the matrix since we know we want
261 // to transform only the specific normal vector (0, 0, 1, 0). In this case,
262 // we only need the 3rd row, 3rd column of the inverse-transpose. We can
263 // calculate only the 3rd row 3rd column element of the inverse, skipping
266 // For more information, refer to:
267 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
270 double determinant
= matrix_
.determinant();
272 // If matrix was not invertible, then just assume back face is not visible.
273 if (std::abs(determinant
) <= kEpsilon
)
276 // Compute the cofactor of the 3rd row, 3rd column.
277 double cofactor_part_1
=
278 matrix_
.get(0, 0) * matrix_
.get(1, 1) * matrix_
.get(3, 3);
280 double cofactor_part_2
=
281 matrix_
.get(0, 1) * matrix_
.get(1, 3) * matrix_
.get(3, 0);
283 double cofactor_part_3
=
284 matrix_
.get(0, 3) * matrix_
.get(1, 0) * matrix_
.get(3, 1);
286 double cofactor_part_4
=
287 matrix_
.get(0, 0) * matrix_
.get(1, 3) * matrix_
.get(3, 1);
289 double cofactor_part_5
=
290 matrix_
.get(0, 1) * matrix_
.get(1, 0) * matrix_
.get(3, 3);
292 double cofactor_part_6
=
293 matrix_
.get(0, 3) * matrix_
.get(1, 1) * matrix_
.get(3, 0);
303 // Technically the transformed z component is cofactor33 / determinant. But
304 // we can avoid the costly division because we only care about the resulting
305 // +/- sign; we can check this equivalently by multiplication.
306 return cofactor33
* determinant
< 0;
309 bool Transform::GetInverse(Transform
* transform
) const {
310 if (!matrix_
.invert(&transform
->matrix_
)) {
311 // Initialize the return value to identity if this matrix turned
312 // out to be un-invertible.
313 transform
->MakeIdentity();
320 bool Transform::Preserves2dAxisAlignment() const {
321 // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
322 // after being transformed by this matrix (and implicitly projected by
323 // dropping any non-zero z-values).
325 // The 4th column can be ignored because translations don't affect axis
326 // alignment. The 3rd column can be ignored because we are assuming 2d
327 // inputs, where z-values will be zero. The 3rd row can also be ignored
328 // because we are assuming 2d outputs, and any resulting z-value is dropped
329 // anyway. For the inner 2x2 portion, the only effects that keep a rect axis
330 // aligned are (1) swapping axes and (2) scaling axes. This can be checked by
331 // verifying only 1 element of every column and row is non-zero. Degenerate
332 // cases that project the x or y dimension to zero are considered to preserve
335 // If the matrix does have perspective component that is affected by x or y
336 // values: The current implementation conservatively assumes that axis
337 // alignment is not preserved.
339 bool has_x_or_y_perspective
=
340 matrix_
.get(3, 0) != 0 || matrix_
.get(3, 1) != 0;
342 int num_non_zero_in_row_0
= 0;
343 int num_non_zero_in_row_1
= 0;
344 int num_non_zero_in_col_0
= 0;
345 int num_non_zero_in_col_1
= 0;
347 if (std::abs(matrix_
.get(0, 0)) > kEpsilon
) {
348 num_non_zero_in_row_0
++;
349 num_non_zero_in_col_0
++;
352 if (std::abs(matrix_
.get(0, 1)) > kEpsilon
) {
353 num_non_zero_in_row_0
++;
354 num_non_zero_in_col_1
++;
357 if (std::abs(matrix_
.get(1, 0)) > kEpsilon
) {
358 num_non_zero_in_row_1
++;
359 num_non_zero_in_col_0
++;
362 if (std::abs(matrix_
.get(1, 1)) > kEpsilon
) {
363 num_non_zero_in_row_1
++;
364 num_non_zero_in_col_1
++;
368 num_non_zero_in_row_0
<= 1 &&
369 num_non_zero_in_row_1
<= 1 &&
370 num_non_zero_in_col_0
<= 1 &&
371 num_non_zero_in_col_1
<= 1 &&
372 !has_x_or_y_perspective
;
375 void Transform::Transpose() {
379 void Transform::FlattenTo2d() {
380 matrix_
.set(2, 0, 0.0);
381 matrix_
.set(2, 1, 0.0);
382 matrix_
.set(0, 2, 0.0);
383 matrix_
.set(1, 2, 0.0);
384 matrix_
.set(2, 2, 1.0);
385 matrix_
.set(3, 2, 0.0);
386 matrix_
.set(2, 3, 0.0);
389 bool Transform::IsFlat() const {
390 return matrix_
.get(2, 0) == 0.0 && matrix_
.get(2, 1) == 0.0 &&
391 matrix_
.get(0, 2) == 0.0 && matrix_
.get(1, 2) == 0.0 &&
392 matrix_
.get(2, 2) == 1.0 && matrix_
.get(3, 2) == 0.0 &&
393 matrix_
.get(2, 3) == 0.0;
396 Vector2dF
Transform::To2dTranslation() const {
397 return gfx::Vector2dF(SkMScalarToFloat(matrix_
.get(0, 3)),
398 SkMScalarToFloat(matrix_
.get(1, 3)));
401 void Transform::TransformPoint(Point
* point
) const {
403 TransformPointInternal(matrix_
, point
);
406 void Transform::TransformPoint(Point3F
* point
) const {
408 TransformPointInternal(matrix_
, point
);
411 bool Transform::TransformPointReverse(Point
* point
) const {
414 // TODO(sad): Try to avoid trying to invert the matrix.
415 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
416 if (!matrix_
.invert(&inverse
))
419 TransformPointInternal(inverse
, point
);
423 bool Transform::TransformPointReverse(Point3F
* point
) const {
426 // TODO(sad): Try to avoid trying to invert the matrix.
427 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
428 if (!matrix_
.invert(&inverse
))
431 TransformPointInternal(inverse
, point
);
435 void Transform::TransformRect(RectF
* rect
) const {
436 if (matrix_
.isIdentity())
439 SkRect src
= RectFToSkRect(*rect
);
440 const SkMatrix
& matrix
= matrix_
;
441 matrix
.mapRect(&src
);
442 *rect
= SkRectToRectF(src
);
445 bool Transform::TransformRectReverse(RectF
* rect
) const {
446 if (matrix_
.isIdentity())
449 SkMatrix44
inverse(SkMatrix44::kUninitialized_Constructor
);
450 if (!matrix_
.invert(&inverse
))
453 const SkMatrix
& matrix
= inverse
;
454 SkRect src
= RectFToSkRect(*rect
);
455 matrix
.mapRect(&src
);
456 *rect
= SkRectToRectF(src
);
460 void Transform::TransformBox(BoxF
* box
) const {
462 bool first_point
= true;
463 for (int corner
= 0; corner
< 8; ++corner
) {
464 gfx::Point3F point
= box
->origin();
465 point
+= gfx::Vector3dF(corner
& 1 ? box
->width() : 0.f
,
466 corner
& 2 ? box
->height() : 0.f
,
467 corner
& 4 ? box
->depth() : 0.f
);
468 TransformPoint(&point
);
470 bounds
.set_origin(point
);
473 bounds
.ExpandTo(point
);
479 bool Transform::TransformBoxReverse(BoxF
* box
) const {
480 gfx::Transform inverse
= *this;
481 if (!GetInverse(&inverse
))
483 inverse
.TransformBox(box
);
487 bool Transform::Blend(const Transform
& from
, double progress
) {
488 DecomposedTransform to_decomp
;
489 DecomposedTransform from_decomp
;
490 if (!DecomposeTransform(&to_decomp
, *this) ||
491 !DecomposeTransform(&from_decomp
, from
))
494 if (!BlendDecomposedTransforms(&to_decomp
, to_decomp
, from_decomp
, progress
))
497 matrix_
= ComposeTransform(to_decomp
).matrix();
501 void Transform::RoundTranslationComponents() {
502 matrix_
.set(0, 3, Round(matrix_
.get(0, 3)));
503 matrix_
.set(1, 3, Round(matrix_
.get(1, 3)));
506 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
507 Point3F
* point
) const {
508 if (xform
.isIdentity())
511 SkMScalar p
[4] = {SkFloatToMScalar(point
->x()), SkFloatToMScalar(point
->y()),
512 SkFloatToMScalar(point
->z()), 1};
514 xform
.mapMScalars(p
);
516 if (p
[3] != SK_MScalar1
&& p
[3] != 0.f
) {
517 float w_inverse
= SK_MScalar1
/ p
[3];
518 point
->SetPoint(p
[0] * w_inverse
, p
[1] * w_inverse
, p
[2] * w_inverse
);
520 point
->SetPoint(p
[0], p
[1], p
[2]);
524 void Transform::TransformPointInternal(const SkMatrix44
& xform
,
525 Point
* point
) const {
526 if (xform
.isIdentity())
529 SkMScalar p
[4] = {SkIntToMScalar(point
->x()), SkIntToMScalar(point
->y()),
532 xform
.mapMScalars(p
);
534 point
->SetPoint(ToRoundedInt(p
[0]), ToRoundedInt(p
[1]));
537 std::string
Transform::ToString() const {
538 return base::StringPrintf(
539 "[ %+0.4f %+0.4f %+0.4f %+0.4f \n"
540 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
541 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
542 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",