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