blob: 27b5e63991d793eaa0b211fdcf48773c99f4baac [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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
23namespace gfx {
24
25namespace {
26
27// Taken from SkMatrix44.
28const SkMScalar kEpsilon = 1e-8f;
29
30SkMScalar TanDegrees(double degrees) {
31 double radians = degrees * M_PI / 180;
32 return SkDoubleToMScalar(std::tan(radians));
33}
34
35inline bool ApproximatelyZero(SkMScalar x, SkMScalar tolerance) {
36 return std::abs(x) <= tolerance;
37}
38
39inline bool ApproximatelyOne(SkMScalar x, SkMScalar tolerance) {
40 return std::abs(x - SkDoubleToMScalar(1.0)) <= tolerance;
41}
42
Etienne Membrives386015a2015-02-19 17:27:12 +010043static 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);
47}
48
James Robinson646469d2014-10-03 15:33:28 -070049} // namespace
50
51Transform::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);
72
73 matrix_.set(0, 1, col2row1);
74 matrix_.set(1, 1, col2row2);
75 matrix_.set(2, 1, col2row3);
76 matrix_.set(3, 1, col2row4);
77
78 matrix_.set(0, 2, col3row1);
79 matrix_.set(1, 2, col3row2);
80 matrix_.set(2, 2, col3row3);
81 matrix_.set(3, 2, col3row4);
82
83 matrix_.set(0, 3, col4row1);
84 matrix_.set(1, 3, col4row2);
85 matrix_.set(2, 3, col4row3);
86 matrix_.set(3, 3, col4row4);
87}
88
89Transform::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);
102}
103
104void 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);
118 }
119}
120
121void 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);
137 }
138}
139
140void 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);
154 }
155}
156
157void 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 degrees);
163 } else {
164 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
165 rot.setRotateDegreesAbout(SkFloatToMScalar(axis.x()),
166 SkFloatToMScalar(axis.y()),
167 SkFloatToMScalar(axis.z()),
168 degrees);
169 matrix_.preConcat(rot);
170 }
171}
172
173void Transform::Scale(SkMScalar x, SkMScalar y) { matrix_.preScale(x, y, 1); }
174
175void Transform::Scale3d(SkMScalar x, SkMScalar y, SkMScalar z) {
176 matrix_.preScale(x, y, z);
177}
178
179void Transform::Translate(SkMScalar x, SkMScalar y) {
180 matrix_.preTranslate(x, y, 0);
181}
182
183void Transform::Translate3d(SkMScalar x, SkMScalar y, SkMScalar z) {
184 matrix_.preTranslate(x, y, z);
185}
186
187void 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);
194 }
195}
196
197void 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);
204 }
205}
206
207void Transform::ApplyPerspectiveDepth(SkMScalar depth) {
208 if (depth == 0)
209 return;
210 if (matrix_.isIdentity())
211 matrix_.set(3, 2, -1.0 / depth);
212 else {
213 SkMatrix44 m(SkMatrix44::kIdentity_Constructor);
214 m.set(3, 2, -1.0 / depth);
215 matrix_.preConcat(m);
216 }
217}
218
219void Transform::PreconcatTransform(const Transform& transform) {
220 matrix_.preConcat(transform.matrix_);
221}
222
223void Transform::ConcatTransform(const Transform& transform) {
224 matrix_.postConcat(transform.matrix_);
225}
226
227bool 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;
244}
245
246bool Transform::IsIdentityOrIntegerTranslation() const {
247 if (!IsIdentityOrTranslation())
248 return false;
249
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);
254
255 return no_fractional_translation;
256}
257
258bool 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;
263
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.
267 //
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.
273 //
274 // For more information, refer to:
275 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
276 //
277
278 double determinant = matrix_.determinant();
279
280 // If matrix was not invertible, then just assume back face is not visible.
281 if (std::abs(determinant) <= kEpsilon)
282 return false;
283
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);
287
288 double cofactor_part_2 =
289 matrix_.get(0, 1) * matrix_.get(1, 3) * matrix_.get(3, 0);
290
291 double cofactor_part_3 =
292 matrix_.get(0, 3) * matrix_.get(1, 0) * matrix_.get(3, 1);
293
294 double cofactor_part_4 =
295 matrix_.get(0, 0) * matrix_.get(1, 3) * matrix_.get(3, 1);
296
297 double cofactor_part_5 =
298 matrix_.get(0, 1) * matrix_.get(1, 0) * matrix_.get(3, 3);
299
300 double cofactor_part_6 =
301 matrix_.get(0, 3) * matrix_.get(1, 1) * matrix_.get(3, 0);
302
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;
310
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;
315}
316
317bool 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;
323 }
324
325 return true;
326}
327
328bool 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).
332 //
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.
342 //
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.
346
347 bool has_x_or_y_perspective =
348 matrix_.get(3, 0) != 0 || matrix_.get(3, 1) != 0;
349
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;
354
355 if (std::abs(matrix_.get(0, 0)) > kEpsilon) {
356 num_non_zero_in_row_0++;
357 num_non_zero_in_col_0++;
358 }
359
360 if (std::abs(matrix_.get(0, 1)) > kEpsilon) {
361 num_non_zero_in_row_0++;
362 num_non_zero_in_col_1++;
363 }
364
365 if (std::abs(matrix_.get(1, 0)) > kEpsilon) {
366 num_non_zero_in_row_1++;
367 num_non_zero_in_col_0++;
368 }
369
370 if (std::abs(matrix_.get(1, 1)) > kEpsilon) {
371 num_non_zero_in_row_1++;
372 num_non_zero_in_col_1++;
373 }
374
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;
381}
382
383void Transform::Transpose() {
384 matrix_.transpose();
385}
386
387void 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);
395}
396
Dave Moore0ae79f42015-03-17 12:56:46 -0700397bool 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;
402}
403
James Robinson646469d2014-10-03 15:33:28 -0700404Vector2dF Transform::To2dTranslation() const {
405 return gfx::Vector2dF(SkMScalarToFloat(matrix_.get(0, 3)),
406 SkMScalarToFloat(matrix_.get(1, 3)));
407}
408
409void Transform::TransformPoint(Point* point) const {
410 DCHECK(point);
411 TransformPointInternal(matrix_, point);
412}
413
414void Transform::TransformPoint(Point3F* point) const {
415 DCHECK(point);
416 TransformPointInternal(matrix_, point);
417}
418
419bool Transform::TransformPointReverse(Point* point) const {
420 DCHECK(point);
421
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;
426
427 TransformPointInternal(inverse, point);
428 return true;
429}
430
431bool Transform::TransformPointReverse(Point3F* point) const {
432 DCHECK(point);
433
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;
438
439 TransformPointInternal(inverse, point);
440 return true;
441}
442
443void Transform::TransformRect(RectF* rect) const {
444 if (matrix_.isIdentity())
445 return;
446
447 SkRect src = RectFToSkRect(*rect);
448 const SkMatrix& matrix = matrix_;
449 matrix.mapRect(&src);
450 *rect = SkRectToRectF(src);
451}
452
453bool Transform::TransformRectReverse(RectF* rect) const {
454 if (matrix_.isIdentity())
455 return true;
456
457 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
458 if (!matrix_.invert(&inverse))
459 return false;
460
461 const SkMatrix& matrix = inverse;
462 SkRect src = RectFToSkRect(*rect);
463 matrix.mapRect(&src);
464 *rect = SkRectToRectF(src);
465 return true;
466}
467
468void 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);
482 }
483 }
484 *box = bounds;
485}
486
487bool Transform::TransformBoxReverse(BoxF* box) const {
488 gfx::Transform inverse = *this;
489 if (!GetInverse(&inverse))
490 return false;
491 inverse.TransformBox(box);
492 return true;
493}
494
495bool 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;
501
502 if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
503 return false;
504
505 matrix_ = ComposeTransform(to_decomp).matrix();
506 return true;
507}
508
Etienne Membrives386015a2015-02-19 17:27:12 +0100509void Transform::RoundTranslationComponents() {
510 matrix_.set(0, 3, Round(matrix_.get(0, 3)));
511 matrix_.set(1, 3, Round(matrix_.get(1, 3)));
512}
513
514
James Robinson646469d2014-10-03 15:33:28 -0700515void Transform::TransformPointInternal(const SkMatrix44& xform,
516 Point3F* point) const {
517 if (xform.isIdentity())
518 return;
519
520 SkMScalar p[4] = {SkFloatToMScalar(point->x()), SkFloatToMScalar(point->y()),
521 SkFloatToMScalar(point->z()), 1};
522
523 xform.mapMScalars(p);
524
525 if (p[3] != SK_MScalar1 && p[3] != 0.f) {
526 float w_inverse = SK_MScalar1 / p[3];
527 point->SetPoint(p[0] * w_inverse, p[1] * w_inverse, p[2] * w_inverse);
528 } else {
529 point->SetPoint(p[0], p[1], p[2]);
530 }
531}
532
533void Transform::TransformPointInternal(const SkMatrix44& xform,
534 Point* point) const {
535 if (xform.isIdentity())
536 return;
537
538 SkMScalar p[4] = {SkFloatToMScalar(point->x()), SkFloatToMScalar(point->y()),
539 0, 1};
540
541 xform.mapMScalars(p);
542
543 point->SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1]));
544}
545
546std::string Transform::ToString() const {
547 return base::StringPrintf(
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 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
552 matrix_.get(0, 0),
553 matrix_.get(0, 1),
554 matrix_.get(0, 2),
555 matrix_.get(0, 3),
556 matrix_.get(1, 0),
557 matrix_.get(1, 1),
558 matrix_.get(1, 2),
559 matrix_.get(1, 3),
560 matrix_.get(2, 0),
561 matrix_.get(2, 1),
562 matrix_.get(2, 2),
563 matrix_.get(2, 3),
564 matrix_.get(3, 0),
565 matrix_.get(3, 1),
566 matrix_.get(3, 2),
567 matrix_.get(3, 3));
568}
569
570} // namespace gfx