Disable ExtensionIconSourceTest.IconLoaded* on Mac and Win
[chromium-blink-merge.git] / ui / gfx / transform.cc
blob5e8799841fc4508782958a3aa716e3c5fe30630e
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/stringprintf.h"
13 #include "ui/gfx/point.h"
14 #include "ui/gfx/point3_f.h"
15 #include "ui/gfx/vector3d_f.h"
16 #include "ui/gfx/rect.h"
17 #include "ui/gfx/safe_integer_conversions.h"
18 #include "ui/gfx/skia_util.h"
19 #include "ui/gfx/transform_util.h"
21 namespace gfx {
23 namespace {
25 // Taken from SkMatrix44.
26 const double kTooSmallForDeterminant = 1e-8;
28 double TanDegrees(double degrees) {
29 double radians = degrees * M_PI / 180;
30 return std::tan(radians);
33 } // namespace
35 Transform::Transform(
36 double col1row1, double col2row1, double col3row1, double col4row1,
37 double col1row2, double col2row2, double col3row2, double col4row2,
38 double col1row3, double col2row3, double col3row3, double col4row3,
39 double col1row4, double col2row4, double col3row4, double col4row4)
40 : matrix_(SkMatrix44::kUninitialized_Constructor)
42 matrix_.setDouble(0, 0, col1row1);
43 matrix_.setDouble(1, 0, col1row2);
44 matrix_.setDouble(2, 0, col1row3);
45 matrix_.setDouble(3, 0, col1row4);
47 matrix_.setDouble(0, 1, col2row1);
48 matrix_.setDouble(1, 1, col2row2);
49 matrix_.setDouble(2, 1, col2row3);
50 matrix_.setDouble(3, 1, col2row4);
52 matrix_.setDouble(0, 2, col3row1);
53 matrix_.setDouble(1, 2, col3row2);
54 matrix_.setDouble(2, 2, col3row3);
55 matrix_.setDouble(3, 2, col3row4);
57 matrix_.setDouble(0, 3, col4row1);
58 matrix_.setDouble(1, 3, col4row2);
59 matrix_.setDouble(2, 3, col4row3);
60 matrix_.setDouble(3, 3, col4row4);
63 Transform::Transform(
64 double col1row1, double col2row1,
65 double col1row2, double col2row2,
66 double x_translation, double y_translation)
67 : matrix_(SkMatrix44::kIdentity_Constructor)
69 matrix_.setDouble(0, 0, col1row1);
70 matrix_.setDouble(1, 0, col1row2);
71 matrix_.setDouble(0, 1, col2row1);
72 matrix_.setDouble(1, 1, col2row2);
73 matrix_.setDouble(0, 3, x_translation);
74 matrix_.setDouble(1, 3, y_translation);
77 void Transform::RotateAboutXAxis(double degrees) {
78 double radians = degrees * M_PI / 180;
79 double cosTheta = std::cos(radians);
80 double sinTheta = std::sin(radians);
81 if (matrix_.isIdentity()) {
82 matrix_.set3x3(1, 0, 0,
83 0, cosTheta, sinTheta,
84 0, -sinTheta, cosTheta);
85 } else {
86 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
87 rot.set3x3(1, 0, 0,
88 0, cosTheta, sinTheta,
89 0, -sinTheta, cosTheta);
90 matrix_.preConcat(rot);
94 void Transform::RotateAboutYAxis(double degrees) {
95 double radians = degrees * M_PI / 180;
96 double cosTheta = std::cos(radians);
97 double sinTheta = std::sin(radians);
98 if (matrix_.isIdentity()) {
99 // Note carefully the placement of the -sinTheta for rotation about
100 // y-axis is different than rotation about x-axis or z-axis.
101 matrix_.set3x3(cosTheta, 0, -sinTheta,
102 0, 1, 0,
103 sinTheta, 0, cosTheta);
104 } else {
105 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
106 rot.set3x3(cosTheta, 0, -sinTheta,
107 0, 1, 0,
108 sinTheta, 0, cosTheta);
109 matrix_.preConcat(rot);
113 void Transform::RotateAboutZAxis(double degrees) {
114 double radians = degrees * M_PI / 180;
115 double cosTheta = std::cos(radians);
116 double sinTheta = std::sin(radians);
117 if (matrix_.isIdentity()) {
118 matrix_.set3x3(cosTheta, sinTheta, 0,
119 -sinTheta, cosTheta, 0,
120 0, 0, 1);
121 } else {
122 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
123 rot.set3x3(cosTheta, sinTheta, 0,
124 -sinTheta, cosTheta, 0,
125 0, 0, 1);
126 matrix_.preConcat(rot);
130 void Transform::RotateAbout(const Vector3dF& axis, double degrees) {
131 if (matrix_.isIdentity()) {
132 matrix_.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()),
133 SkDoubleToMScalar(axis.y()),
134 SkDoubleToMScalar(axis.z()),
135 SkDoubleToMScalar(degrees));
136 } else {
137 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
138 rot.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()),
139 SkDoubleToMScalar(axis.y()),
140 SkDoubleToMScalar(axis.z()),
141 SkDoubleToMScalar(degrees));
142 matrix_.preConcat(rot);
146 void Transform::Scale(double x, double y) {
147 matrix_.preScale(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 1);
150 void Transform::Scale3d(double x, double y, double z) {
151 matrix_.preScale(SkDoubleToMScalar(x),
152 SkDoubleToMScalar(y),
153 SkDoubleToMScalar(z));
156 void Transform::Translate(double x, double y) {
157 matrix_.preTranslate(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 0);
160 void Transform::Translate3d(double x, double y, double z) {
161 matrix_.preTranslate(SkDoubleToMScalar(x),
162 SkDoubleToMScalar(y),
163 SkDoubleToMScalar(z));
166 void Transform::SkewX(double angle_x) {
167 if (matrix_.isIdentity())
168 matrix_.setDouble(0, 1, TanDegrees(angle_x));
169 else {
170 SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
171 skew.setDouble(0, 1, TanDegrees(angle_x));
172 matrix_.preConcat(skew);
176 void Transform::SkewY(double angle_y) {
177 if (matrix_.isIdentity())
178 matrix_.setDouble(1, 0, TanDegrees(angle_y));
179 else {
180 SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
181 skew.setDouble(1, 0, TanDegrees(angle_y));
182 matrix_.preConcat(skew);
186 void Transform::ApplyPerspectiveDepth(double depth) {
187 if (depth == 0)
188 return;
189 if (matrix_.isIdentity())
190 matrix_.setDouble(3, 2, -1.0 / depth);
191 else {
192 SkMatrix44 m(SkMatrix44::kIdentity_Constructor);
193 m.setDouble(3, 2, -1.0 / depth);
194 matrix_.preConcat(m);
198 void Transform::PreconcatTransform(const Transform& transform) {
199 matrix_.preConcat(transform.matrix_);
202 void Transform::ConcatTransform(const Transform& transform) {
203 matrix_.postConcat(transform.matrix_);
206 bool Transform::IsIdentityOrIntegerTranslation() const {
207 if (!IsIdentityOrTranslation())
208 return false;
210 bool no_fractional_translation =
211 static_cast<int>(matrix_.getDouble(0, 3)) == matrix_.getDouble(0, 3) &&
212 static_cast<int>(matrix_.getDouble(1, 3)) == matrix_.getDouble(1, 3) &&
213 static_cast<int>(matrix_.getDouble(2, 3)) == matrix_.getDouble(2, 3);
215 return no_fractional_translation;
218 bool Transform::IsBackFaceVisible() const {
219 // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
220 // would have its back face visible after applying the transform.
221 if (matrix_.isIdentity())
222 return false;
224 // This is done by transforming the normal and seeing if the resulting z
225 // value is positive or negative. However, note that transforming a normal
226 // actually requires using the inverse-transpose of the original transform.
228 // We can avoid inverting and transposing the matrix since we know we want
229 // to transform only the specific normal vector (0, 0, 1, 0). In this case,
230 // we only need the 3rd row, 3rd column of the inverse-transpose. We can
231 // calculate only the 3rd row 3rd column element of the inverse, skipping
232 // everything else.
234 // For more information, refer to:
235 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
238 double determinant = matrix_.determinant();
240 // If matrix was not invertible, then just assume back face is not visible.
241 if (std::abs(determinant) <= kTooSmallForDeterminant)
242 return false;
244 // Compute the cofactor of the 3rd row, 3rd column.
245 double cofactor_part_1 =
246 matrix_.getDouble(0, 0) *
247 matrix_.getDouble(1, 1) *
248 matrix_.getDouble(3, 3);
250 double cofactor_part_2 =
251 matrix_.getDouble(0, 1) *
252 matrix_.getDouble(1, 3) *
253 matrix_.getDouble(3, 0);
255 double cofactor_part_3 =
256 matrix_.getDouble(0, 3) *
257 matrix_.getDouble(1, 0) *
258 matrix_.getDouble(3, 1);
260 double cofactor_part_4 =
261 matrix_.getDouble(0, 0) *
262 matrix_.getDouble(1, 3) *
263 matrix_.getDouble(3, 1);
265 double cofactor_part_5 =
266 matrix_.getDouble(0, 1) *
267 matrix_.getDouble(1, 0) *
268 matrix_.getDouble(3, 3);
270 double cofactor_part_6 =
271 matrix_.getDouble(0, 3) *
272 matrix_.getDouble(1, 1) *
273 matrix_.getDouble(3, 0);
275 double cofactor33 =
276 cofactor_part_1 +
277 cofactor_part_2 +
278 cofactor_part_3 -
279 cofactor_part_4 -
280 cofactor_part_5 -
281 cofactor_part_6;
283 // Technically the transformed z component is cofactor33 / determinant. But
284 // we can avoid the costly division because we only care about the resulting
285 // +/- sign; we can check this equivalently by multiplication.
286 return cofactor33 * determinant < 0;
289 bool Transform::GetInverse(Transform* transform) const {
290 if (!matrix_.invert(&transform->matrix_)) {
291 // Initialize the return value to identity if this matrix turned
292 // out to be un-invertible.
293 transform->MakeIdentity();
294 return false;
297 return true;
300 void Transform::Transpose() {
301 matrix_.transpose();
304 void Transform::FlattenTo2d() {
305 matrix_.setDouble(2, 0, 0.0);
306 matrix_.setDouble(2, 1, 0.0);
307 matrix_.setDouble(0, 2, 0.0);
308 matrix_.setDouble(1, 2, 0.0);
309 matrix_.setDouble(2, 2, 1.0);
310 matrix_.setDouble(3, 2, 0.0);
311 matrix_.setDouble(2, 3, 0.0);
314 void Transform::TransformPoint(Point& point) const {
315 TransformPointInternal(matrix_, point);
318 void Transform::TransformPoint(Point3F& point) const {
319 TransformPointInternal(matrix_, point);
322 bool Transform::TransformPointReverse(Point& point) const {
323 // TODO(sad): Try to avoid trying to invert the matrix.
324 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
325 if (!matrix_.invert(&inverse))
326 return false;
328 TransformPointInternal(inverse, point);
329 return true;
332 bool Transform::TransformPointReverse(Point3F& point) const {
333 // TODO(sad): Try to avoid trying to invert the matrix.
334 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
335 if (!matrix_.invert(&inverse))
336 return false;
338 TransformPointInternal(inverse, point);
339 return true;
342 void Transform::TransformRect(RectF* rect) const {
343 if (matrix_.isIdentity())
344 return;
346 SkRect src = RectFToSkRect(*rect);
347 const SkMatrix& matrix = matrix_;
348 matrix.mapRect(&src);
349 *rect = SkRectToRectF(src);
352 bool Transform::TransformRectReverse(RectF* rect) const {
353 if (matrix_.isIdentity())
354 return true;
356 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
357 if (!matrix_.invert(&inverse))
358 return false;
360 const SkMatrix& matrix = inverse;
361 SkRect src = RectFToSkRect(*rect);
362 matrix.mapRect(&src);
363 *rect = SkRectToRectF(src);
364 return true;
367 bool Transform::Blend(const Transform& from, double progress) {
368 if (progress <= 0.0) {
369 *this = from;
370 return true;
373 if (progress >= 1.0)
374 return true;
376 DecomposedTransform to_decomp;
377 DecomposedTransform from_decomp;
378 if (!DecomposeTransform(&to_decomp, *this) ||
379 !DecomposeTransform(&from_decomp, from))
380 return false;
382 if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress))
383 return false;
385 matrix_ = ComposeTransform(to_decomp).matrix();
386 return true;
389 void Transform::TransformPointInternal(const SkMatrix44& xform,
390 Point3F& point) const {
391 if (xform.isIdentity())
392 return;
394 SkMScalar p[4] = {
395 SkDoubleToMScalar(point.x()),
396 SkDoubleToMScalar(point.y()),
397 SkDoubleToMScalar(point.z()),
398 SkDoubleToMScalar(1)
401 xform.mapMScalars(p);
403 if (p[3] != 1 && abs(p[3]) > 0) {
404 point.SetPoint(p[0] / p[3], p[1] / p[3], p[2]/ p[3]);
405 } else {
406 point.SetPoint(p[0], p[1], p[2]);
410 void Transform::TransformPointInternal(const SkMatrix44& xform,
411 Point& point) const {
412 if (xform.isIdentity())
413 return;
415 SkMScalar p[4] = {
416 SkDoubleToMScalar(point.x()),
417 SkDoubleToMScalar(point.y()),
418 SkDoubleToMScalar(0),
419 SkDoubleToMScalar(1)
422 xform.mapMScalars(p);
424 point.SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1]));
427 std::string Transform::ToString() const {
428 return base::StringPrintf(
429 "[ %+0.4f %+0.4f %+0.4f %+0.4f \n"
430 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
431 " %+0.4f %+0.4f %+0.4f %+0.4f \n"
432 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
433 matrix_.getDouble(0, 0),
434 matrix_.getDouble(0, 1),
435 matrix_.getDouble(0, 2),
436 matrix_.getDouble(0, 3),
437 matrix_.getDouble(1, 0),
438 matrix_.getDouble(1, 1),
439 matrix_.getDouble(1, 2),
440 matrix_.getDouble(1, 3),
441 matrix_.getDouble(2, 0),
442 matrix_.getDouble(2, 1),
443 matrix_.getDouble(2, 2),
444 matrix_.getDouble(2, 3),
445 matrix_.getDouble(3, 0),
446 matrix_.getDouble(3, 1),
447 matrix_.getDouble(3, 2),
448 matrix_.getDouble(3, 3));
451 } // namespace gfx