• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
7 
8 #include "ui/gfx/transform.h"
9 
10 #include <cmath>
11 
12 #include "base/logging.h"
13 #include "base/strings/stringprintf.h"
14 #include "ui/gfx/box_f.h"
15 #include "ui/gfx/point.h"
16 #include "ui/gfx/point3_f.h"
17 #include "ui/gfx/rect.h"
18 #include "ui/gfx/safe_integer_conversions.h"
19 #include "ui/gfx/skia_util.h"
20 #include "ui/gfx/transform_util.h"
21 #include "ui/gfx/vector3d_f.h"
22 
23 namespace gfx {
24 
25 namespace {
26 
27 // Taken from SkMatrix44.
28 const SkMScalar kEpsilon = 1e-8f;
29 
TanDegrees(double degrees)30 SkMScalar TanDegrees(double degrees) {
31   double radians = degrees * M_PI / 180;
32   return SkDoubleToMScalar(std::tan(radians));
33 }
34 
ApproximatelyZero(SkMScalar x,SkMScalar tolerance)35 inline bool ApproximatelyZero(SkMScalar x, SkMScalar tolerance) {
36   return std::abs(x) <= tolerance;
37 }
38 
ApproximatelyOne(SkMScalar x,SkMScalar tolerance)39 inline bool ApproximatelyOne(SkMScalar x, SkMScalar tolerance) {
40   return std::abs(x - SkDoubleToMScalar(1.0)) <= tolerance;
41 }
42 
43 }  // namespace
44 
Transform(SkMScalar col1row1,SkMScalar col2row1,SkMScalar col3row1,SkMScalar col4row1,SkMScalar col1row2,SkMScalar col2row2,SkMScalar col3row2,SkMScalar col4row2,SkMScalar col1row3,SkMScalar col2row3,SkMScalar col3row3,SkMScalar col4row3,SkMScalar col1row4,SkMScalar col2row4,SkMScalar col3row4,SkMScalar col4row4)45 Transform::Transform(SkMScalar col1row1,
46                      SkMScalar col2row1,
47                      SkMScalar col3row1,
48                      SkMScalar col4row1,
49                      SkMScalar col1row2,
50                      SkMScalar col2row2,
51                      SkMScalar col3row2,
52                      SkMScalar col4row2,
53                      SkMScalar col1row3,
54                      SkMScalar col2row3,
55                      SkMScalar col3row3,
56                      SkMScalar col4row3,
57                      SkMScalar col1row4,
58                      SkMScalar col2row4,
59                      SkMScalar col3row4,
60                      SkMScalar col4row4)
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);
66 
67   matrix_.set(0, 1, col2row1);
68   matrix_.set(1, 1, col2row2);
69   matrix_.set(2, 1, col2row3);
70   matrix_.set(3, 1, col2row4);
71 
72   matrix_.set(0, 2, col3row1);
73   matrix_.set(1, 2, col3row2);
74   matrix_.set(2, 2, col3row3);
75   matrix_.set(3, 2, col3row4);
76 
77   matrix_.set(0, 3, col4row1);
78   matrix_.set(1, 3, col4row2);
79   matrix_.set(2, 3, col4row3);
80   matrix_.set(3, 3, col4row4);
81 }
82 
Transform(SkMScalar col1row1,SkMScalar col2row1,SkMScalar col1row2,SkMScalar col2row2,SkMScalar x_translation,SkMScalar y_translation)83 Transform::Transform(SkMScalar col1row1,
84                      SkMScalar col2row1,
85                      SkMScalar col1row2,
86                      SkMScalar col2row2,
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);
96 }
97 
RotateAboutXAxis(double degrees)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);
106   } else {
107     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
108     rot.set3x3(1, 0, 0,
109                0, cosTheta, sinTheta,
110                0, -sinTheta, cosTheta);
111     matrix_.preConcat(rot);
112   }
113 }
114 
RotateAboutYAxis(double degrees)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,
123                      0, 1, 0,
124                      sinTheta, 0, cosTheta);
125   } else {
126     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
127     rot.set3x3(cosTheta, 0, -sinTheta,
128                0, 1, 0,
129                sinTheta, 0, cosTheta);
130     matrix_.preConcat(rot);
131   }
132 }
133 
RotateAboutZAxis(double degrees)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,
141                      0, 0, 1);
142   } else {
143     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
144     rot.set3x3(cosTheta, sinTheta, 0,
145                -sinTheta, cosTheta, 0,
146                0, 0, 1);
147     matrix_.preConcat(rot);
148   }
149 }
150 
RotateAbout(const Vector3dF & axis,double degrees)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                                   degrees);
157   } else {
158     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
159     rot.setRotateDegreesAbout(SkFloatToMScalar(axis.x()),
160                               SkFloatToMScalar(axis.y()),
161                               SkFloatToMScalar(axis.z()),
162                               degrees);
163     matrix_.preConcat(rot);
164   }
165 }
166 
Scale(SkMScalar x,SkMScalar y)167 void Transform::Scale(SkMScalar x, SkMScalar y) { matrix_.preScale(x, y, 1); }
168 
Scale3d(SkMScalar x,SkMScalar y,SkMScalar z)169 void Transform::Scale3d(SkMScalar x, SkMScalar y, SkMScalar z) {
170   matrix_.preScale(x, y, z);
171 }
172 
Translate(SkMScalar x,SkMScalar y)173 void Transform::Translate(SkMScalar x, SkMScalar y) {
174   matrix_.preTranslate(x, y, 0);
175 }
176 
Translate3d(SkMScalar x,SkMScalar y,SkMScalar z)177 void Transform::Translate3d(SkMScalar x, SkMScalar y, SkMScalar z) {
178   matrix_.preTranslate(x, y, z);
179 }
180 
SkewX(double angle_x)181 void Transform::SkewX(double angle_x) {
182   if (matrix_.isIdentity())
183     matrix_.set(0, 1, TanDegrees(angle_x));
184   else {
185     SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
186     skew.set(0, 1, TanDegrees(angle_x));
187     matrix_.preConcat(skew);
188   }
189 }
190 
SkewY(double angle_y)191 void Transform::SkewY(double angle_y) {
192   if (matrix_.isIdentity())
193     matrix_.set(1, 0, TanDegrees(angle_y));
194   else {
195     SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
196     skew.set(1, 0, TanDegrees(angle_y));
197     matrix_.preConcat(skew);
198   }
199 }
200 
ApplyPerspectiveDepth(SkMScalar depth)201 void Transform::ApplyPerspectiveDepth(SkMScalar depth) {
202   if (depth == 0)
203     return;
204   if (matrix_.isIdentity())
205     matrix_.set(3, 2, -1.0 / depth);
206   else {
207     SkMatrix44 m(SkMatrix44::kIdentity_Constructor);
208     m.set(3, 2, -1.0 / depth);
209     matrix_.preConcat(m);
210   }
211 }
212 
PreconcatTransform(const Transform & transform)213 void Transform::PreconcatTransform(const Transform& transform) {
214   matrix_.preConcat(transform.matrix_);
215 }
216 
ConcatTransform(const Transform & transform)217 void Transform::ConcatTransform(const Transform& transform) {
218   matrix_.postConcat(transform.matrix_);
219 }
220 
IsApproximatelyIdentityOrTranslation(SkMScalar tolerance) const221 bool Transform::IsApproximatelyIdentityOrTranslation(
222     SkMScalar tolerance) const {
223   DCHECK_GE(tolerance, 0);
224   return
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;
238 }
239 
IsIdentityOrIntegerTranslation() const240 bool Transform::IsIdentityOrIntegerTranslation() const {
241   if (!IsIdentityOrTranslation())
242     return false;
243 
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);
248 
249   return no_fractional_translation;
250 }
251 
IsBackFaceVisible() const252 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())
256     return false;
257 
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.
261   //
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
266   // everything else.
267   //
268   // For more information, refer to:
269   //   http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
270   //
271 
272   double determinant = matrix_.determinant();
273 
274   // If matrix was not invertible, then just assume back face is not visible.
275   if (std::abs(determinant) <= kEpsilon)
276     return false;
277 
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);
281 
282   double cofactor_part_2 =
283       matrix_.get(0, 1) * matrix_.get(1, 3) * matrix_.get(3, 0);
284 
285   double cofactor_part_3 =
286       matrix_.get(0, 3) * matrix_.get(1, 0) * matrix_.get(3, 1);
287 
288   double cofactor_part_4 =
289       matrix_.get(0, 0) * matrix_.get(1, 3) * matrix_.get(3, 1);
290 
291   double cofactor_part_5 =
292       matrix_.get(0, 1) * matrix_.get(1, 0) * matrix_.get(3, 3);
293 
294   double cofactor_part_6 =
295       matrix_.get(0, 3) * matrix_.get(1, 1) * matrix_.get(3, 0);
296 
297   double cofactor33 =
298       cofactor_part_1 +
299       cofactor_part_2 +
300       cofactor_part_3 -
301       cofactor_part_4 -
302       cofactor_part_5 -
303       cofactor_part_6;
304 
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;
309 }
310 
GetInverse(Transform * transform) const311 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();
316     return false;
317   }
318 
319   return true;
320 }
321 
Preserves2dAxisAlignment() const322 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).
326   //
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
335   // axis alignment.
336   //
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.
340 
341   bool has_x_or_y_perspective =
342       matrix_.get(3, 0) != 0 || matrix_.get(3, 1) != 0;
343 
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;
348 
349   if (std::abs(matrix_.get(0, 0)) > kEpsilon) {
350     num_non_zero_in_row_0++;
351     num_non_zero_in_col_0++;
352   }
353 
354   if (std::abs(matrix_.get(0, 1)) > kEpsilon) {
355     num_non_zero_in_row_0++;
356     num_non_zero_in_col_1++;
357   }
358 
359   if (std::abs(matrix_.get(1, 0)) > kEpsilon) {
360     num_non_zero_in_row_1++;
361     num_non_zero_in_col_0++;
362   }
363 
364   if (std::abs(matrix_.get(1, 1)) > kEpsilon) {
365     num_non_zero_in_row_1++;
366     num_non_zero_in_col_1++;
367   }
368 
369   return
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;
375 }
376 
Transpose()377 void Transform::Transpose() {
378   matrix_.transpose();
379 }
380 
FlattenTo2d()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);
389 }
390 
To2dTranslation() const391 Vector2dF Transform::To2dTranslation() const {
392   return gfx::Vector2dF(SkMScalarToFloat(matrix_.get(0, 3)),
393                         SkMScalarToFloat(matrix_.get(1, 3)));
394 }
395 
TransformPoint(Point * point) const396 void Transform::TransformPoint(Point* point) const {
397   DCHECK(point);
398   TransformPointInternal(matrix_, point);
399 }
400 
TransformPoint(Point3F * point) const401 void Transform::TransformPoint(Point3F* point) const {
402   DCHECK(point);
403   TransformPointInternal(matrix_, point);
404 }
405 
TransformPointReverse(Point * point) const406 bool Transform::TransformPointReverse(Point* point) const {
407   DCHECK(point);
408 
409   // TODO(sad): Try to avoid trying to invert the matrix.
410   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
411   if (!matrix_.invert(&inverse))
412     return false;
413 
414   TransformPointInternal(inverse, point);
415   return true;
416 }
417 
TransformPointReverse(Point3F * point) const418 bool Transform::TransformPointReverse(Point3F* point) const {
419   DCHECK(point);
420 
421   // TODO(sad): Try to avoid trying to invert the matrix.
422   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
423   if (!matrix_.invert(&inverse))
424     return false;
425 
426   TransformPointInternal(inverse, point);
427   return true;
428 }
429 
TransformRect(RectF * rect) const430 void Transform::TransformRect(RectF* rect) const {
431   if (matrix_.isIdentity())
432     return;
433 
434   SkRect src = RectFToSkRect(*rect);
435   const SkMatrix& matrix = matrix_;
436   matrix.mapRect(&src);
437   *rect = SkRectToRectF(src);
438 }
439 
TransformRectReverse(RectF * rect) const440 bool Transform::TransformRectReverse(RectF* rect) const {
441   if (matrix_.isIdentity())
442     return true;
443 
444   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
445   if (!matrix_.invert(&inverse))
446     return false;
447 
448   const SkMatrix& matrix = inverse;
449   SkRect src = RectFToSkRect(*rect);
450   matrix.mapRect(&src);
451   *rect = SkRectToRectF(src);
452   return true;
453 }
454 
TransformBox(BoxF * box) const455 void Transform::TransformBox(BoxF* box) const {
456   BoxF bounds;
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);
464     if (first_point) {
465       bounds.set_origin(point);
466       first_point = false;
467     } else {
468       bounds.ExpandTo(point);
469     }
470   }
471   *box = bounds;
472 }
473 
TransformBoxReverse(BoxF * box) const474 bool Transform::TransformBoxReverse(BoxF* box) const {
475   gfx::Transform inverse = *this;
476   if (!GetInverse(&inverse))
477     return false;
478   inverse.TransformBox(box);
479   return true;
480 }
481 
Blend(const Transform & from,double progress)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))
487     return false;
488 
489   if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
490     return false;
491 
492   matrix_ = ComposeTransform(to_decomp).matrix();
493   return true;
494 }
495 
TransformPointInternal(const SkMatrix44 & xform,Point3F * point) const496 void Transform::TransformPointInternal(const SkMatrix44& xform,
497                                        Point3F* point) const {
498   if (xform.isIdentity())
499     return;
500 
501   SkMScalar p[4] = {SkFloatToMScalar(point->x()), SkFloatToMScalar(point->y()),
502                     SkFloatToMScalar(point->z()), 1};
503 
504   xform.mapMScalars(p);
505 
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);
509   } else {
510     point->SetPoint(p[0], p[1], p[2]);
511   }
512 }
513 
TransformPointInternal(const SkMatrix44 & xform,Point * point) const514 void Transform::TransformPointInternal(const SkMatrix44& xform,
515                                        Point* point) const {
516   if (xform.isIdentity())
517     return;
518 
519   SkMScalar p[4] = {SkFloatToMScalar(point->x()), SkFloatToMScalar(point->y()),
520                     0, 1};
521 
522   xform.mapMScalars(p);
523 
524   point->SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1]));
525 }
526 
ToString() const527 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",
533       matrix_.get(0, 0),
534       matrix_.get(0, 1),
535       matrix_.get(0, 2),
536       matrix_.get(0, 3),
537       matrix_.get(1, 0),
538       matrix_.get(1, 1),
539       matrix_.get(1, 2),
540       matrix_.get(1, 3),
541       matrix_.get(2, 0),
542       matrix_.get(2, 1),
543       matrix_.get(2, 2),
544       matrix_.get(2, 3),
545       matrix_.get(3, 0),
546       matrix_.get(3, 1),
547       matrix_.get(3, 2),
548       matrix_.get(3, 3));
549 }
550 
551 }  // namespace gfx
552