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/strings/stringprintf.h" 13 #include "ui/gfx/point.h" 14 #include "ui/gfx/point3_f.h" 15 #include "ui/gfx/rect.h" 16 #include "ui/gfx/safe_integer_conversions.h" 17 #include "ui/gfx/skia_util.h" 18 #include "ui/gfx/transform_util.h" 19 #include "ui/gfx/vector3d_f.h" 20 21 namespace gfx { 22 23 namespace { 24 25 // Taken from SkMatrix44. 26 const double kEpsilon = 1e-8; 27 28 double TanDegrees(double degrees) { 29 double radians = degrees * M_PI / 180; 30 return std::tan(radians); 31 } 32 33 } // namespace 34 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) 41 { 42 matrix_.setDouble(0, 0, col1row1); 43 matrix_.setDouble(1, 0, col1row2); 44 matrix_.setDouble(2, 0, col1row3); 45 matrix_.setDouble(3, 0, col1row4); 46 47 matrix_.setDouble(0, 1, col2row1); 48 matrix_.setDouble(1, 1, col2row2); 49 matrix_.setDouble(2, 1, col2row3); 50 matrix_.setDouble(3, 1, col2row4); 51 52 matrix_.setDouble(0, 2, col3row1); 53 matrix_.setDouble(1, 2, col3row2); 54 matrix_.setDouble(2, 2, col3row3); 55 matrix_.setDouble(3, 2, col3row4); 56 57 matrix_.setDouble(0, 3, col4row1); 58 matrix_.setDouble(1, 3, col4row2); 59 matrix_.setDouble(2, 3, col4row3); 60 matrix_.setDouble(3, 3, col4row4); 61 } 62 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) 68 { 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); 75 } 76 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); 91 } 92 } 93 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); 110 } 111 } 112 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); 127 } 128 } 129 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); 143 } 144 } 145 146 void Transform::Scale(double x, double y) { 147 matrix_.preScale(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 1); 148 } 149 150 void Transform::Scale3d(double x, double y, double z) { 151 matrix_.preScale(SkDoubleToMScalar(x), 152 SkDoubleToMScalar(y), 153 SkDoubleToMScalar(z)); 154 } 155 156 void Transform::Translate(double x, double y) { 157 matrix_.preTranslate(SkDoubleToMScalar(x), SkDoubleToMScalar(y), 0); 158 } 159 160 void Transform::Translate3d(double x, double y, double z) { 161 matrix_.preTranslate(SkDoubleToMScalar(x), 162 SkDoubleToMScalar(y), 163 SkDoubleToMScalar(z)); 164 } 165 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); 173 } 174 } 175 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); 183 } 184 } 185 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); 195 } 196 } 197 198 void Transform::PreconcatTransform(const Transform& transform) { 199 matrix_.preConcat(transform.matrix_); 200 } 201 202 void Transform::ConcatTransform(const Transform& transform) { 203 matrix_.postConcat(transform.matrix_); 204 } 205 206 bool Transform::IsIdentityOrIntegerTranslation() const { 207 if (!IsIdentityOrTranslation()) 208 return false; 209 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); 214 215 return no_fractional_translation; 216 } 217 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; 223 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. 227 // 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. 233 // 234 // For more information, refer to: 235 // http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution 236 // 237 238 double determinant = matrix_.determinant(); 239 240 // If matrix was not invertible, then just assume back face is not visible. 241 if (std::abs(determinant) <= kEpsilon) 242 return false; 243 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); 249 250 double cofactor_part_2 = 251 matrix_.getDouble(0, 1) * 252 matrix_.getDouble(1, 3) * 253 matrix_.getDouble(3, 0); 254 255 double cofactor_part_3 = 256 matrix_.getDouble(0, 3) * 257 matrix_.getDouble(1, 0) * 258 matrix_.getDouble(3, 1); 259 260 double cofactor_part_4 = 261 matrix_.getDouble(0, 0) * 262 matrix_.getDouble(1, 3) * 263 matrix_.getDouble(3, 1); 264 265 double cofactor_part_5 = 266 matrix_.getDouble(0, 1) * 267 matrix_.getDouble(1, 0) * 268 matrix_.getDouble(3, 3); 269 270 double cofactor_part_6 = 271 matrix_.getDouble(0, 3) * 272 matrix_.getDouble(1, 1) * 273 matrix_.getDouble(3, 0); 274 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; 282 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; 287 } 288 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; 295 } 296 297 return true; 298 } 299 300 bool Transform::Preserves2dAxisAlignment() const { 301 // Check whether an axis aligned 2-dimensional rect would remain axis-aligned 302 // after being transformed by this matrix (and implicitly projected by 303 // dropping any non-zero z-values). 304 // 305 // The 4th column can be ignored because translations don't affect axis 306 // alignment. The 3rd column can be ignored because we are assuming 2d 307 // inputs, where z-values will be zero. The 3rd row can also be ignored 308 // because we are assuming 2d outputs, and any resulting z-value is dropped 309 // anyway. For the inner 2x2 portion, the only effects that keep a rect axis 310 // aligned are (1) swapping axes and (2) scaling axes. This can be checked by 311 // verifying only 1 element of every column and row is non-zero. Degenerate 312 // cases that project the x or y dimension to zero are considered to preserve 313 // axis alignment. 314 // 315 // If the matrix does have perspective component that is affected by x or y 316 // values: The current implementation conservatively assumes that axis 317 // alignment is not preserved. 318 319 bool has_x_or_y_perspective = matrix_.getDouble(3, 0) != 0 || 320 matrix_.getDouble(3, 1) != 0; 321 322 int num_non_zero_in_row_0 = 0; 323 int num_non_zero_in_row_1 = 0; 324 int num_non_zero_in_col_0 = 0; 325 int num_non_zero_in_col_1 = 0; 326 327 if (std::abs(matrix_.getDouble(0, 0)) > kEpsilon) { 328 num_non_zero_in_row_0++; 329 num_non_zero_in_col_0++; 330 } 331 332 if (std::abs(matrix_.getDouble(0, 1)) > kEpsilon) { 333 num_non_zero_in_row_0++; 334 num_non_zero_in_col_1++; 335 } 336 337 if (std::abs(matrix_.getDouble(1, 0)) > kEpsilon) { 338 num_non_zero_in_row_1++; 339 num_non_zero_in_col_0++; 340 } 341 342 if (std::abs(matrix_.getDouble(1, 1)) > kEpsilon) { 343 num_non_zero_in_row_1++; 344 num_non_zero_in_col_1++; 345 } 346 347 return 348 num_non_zero_in_row_0 <= 1 && 349 num_non_zero_in_row_1 <= 1 && 350 num_non_zero_in_col_0 <= 1 && 351 num_non_zero_in_col_1 <= 1 && 352 !has_x_or_y_perspective; 353 } 354 355 void Transform::Transpose() { 356 matrix_.transpose(); 357 } 358 359 void Transform::FlattenTo2d() { 360 matrix_.setDouble(2, 0, 0.0); 361 matrix_.setDouble(2, 1, 0.0); 362 matrix_.setDouble(0, 2, 0.0); 363 matrix_.setDouble(1, 2, 0.0); 364 matrix_.setDouble(2, 2, 1.0); 365 matrix_.setDouble(3, 2, 0.0); 366 matrix_.setDouble(2, 3, 0.0); 367 } 368 369 void Transform::TransformPoint(Point& point) const { 370 TransformPointInternal(matrix_, point); 371 } 372 373 void Transform::TransformPoint(Point3F& point) const { 374 TransformPointInternal(matrix_, point); 375 } 376 377 bool Transform::TransformPointReverse(Point& point) const { 378 // TODO(sad): Try to avoid trying to invert the matrix. 379 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 380 if (!matrix_.invert(&inverse)) 381 return false; 382 383 TransformPointInternal(inverse, point); 384 return true; 385 } 386 387 bool Transform::TransformPointReverse(Point3F& point) const { 388 // TODO(sad): Try to avoid trying to invert the matrix. 389 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 390 if (!matrix_.invert(&inverse)) 391 return false; 392 393 TransformPointInternal(inverse, point); 394 return true; 395 } 396 397 void Transform::TransformRect(RectF* rect) const { 398 if (matrix_.isIdentity()) 399 return; 400 401 SkRect src = RectFToSkRect(*rect); 402 const SkMatrix& matrix = matrix_; 403 matrix.mapRect(&src); 404 *rect = SkRectToRectF(src); 405 } 406 407 bool Transform::TransformRectReverse(RectF* rect) const { 408 if (matrix_.isIdentity()) 409 return true; 410 411 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 412 if (!matrix_.invert(&inverse)) 413 return false; 414 415 const SkMatrix& matrix = inverse; 416 SkRect src = RectFToSkRect(*rect); 417 matrix.mapRect(&src); 418 *rect = SkRectToRectF(src); 419 return true; 420 } 421 422 bool Transform::Blend(const Transform& from, double progress) { 423 DecomposedTransform to_decomp; 424 DecomposedTransform from_decomp; 425 if (!DecomposeTransform(&to_decomp, *this) || 426 !DecomposeTransform(&from_decomp, from)) 427 return false; 428 429 if (!BlendDecomposedTransforms(&to_decomp, to_decomp, from_decomp, progress)) 430 return false; 431 432 matrix_ = ComposeTransform(to_decomp).matrix(); 433 return true; 434 } 435 436 void Transform::TransformPointInternal(const SkMatrix44& xform, 437 Point3F& point) const { 438 if (xform.isIdentity()) 439 return; 440 441 SkMScalar p[4] = { 442 SkDoubleToMScalar(point.x()), 443 SkDoubleToMScalar(point.y()), 444 SkDoubleToMScalar(point.z()), 445 SkDoubleToMScalar(1) 446 }; 447 448 xform.mapMScalars(p); 449 450 if (p[3] != 1 && abs(p[3]) > 0) { 451 point.SetPoint(p[0] / p[3], p[1] / p[3], p[2]/ p[3]); 452 } else { 453 point.SetPoint(p[0], p[1], p[2]); 454 } 455 } 456 457 void Transform::TransformPointInternal(const SkMatrix44& xform, 458 Point& point) const { 459 if (xform.isIdentity()) 460 return; 461 462 SkMScalar p[4] = { 463 SkDoubleToMScalar(point.x()), 464 SkDoubleToMScalar(point.y()), 465 SkDoubleToMScalar(0), 466 SkDoubleToMScalar(1) 467 }; 468 469 xform.mapMScalars(p); 470 471 point.SetPoint(ToRoundedInt(p[0]), ToRoundedInt(p[1])); 472 } 473 474 std::string Transform::ToString() const { 475 return base::StringPrintf( 476 "[ %+0.4f %+0.4f %+0.4f %+0.4f \n" 477 " %+0.4f %+0.4f %+0.4f %+0.4f \n" 478 " %+0.4f %+0.4f %+0.4f %+0.4f \n" 479 " %+0.4f %+0.4f %+0.4f %+0.4f ]\n", 480 matrix_.getDouble(0, 0), 481 matrix_.getDouble(0, 1), 482 matrix_.getDouble(0, 2), 483 matrix_.getDouble(0, 3), 484 matrix_.getDouble(1, 0), 485 matrix_.getDouble(1, 1), 486 matrix_.getDouble(1, 2), 487 matrix_.getDouble(1, 3), 488 matrix_.getDouble(2, 0), 489 matrix_.getDouble(2, 1), 490 matrix_.getDouble(2, 2), 491 matrix_.getDouble(2, 3), 492 matrix_.getDouble(3, 0), 493 matrix_.getDouble(3, 1), 494 matrix_.getDouble(3, 2), 495 matrix_.getDouble(3, 3)); 496 } 497 498 } // namespace gfx 499