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 #include "ui/gfx/transform.h"
6 
7 #include "base/check_op.h"
8 #include "base/strings/stringprintf.h"
9 #include "ui/gfx/geometry/angle_conversions.h"
10 #include "ui/gfx/geometry/box_f.h"
11 #include "ui/gfx/geometry/point3_f.h"
12 #include "ui/gfx/geometry/point_conversions.h"
13 #include "ui/gfx/geometry/quaternion.h"
14 #include "ui/gfx/geometry/rect.h"
15 #include "ui/gfx/geometry/vector3d_f.h"
16 #include "ui/gfx/rrect_f.h"
17 #include "ui/gfx/skia_util.h"
18 #include "ui/gfx/transform_util.h"
19 
20 namespace gfx {
21 
22 namespace {
23 
24 const SkScalar kEpsilon = std::numeric_limits<float>::epsilon();
25 
TanDegrees(double degrees)26 SkScalar TanDegrees(double degrees) {
27   return SkDoubleToScalar(std::tan(gfx::DegToRad(degrees)));
28 }
29 
ApproximatelyZero(SkScalar x,SkScalar tolerance)30 inline bool ApproximatelyZero(SkScalar x, SkScalar tolerance) {
31   return std::abs(x) <= tolerance;
32 }
33 
ApproximatelyOne(SkScalar x,SkScalar tolerance)34 inline bool ApproximatelyOne(SkScalar x, SkScalar tolerance) {
35   return std::abs(x - 1) <= tolerance;
36 }
37 
38 }  // namespace
39 
Transform(SkScalar col1row1,SkScalar col2row1,SkScalar col3row1,SkScalar col4row1,SkScalar col1row2,SkScalar col2row2,SkScalar col3row2,SkScalar col4row2,SkScalar col1row3,SkScalar col2row3,SkScalar col3row3,SkScalar col4row3,SkScalar col1row4,SkScalar col2row4,SkScalar col3row4,SkScalar col4row4)40 Transform::Transform(SkScalar col1row1,
41                      SkScalar col2row1,
42                      SkScalar col3row1,
43                      SkScalar col4row1,
44                      SkScalar col1row2,
45                      SkScalar col2row2,
46                      SkScalar col3row2,
47                      SkScalar col4row2,
48                      SkScalar col1row3,
49                      SkScalar col2row3,
50                      SkScalar col3row3,
51                      SkScalar col4row3,
52                      SkScalar col1row4,
53                      SkScalar col2row4,
54                      SkScalar col3row4,
55                      SkScalar col4row4)
56     : matrix_(SkMatrix44::kUninitialized_Constructor) {
57   matrix_.set4x4(col1row1, col1row2, col1row3, col1row4, col2row1, col2row2,
58                  col2row3, col2row4, col3row1, col3row2, col3row3, col3row4,
59                  col4row1, col4row2, col4row3, col4row4);
60 }
61 
Transform(SkScalar col1row1,SkScalar col2row1,SkScalar col1row2,SkScalar col2row2,SkScalar x_translation,SkScalar y_translation)62 Transform::Transform(SkScalar col1row1,
63                      SkScalar col2row1,
64                      SkScalar col1row2,
65                      SkScalar col2row2,
66                      SkScalar x_translation,
67                      SkScalar y_translation)
68     : matrix_(SkMatrix44::kIdentity_Constructor) {
69   matrix_.set(0, 0, col1row1);
70   matrix_.set(1, 0, col1row2);
71   matrix_.set(0, 1, col2row1);
72   matrix_.set(1, 1, col2row2);
73   matrix_.set(0, 3, x_translation);
74   matrix_.set(1, 3, y_translation);
75 }
76 
Transform(const Quaternion & q)77 Transform::Transform(const Quaternion& q)
78     : matrix_(SkMatrix44::kUninitialized_Constructor) {
79   double x = q.x();
80   double y = q.y();
81   double z = q.z();
82   double w = q.w();
83 
84   // Implicitly calls matrix.setIdentity()
85   matrix_.set3x3(SkDoubleToScalar(1.0 - 2.0 * (y * y + z * z)),
86                  SkDoubleToScalar(2.0 * (x * y + z * w)),
87                  SkDoubleToScalar(2.0 * (x * z - y * w)),
88                  SkDoubleToScalar(2.0 * (x * y - z * w)),
89                  SkDoubleToScalar(1.0 - 2.0 * (x * x + z * z)),
90                  SkDoubleToScalar(2.0 * (y * z + x * w)),
91                  SkDoubleToScalar(2.0 * (x * z + y * w)),
92                  SkDoubleToScalar(2.0 * (y * z - x * w)),
93                  SkDoubleToScalar(1.0 - 2.0 * (x * x + y * y)));
94 }
95 
RotateAboutXAxis(double degrees)96 void Transform::RotateAboutXAxis(double degrees) {
97   double radians = gfx::DegToRad(degrees);
98   SkScalar cosTheta = SkDoubleToScalar(std::cos(radians));
99   SkScalar sinTheta = SkDoubleToScalar(std::sin(radians));
100   if (matrix_.isIdentity()) {
101       matrix_.set3x3(1, 0, 0,
102                      0, cosTheta, sinTheta,
103                      0, -sinTheta, cosTheta);
104   } else {
105     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
106     rot.set3x3(1, 0, 0,
107                0, cosTheta, sinTheta,
108                0, -sinTheta, cosTheta);
109     matrix_.preConcat(rot);
110   }
111 }
112 
RotateAboutYAxis(double degrees)113 void Transform::RotateAboutYAxis(double degrees) {
114   double radians = gfx::DegToRad(degrees);
115   SkScalar cosTheta = SkDoubleToScalar(std::cos(radians));
116   SkScalar sinTheta = SkDoubleToScalar(std::sin(radians));
117   if (matrix_.isIdentity()) {
118       // Note carefully the placement of the -sinTheta for rotation about
119       // y-axis is different than rotation about x-axis or z-axis.
120       matrix_.set3x3(cosTheta, 0, -sinTheta,
121                      0, 1, 0,
122                      sinTheta, 0, cosTheta);
123   } else {
124     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
125     rot.set3x3(cosTheta, 0, -sinTheta,
126                0, 1, 0,
127                sinTheta, 0, cosTheta);
128     matrix_.preConcat(rot);
129   }
130 }
131 
RotateAboutZAxis(double degrees)132 void Transform::RotateAboutZAxis(double degrees) {
133   double radians = gfx::DegToRad(degrees);
134   SkScalar cosTheta = SkDoubleToScalar(std::cos(radians));
135   SkScalar sinTheta = SkDoubleToScalar(std::sin(radians));
136   if (matrix_.isIdentity()) {
137       matrix_.set3x3(cosTheta, sinTheta, 0,
138                      -sinTheta, cosTheta, 0,
139                      0, 0, 1);
140   } else {
141     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
142     rot.set3x3(cosTheta, sinTheta, 0,
143                -sinTheta, cosTheta, 0,
144                0, 0, 1);
145     matrix_.preConcat(rot);
146   }
147 }
148 
RotateAbout(const Vector3dF & axis,double degrees)149 void Transform::RotateAbout(const Vector3dF& axis, double degrees) {
150   if (matrix_.isIdentity()) {
151     matrix_.setRotateDegreesAbout(axis.x(), axis.y(), axis.z(),
152                                   SkDoubleToScalar(degrees));
153   } else {
154     SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
155     rot.setRotateDegreesAbout(axis.x(), axis.y(), axis.z(),
156                               SkDoubleToScalar(degrees));
157     matrix_.preConcat(rot);
158   }
159 }
160 
Scale(SkScalar x,SkScalar y)161 void Transform::Scale(SkScalar x, SkScalar y) {
162   matrix_.preScale(x, y, 1);
163 }
164 
PostScale(SkScalar x,SkScalar y)165 void Transform::PostScale(SkScalar x, SkScalar y) {
166   matrix_.postScale(x, y, 1);
167 }
168 
Scale3d(SkScalar x,SkScalar y,SkScalar z)169 void Transform::Scale3d(SkScalar x, SkScalar y, SkScalar z) {
170   matrix_.preScale(x, y, z);
171 }
172 
Translate(const Vector2dF & offset)173 void Transform::Translate(const Vector2dF& offset) {
174   Translate(offset.x(), offset.y());
175 }
176 
Translate(SkScalar x,SkScalar y)177 void Transform::Translate(SkScalar x, SkScalar y) {
178   matrix_.preTranslate(x, y, 0);
179 }
180 
PostTranslate(const Vector2dF & offset)181 void Transform::PostTranslate(const Vector2dF& offset) {
182   PostTranslate(offset.x(), offset.y());
183 }
184 
PostTranslate(SkScalar x,SkScalar y)185 void Transform::PostTranslate(SkScalar x, SkScalar y) {
186   matrix_.postTranslate(x, y, 0);
187 }
188 
Translate3d(const Vector3dF & offset)189 void Transform::Translate3d(const Vector3dF& offset) {
190   Translate3d(offset.x(), offset.y(), offset.z());
191 }
192 
Translate3d(SkScalar x,SkScalar y,SkScalar z)193 void Transform::Translate3d(SkScalar x, SkScalar y, SkScalar z) {
194   matrix_.preTranslate(x, y, z);
195 }
196 
Skew(double angle_x,double angle_y)197 void Transform::Skew(double angle_x, double angle_y) {
198   if (matrix_.isIdentity()) {
199     matrix_.set(0, 1, TanDegrees(angle_x));
200     matrix_.set(1, 0, TanDegrees(angle_y));
201   } else {
202     SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
203     skew.set(0, 1, TanDegrees(angle_x));
204     skew.set(1, 0, TanDegrees(angle_y));
205     matrix_.preConcat(skew);
206   }
207 }
208 
ApplyPerspectiveDepth(SkScalar depth)209 void Transform::ApplyPerspectiveDepth(SkScalar depth) {
210   if (depth == 0)
211     return;
212   if (matrix_.isIdentity()) {
213     matrix_.set(3, 2, -SK_Scalar1 / depth);
214   } else {
215     SkMatrix44 m(SkMatrix44::kIdentity_Constructor);
216     m.set(3, 2, -SK_Scalar1 / depth);
217     matrix_.preConcat(m);
218   }
219 }
220 
PreconcatTransform(const Transform & transform)221 void Transform::PreconcatTransform(const Transform& transform) {
222   matrix_.preConcat(transform.matrix_);
223 }
224 
ConcatTransform(const Transform & transform)225 void Transform::ConcatTransform(const Transform& transform) {
226   matrix_.postConcat(transform.matrix_);
227 }
228 
IsApproximatelyIdentityOrTranslation(SkScalar tolerance) const229 bool Transform::IsApproximatelyIdentityOrTranslation(SkScalar tolerance) const {
230   DCHECK_GE(tolerance, 0);
231   return
232       ApproximatelyOne(matrix_.get(0, 0), tolerance) &&
233       ApproximatelyZero(matrix_.get(1, 0), tolerance) &&
234       ApproximatelyZero(matrix_.get(2, 0), tolerance) &&
235       matrix_.get(3, 0) == 0 &&
236       ApproximatelyZero(matrix_.get(0, 1), tolerance) &&
237       ApproximatelyOne(matrix_.get(1, 1), tolerance) &&
238       ApproximatelyZero(matrix_.get(2, 1), tolerance) &&
239       matrix_.get(3, 1) == 0 &&
240       ApproximatelyZero(matrix_.get(0, 2), tolerance) &&
241       ApproximatelyZero(matrix_.get(1, 2), tolerance) &&
242       ApproximatelyOne(matrix_.get(2, 2), tolerance) &&
243       matrix_.get(3, 2) == 0 &&
244       matrix_.get(3, 3) == 1;
245 }
246 
IsApproximatelyIdentityOrIntegerTranslation(SkScalar tolerance) const247 bool Transform::IsApproximatelyIdentityOrIntegerTranslation(
248     SkScalar tolerance) const {
249   if (!IsApproximatelyIdentityOrTranslation(tolerance))
250     return false;
251 
252   for (float t : {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)}) {
253     if (!base::IsValueInRangeForNumericType<int>(t) ||
254         std::abs(std::round(t) - t) > tolerance)
255       return false;
256   }
257   return true;
258 }
259 
IsIdentityOrIntegerTranslation() const260 bool Transform::IsIdentityOrIntegerTranslation() const {
261   if (!IsIdentityOrTranslation())
262     return false;
263 
264   for (float t : {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)}) {
265     if (!base::IsValueInRangeForNumericType<int>(t) || static_cast<int>(t) != t)
266       return false;
267   }
268   return true;
269 }
270 
IsBackFaceVisible() const271 bool Transform::IsBackFaceVisible() const {
272   // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
273   // would have its back face visible after applying the transform.
274   if (matrix_.isIdentity())
275     return false;
276 
277   // This is done by transforming the normal and seeing if the resulting z
278   // value is positive or negative. However, note that transforming a normal
279   // actually requires using the inverse-transpose of the original transform.
280   //
281   // We can avoid inverting and transposing the matrix since we know we want
282   // to transform only the specific normal vector (0, 0, 1, 0). In this case,
283   // we only need the 3rd row, 3rd column of the inverse-transpose. We can
284   // calculate only the 3rd row 3rd column element of the inverse, skipping
285   // everything else.
286   //
287   // For more information, refer to:
288   //   http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
289   //
290 
291   double determinant = matrix_.determinant();
292 
293   // If matrix was not invertible, then just assume back face is not visible.
294   if (determinant == 0)
295     return false;
296 
297   // Compute the cofactor of the 3rd row, 3rd column.
298   double cofactor_part_1 =
299       matrix_.get(0, 0) * matrix_.get(1, 1) * matrix_.get(3, 3);
300 
301   double cofactor_part_2 =
302       matrix_.get(0, 1) * matrix_.get(1, 3) * matrix_.get(3, 0);
303 
304   double cofactor_part_3 =
305       matrix_.get(0, 3) * matrix_.get(1, 0) * matrix_.get(3, 1);
306 
307   double cofactor_part_4 =
308       matrix_.get(0, 0) * matrix_.get(1, 3) * matrix_.get(3, 1);
309 
310   double cofactor_part_5 =
311       matrix_.get(0, 1) * matrix_.get(1, 0) * matrix_.get(3, 3);
312 
313   double cofactor_part_6 =
314       matrix_.get(0, 3) * matrix_.get(1, 1) * matrix_.get(3, 0);
315 
316   double cofactor33 =
317       cofactor_part_1 +
318       cofactor_part_2 +
319       cofactor_part_3 -
320       cofactor_part_4 -
321       cofactor_part_5 -
322       cofactor_part_6;
323 
324   // Technically the transformed z component is cofactor33 / determinant.  But
325   // we can avoid the costly division because we only care about the resulting
326   // +/- sign; we can check this equivalently by multiplication.
327   return cofactor33 * determinant < -kEpsilon;
328 }
329 
GetInverse(Transform * transform) const330 bool Transform::GetInverse(Transform* transform) const {
331   if (!matrix_.invert(&transform->matrix_)) {
332     // Initialize the return value to identity if this matrix turned
333     // out to be un-invertible.
334     transform->MakeIdentity();
335     return false;
336   }
337 
338   return true;
339 }
340 
Preserves2dAxisAlignment() const341 bool Transform::Preserves2dAxisAlignment() const {
342   // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
343   // after being transformed by this matrix (and implicitly projected by
344   // dropping any non-zero z-values).
345   //
346   // The 4th column can be ignored because translations don't affect axis
347   // alignment. The 3rd column can be ignored because we are assuming 2d
348   // inputs, where z-values will be zero. The 3rd row can also be ignored
349   // because we are assuming 2d outputs, and any resulting z-value is dropped
350   // anyway. For the inner 2x2 portion, the only effects that keep a rect axis
351   // aligned are (1) swapping axes and (2) scaling axes. This can be checked by
352   // verifying only 1 element of every column and row is non-zero.  Degenerate
353   // cases that project the x or y dimension to zero are considered to preserve
354   // axis alignment.
355   //
356   // If the matrix does have perspective component that is affected by x or y
357   // values: The current implementation conservatively assumes that axis
358   // alignment is not preserved.
359 
360   bool has_x_or_y_perspective =
361       matrix_.get(3, 0) != 0 || matrix_.get(3, 1) != 0;
362 
363   int num_non_zero_in_row_0 = 0;
364   int num_non_zero_in_row_1 = 0;
365   int num_non_zero_in_col_0 = 0;
366   int num_non_zero_in_col_1 = 0;
367 
368   if (std::abs(matrix_.get(0, 0)) > kEpsilon) {
369     num_non_zero_in_row_0++;
370     num_non_zero_in_col_0++;
371   }
372 
373   if (std::abs(matrix_.get(0, 1)) > kEpsilon) {
374     num_non_zero_in_row_0++;
375     num_non_zero_in_col_1++;
376   }
377 
378   if (std::abs(matrix_.get(1, 0)) > kEpsilon) {
379     num_non_zero_in_row_1++;
380     num_non_zero_in_col_0++;
381   }
382 
383   if (std::abs(matrix_.get(1, 1)) > kEpsilon) {
384     num_non_zero_in_row_1++;
385     num_non_zero_in_col_1++;
386   }
387 
388   return
389       num_non_zero_in_row_0 <= 1 &&
390       num_non_zero_in_row_1 <= 1 &&
391       num_non_zero_in_col_0 <= 1 &&
392       num_non_zero_in_col_1 <= 1 &&
393       !has_x_or_y_perspective;
394 }
395 
Transpose()396 void Transform::Transpose() {
397   matrix_.transpose();
398 }
399 
FlattenTo2d()400 void Transform::FlattenTo2d() {
401   float tmp[16];
402   matrix_.asColMajorf(tmp);
403   tmp[2] = 0.0;
404   tmp[6] = 0.0;
405   tmp[8] = 0.0;
406   tmp[9] = 0.0;
407   tmp[10] = 1.0;
408   tmp[11] = 0.0;
409   tmp[14] = 0.0;
410   matrix_.setColMajorf(tmp);
411 }
412 
IsFlat() const413 bool Transform::IsFlat() const {
414   return matrix_.get(2, 0) == 0.0 && matrix_.get(2, 1) == 0.0 &&
415          matrix_.get(0, 2) == 0.0 && matrix_.get(1, 2) == 0.0 &&
416          matrix_.get(2, 2) == 1.0 && matrix_.get(3, 2) == 0.0 &&
417          matrix_.get(2, 3) == 0.0;
418 }
419 
To2dTranslation() const420 Vector2dF Transform::To2dTranslation() const {
421   return gfx::Vector2dF(SkScalarToFloat(matrix_.get(0, 3)),
422                         SkScalarToFloat(matrix_.get(1, 3)));
423 }
424 
TransformPoint(Point * point) const425 void Transform::TransformPoint(Point* point) const {
426   DCHECK(point);
427   TransformPointInternal(matrix_, point);
428 }
429 
TransformPoint(PointF * point) const430 void Transform::TransformPoint(PointF* point) const {
431   DCHECK(point);
432   TransformPointInternal(matrix_, point);
433 }
434 
TransformPoint(Point3F * point) const435 void Transform::TransformPoint(Point3F* point) const {
436   DCHECK(point);
437   TransformPointInternal(matrix_, point);
438 }
439 
TransformVector(Vector3dF * vector) const440 void Transform::TransformVector(Vector3dF* vector) const {
441   DCHECK(vector);
442   TransformVectorInternal(matrix_, vector);
443 }
444 
TransformPointReverse(Point * point) const445 bool Transform::TransformPointReverse(Point* point) const {
446   DCHECK(point);
447 
448   // TODO(sad): Try to avoid trying to invert the matrix.
449   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
450   if (!matrix_.invert(&inverse))
451     return false;
452 
453   TransformPointInternal(inverse, point);
454   return true;
455 }
456 
TransformPointReverse(Point3F * point) const457 bool Transform::TransformPointReverse(Point3F* point) const {
458   DCHECK(point);
459 
460   // TODO(sad): Try to avoid trying to invert the matrix.
461   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
462   if (!matrix_.invert(&inverse))
463     return false;
464 
465   TransformPointInternal(inverse, point);
466   return true;
467 }
468 
TransformRect(RectF * rect) const469 void Transform::TransformRect(RectF* rect) const {
470   if (matrix_.isIdentity())
471     return;
472 
473   SkRect src = RectFToSkRect(*rect);
474   SkMatrix(matrix_).mapRect(&src);
475   *rect = SkRectToRectF(src);
476 }
477 
TransformRectReverse(RectF * rect) const478 bool Transform::TransformRectReverse(RectF* rect) const {
479   if (matrix_.isIdentity())
480     return true;
481 
482   SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
483   if (!matrix_.invert(&inverse))
484     return false;
485 
486   SkRect src = RectFToSkRect(*rect);
487   SkMatrix(inverse).mapRect(&src);
488   *rect = SkRectToRectF(src);
489   return true;
490 }
491 
TransformRRectF(RRectF * rrect) const492 bool Transform::TransformRRectF(RRectF* rrect) const {
493   SkRRect result;
494   if (!SkRRect(*rrect).transform(SkMatrix(matrix_), &result))
495     return false;
496   *rrect = gfx::RRectF(result);
497   return true;
498 }
499 
TransformBox(BoxF * box) const500 void Transform::TransformBox(BoxF* box) const {
501   BoxF bounds;
502   bool first_point = true;
503   for (int corner = 0; corner < 8; ++corner) {
504     gfx::Point3F point = box->origin();
505     point += gfx::Vector3dF(corner & 1 ? box->width() : 0.f,
506                             corner & 2 ? box->height() : 0.f,
507                             corner & 4 ? box->depth() : 0.f);
508     TransformPoint(&point);
509     if (first_point) {
510       bounds.set_origin(point);
511       first_point = false;
512     } else {
513       bounds.ExpandTo(point);
514     }
515   }
516   *box = bounds;
517 }
518 
TransformBoxReverse(BoxF * box) const519 bool Transform::TransformBoxReverse(BoxF* box) const {
520   gfx::Transform inverse = *this;
521   if (!GetInverse(&inverse))
522     return false;
523   inverse.TransformBox(box);
524   return true;
525 }
526 
Blend(const Transform & from,double progress)527 bool Transform::Blend(const Transform& from, double progress) {
528   DecomposedTransform to_decomp;
529   DecomposedTransform from_decomp;
530   if (!DecomposeTransform(&to_decomp, *this) ||
531       !DecomposeTransform(&from_decomp, from))
532     return false;
533 
534   to_decomp = BlendDecomposedTransforms(to_decomp, from_decomp, progress);
535 
536   matrix_ = ComposeTransform(to_decomp).matrix();
537   return true;
538 }
539 
RoundTranslationComponents()540 void Transform::RoundTranslationComponents() {
541   matrix_.set(0, 3, std::round(matrix_.get(0, 3)));
542   matrix_.set(1, 3, std::round(matrix_.get(1, 3)));
543 }
544 
TransformPointInternal(const SkMatrix44 & xform,Point3F * point) const545 void Transform::TransformPointInternal(const SkMatrix44& xform,
546                                        Point3F* point) const {
547   if (xform.isIdentity())
548     return;
549 
550   SkScalar p[4] = {point->x(), point->y(), point->z(), 1};
551 
552   xform.mapScalars(p);
553 
554   if (p[3] != SK_Scalar1 && p[3] != 0.f) {
555     float w_inverse = SK_Scalar1 / p[3];
556     point->SetPoint(p[0] * w_inverse, p[1] * w_inverse, p[2] * w_inverse);
557   } else {
558     point->SetPoint(p[0], p[1], p[2]);
559   }
560 }
561 
TransformVectorInternal(const SkMatrix44 & xform,Vector3dF * vector) const562 void Transform::TransformVectorInternal(const SkMatrix44& xform,
563                                         Vector3dF* vector) const {
564   if (xform.isIdentity())
565     return;
566 
567   SkScalar p[4] = {vector->x(), vector->y(), vector->z(), 0};
568 
569   xform.mapScalars(p);
570 
571   vector->set_x(p[0]);
572   vector->set_y(p[1]);
573   vector->set_z(p[2]);
574 }
575 
TransformPointInternal(const SkMatrix44 & xform,PointF * point) const576 void Transform::TransformPointInternal(const SkMatrix44& xform,
577                                        PointF* point) const {
578   if (xform.isIdentity())
579     return;
580 
581   SkScalar p[4] = {SkIntToScalar(point->x()), SkIntToScalar(point->y()), 0, 1};
582 
583   xform.mapScalars(p);
584 
585   point->SetPoint(p[0], p[1]);
586 }
587 
TransformPointInternal(const SkMatrix44 & xform,Point * point) const588 void Transform::TransformPointInternal(const SkMatrix44& xform,
589                                        Point* point) const {
590   PointF point_float(*point);
591   TransformPointInternal(xform, &point_float);
592   *point = ToRoundedPoint(point_float);
593 }
594 
ApproximatelyEqual(const gfx::Transform & transform) const595 bool Transform::ApproximatelyEqual(const gfx::Transform& transform) const {
596   static const float component_tolerance = 0.1f;
597 
598   // We may have a larger discrepancy in the scroll components due to snapping
599   // (floating point error might round the other way).
600   static const float translation_tolerance = 1.f;
601 
602   for (int row = 0; row < 4; row++) {
603     for (int col = 0; col < 4; col++) {
604       const float delta =
605           std::abs(matrix().get(row, col) - transform.matrix().get(row, col));
606       const float tolerance =
607           col == 3 && row < 3 ? translation_tolerance : component_tolerance;
608       if (delta > tolerance)
609         return false;
610     }
611   }
612 
613   return true;
614 }
615 
ToString() const616 std::string Transform::ToString() const {
617   return base::StringPrintf(
618       "[ %+0.4f %+0.4f %+0.4f %+0.4f  \n"
619       "  %+0.4f %+0.4f %+0.4f %+0.4f  \n"
620       "  %+0.4f %+0.4f %+0.4f %+0.4f  \n"
621       "  %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
622       matrix_.get(0, 0),
623       matrix_.get(0, 1),
624       matrix_.get(0, 2),
625       matrix_.get(0, 3),
626       matrix_.get(1, 0),
627       matrix_.get(1, 1),
628       matrix_.get(1, 2),
629       matrix_.get(1, 3),
630       matrix_.get(2, 0),
631       matrix_.get(2, 1),
632       matrix_.get(2, 2),
633       matrix_.get(2, 3),
634       matrix_.get(3, 0),
635       matrix_.get(3, 1),
636       matrix_.get(3, 2),
637       matrix_.get(3, 3));
638 }
639 
640 }  // namespace gfx
641