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/box_f.h"
15 #include "ui/gfx/geometry/vector3d_f.h"
16 #include "ui/gfx/point.h"
17 #include "ui/gfx/point3_f.h"
18 #include "ui/gfx/rect.h"
19 #include "ui/gfx/safe_integer_conversions.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",