Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / gfx / transform.cc
blob33946cba67f07a79f9330b3dd1c7eea009ce6d7d
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"
10 #include <cmath>
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"
23 namespace gfx {
25 namespace {
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) {
44 if (f == 0.f)
45 return f;
46 return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f);
49 } // namespace
51 Transform::Transform(SkMScalar col1row1,
52 SkMScalar col2row1,
53 SkMScalar col3row1,
54 SkMScalar col4row1,
55 SkMScalar col1row2,
56 SkMScalar col2row2,
57 SkMScalar col3row2,
58 SkMScalar col4row2,
59 SkMScalar col1row3,
60 SkMScalar col2row3,
61 SkMScalar col3row3,
62 SkMScalar col4row3,
63 SkMScalar col1row4,
64 SkMScalar col2row4,
65 SkMScalar col3row4,
66 SkMScalar col4row4)
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,
90 SkMScalar col2row1,
91 SkMScalar col1row2,
92 SkMScalar col2row2,
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);
112 } else {
113 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
114 rot.set3x3(1, 0, 0,
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,
129 0, 1, 0,
130 sinTheta, 0, cosTheta);
131 } else {
132 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
133 rot.set3x3(cosTheta, 0, -sinTheta,
134 0, 1, 0,
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,
147 0, 0, 1);
148 } else {
149 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
150 rot.set3x3(cosTheta, sinTheta, 0,
151 -sinTheta, cosTheta, 0,
152 0, 0, 1);
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));
163 } else {
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));
190 } else {
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));
200 } else {
201 SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
202 skew.set(1, 0, TanDegrees(angle_y));
203 matrix_.preConcat(skew);
207 void Transform::ApplyPerspectiveDepth(SkMScalar depth) {
208 if (depth == 0)
209 return;
210 if (matrix_.isIdentity()) {
211 matrix_.set(3, 2, -SK_MScalar1 / depth);
212 } else {
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);
230 return
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())
248 return false;
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())
262 return false;
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
272 // everything else.
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)
282 return false;
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);
303 double cofactor33 =
304 cofactor_part_1 +
305 cofactor_part_2 +
306 cofactor_part_3 -
307 cofactor_part_4 -
308 cofactor_part_5 -
309 cofactor_part_6;
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();
322 return false;
325 return true;
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
341 // axis alignment.
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++;
375 return
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() {
384 matrix_.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 {
410 DCHECK(point);
411 TransformPointInternal(matrix_, point);
414 void Transform::TransformPoint(Point3F* point) const {
415 DCHECK(point);
416 TransformPointInternal(matrix_, point);
419 bool Transform::TransformPointReverse(Point* point) const {
420 DCHECK(point);
422 // TODO(sad): Try to avoid trying to invert the matrix.
423 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
424 if (!matrix_.invert(&inverse))
425 return false;
427 TransformPointInternal(inverse, point);
428 return true;
431 bool Transform::TransformPointReverse(Point3F* point) const {
432 DCHECK(point);
434 // TODO(sad): Try to avoid trying to invert the matrix.
435 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
436 if (!matrix_.invert(&inverse))
437 return false;
439 TransformPointInternal(inverse, point);
440 return true;
443 void Transform::TransformRect(RectF* rect) const {
444 if (matrix_.isIdentity())
445 return;
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())
455 return true;
457 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
458 if (!matrix_.invert(&inverse))
459 return false;
461 const SkMatrix& matrix = inverse;
462 SkRect src = RectFToSkRect(*rect);
463 matrix.mapRect(&src);
464 *rect = SkRectToRectF(src);
465 return true;
468 void Transform::TransformBox(BoxF* box) const {
469 BoxF bounds;
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);
477 if (first_point) {
478 bounds.set_origin(point);
479 first_point = false;
480 } else {
481 bounds.ExpandTo(point);
484 *box = bounds;
487 bool Transform::TransformBoxReverse(BoxF* box) const {
488 gfx::Transform inverse = *this;
489 if (!GetInverse(&inverse))
490 return false;
491 inverse.TransformBox(box);
492 return true;
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))
500 return false;
502 if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
503 return false;
505 matrix_ = ComposeTransform(to_decomp).matrix();
506 return true;
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())
517 return;
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);
527 } else {
528 point->SetPoint(p[0], p[1], p[2]);
532 void Transform::TransformPointInternal(const SkMatrix44& xform,
533 Point* point) const {
534 if (xform.isIdentity())
535 return;
537 SkMScalar p[4] = {SkIntToMScalar(point->x()), SkIntToMScalar(point->y()),
538 0, 1};
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",
551 matrix_.get(0, 0),
552 matrix_.get(0, 1),
553 matrix_.get(0, 2),
554 matrix_.get(0, 3),
555 matrix_.get(1, 0),
556 matrix_.get(1, 1),
557 matrix_.get(1, 2),
558 matrix_.get(1, 3),
559 matrix_.get(2, 0),
560 matrix_.get(2, 1),
561 matrix_.get(2, 2),
562 matrix_.get(2, 3),
563 matrix_.get(3, 0),
564 matrix_.get(3, 1),
565 matrix_.get(3, 2),
566 matrix_.get(3, 3));
569 } // namespace gfx