Added "controlled by" indicators for mouselock and ppapi broker.
[chromium-blink-merge.git] / ui / gfx / transform.cc
blob85d9c96f268306015447545649d93be0f933eb7a
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 Vector2dF Transform::To2dTranslation() const {
398 return gfx::Vector2dF(SkMScalarToFloat(matrix_.get(0, 3)),
399 SkMScalarToFloat(matrix_.get(1, 3)));
402 void Transform::TransformPoint(Point* point) const {
403 DCHECK(point);
404 TransformPointInternal(matrix_, point);
407 void Transform::TransformPoint(Point3F* point) const {
408 DCHECK(point);
409 TransformPointInternal(matrix_, point);
412 bool Transform::TransformPointReverse(Point* point) const {
413 DCHECK(point);
415 // TODO(sad): Try to avoid trying to invert the matrix.
416 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
417 if (!matrix_.invert(&inverse))
418 return false;
420 TransformPointInternal(inverse, point);
421 return true;
424 bool Transform::TransformPointReverse(Point3F* point) const {
425 DCHECK(point);
427 // TODO(sad): Try to avoid trying to invert the matrix.
428 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
429 if (!matrix_.invert(&inverse))
430 return false;
432 TransformPointInternal(inverse, point);
433 return true;
436 void Transform::TransformRect(RectF* rect) const {
437 if (matrix_.isIdentity())
438 return;
440 SkRect src = RectFToSkRect(*rect);
441 const SkMatrix& matrix = matrix_;
442 matrix.mapRect(&src);
443 *rect = SkRectToRectF(src);
446 bool Transform::TransformRectReverse(RectF* rect) const {
447 if (matrix_.isIdentity())
448 return true;
450 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
451 if (!matrix_.invert(&inverse))
452 return false;
454 const SkMatrix& matrix = inverse;
455 SkRect src = RectFToSkRect(*rect);
456 matrix.mapRect(&src);
457 *rect = SkRectToRectF(src);
458 return true;
461 void Transform::TransformBox(BoxF* box) const {
462 BoxF bounds;
463 bool first_point = true;
464 for (int corner = 0; corner < 8; ++corner) {
465 gfx::Point3F point = box->origin();
466 point += gfx::Vector3dF(corner & 1 ? box->width() : 0.f,
467 corner & 2 ? box->height() : 0.f,
468 corner & 4 ? box->depth() : 0.f);
469 TransformPoint(&point);
470 if (first_point) {
471 bounds.set_origin(point);
472 first_point = false;
473 } else {
474 bounds.ExpandTo(point);
477 *box = bounds;
480 bool Transform::TransformBoxReverse(BoxF* box) const {
481 gfx::Transform inverse = *this;
482 if (!GetInverse(&inverse))
483 return false;
484 inverse.TransformBox(box);
485 return true;
488 bool Transform::Blend(const Transform& from, double progress) {
489 DecomposedTransform to_decomp;
490 DecomposedTransform from_decomp;
491 if (!DecomposeTransform(&to_decomp, *this) ||
492 !DecomposeTransform(&from_decomp, from))
493 return false;
495 if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
496 return false;
498 matrix_ = ComposeTransform(to_decomp).matrix();
499 return true;
502 void Transform::RoundTranslationComponents() {
503 matrix_.set(0, 3, Round(matrix_.get(0, 3)));
504 matrix_.set(1, 3, Round(matrix_.get(1, 3)));
507 void Transform::TransformPointInternal(const SkMatrix44& xform,
508 Point3F* point) const {
509 if (xform.isIdentity())
510 return;
512 SkMScalar p[4] = {SkFloatToMScalar(point->x()), SkFloatToMScalar(point->y()),
513 SkFloatToMScalar(point->z()), 1};
515 xform.mapMScalars(p);
517 if (p[3] != SK_MScalar1 && p[3] != 0.f) {
518 float w_inverse = SK_MScalar1 / p[3];
519 point->SetPoint(p[0] * w_inverse, p[1] * w_inverse, p[2] * w_inverse);
520 } else {
521 point->SetPoint(p[0], p[1], p[2]);
525 void Transform::TransformPointInternal(const SkMatrix44& xform,
526 Point* point) const {
527 if (xform.isIdentity())
528 return;
530 SkMScalar p[4] = {SkIntToMScalar(point->x()), SkIntToMScalar(point->y()),
531 0, 1};
533 xform.mapMScalars(p);
535 point->SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1]));
538 std::string Transform::ToString() const {
539 return base::StringPrintf(
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"
543 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
544 matrix_.get(0, 0),
545 matrix_.get(0, 1),
546 matrix_.get(0, 2),
547 matrix_.get(0, 3),
548 matrix_.get(1, 0),
549 matrix_.get(1, 1),
550 matrix_.get(1, 2),
551 matrix_.get(1, 3),
552 matrix_.get(2, 0),
553 matrix_.get(2, 1),
554 matrix_.get(2, 2),
555 matrix_.get(2, 3),
556 matrix_.get(3, 0),
557 matrix_.get(3, 1),
558 matrix_.get(3, 2),
559 matrix_.get(3, 3));
562 } // namespace gfx