Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/transform_util.h" | 5 #include "ui/gfx/transform_util.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/logging.h" | |
| 9 #include "ui/gfx/point.h" | 10 #include "ui/gfx/point.h" |
| 11 #include "ui/gfx/point3_f.h" | |
| 12 #include "ui/gfx/rect_f.h" | |
| 10 | 13 |
| 11 namespace gfx { | 14 namespace gfx { |
| 12 | 15 |
| 13 namespace { | 16 namespace { |
| 14 | 17 |
| 18 // Taken from SkMatrix44 | |
| 19 const float kNearIntEpsilon = 1e-8; | |
|
Ian Vollick
2013/09/11 12:19:48
Can we expose this from SkMatrix44? I think we'll
avallee
2013/09/14 01:32:25
Done.
| |
| 20 | |
| 21 bool NearInteger(float f) { | |
| 22 return (std::abs(f-std::round(f)) < kNearIntEpsilon); | |
| 23 } | |
| 24 | |
| 15 double Length3(double v[3]) { | 25 double Length3(double v[3]) { |
| 16 return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); | 26 return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); |
| 17 } | 27 } |
| 18 | 28 |
| 19 void Scale3(double v[3], double scale) { | 29 void Scale3(double v[3], double scale) { |
| 20 for (int i = 0; i < 3; ++i) | 30 for (int i = 0; i < 3; ++i) |
| 21 v[i] *= scale; | 31 v[i] *= scale; |
| 22 } | 32 } |
| 23 | 33 |
| 24 template <int n> | 34 template <int n> |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 257 if (row[2][1] > row[1][2]) | 267 if (row[2][1] > row[1][2]) |
| 258 decomp->quaternion[0] = -decomp->quaternion[0]; | 268 decomp->quaternion[0] = -decomp->quaternion[0]; |
| 259 if (row[0][2] > row[2][0]) | 269 if (row[0][2] > row[2][0]) |
| 260 decomp->quaternion[1] = -decomp->quaternion[1]; | 270 decomp->quaternion[1] = -decomp->quaternion[1]; |
| 261 if (row[1][0] > row[0][1]) | 271 if (row[1][0] > row[0][1]) |
| 262 decomp->quaternion[2] = -decomp->quaternion[2]; | 272 decomp->quaternion[2] = -decomp->quaternion[2]; |
| 263 | 273 |
| 264 return true; | 274 return true; |
| 265 } | 275 } |
| 266 | 276 |
| 267 // Taken from http://www.w3.org/TR/css3-transforms/. | 277 SkMatrix44 QuaternionToRotationMatrix(const double (& quaternion)[4]) { |
| 268 Transform ComposeTransform(const DecomposedTransform& decomp) { | 278 double x = quaternion[0]; |
| 269 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | 279 double y = quaternion[1]; |
| 270 for (int i = 0; i < 4; i++) | 280 double z = quaternion[2]; |
| 271 matrix.setDouble(3, i, decomp.perspective[i]); | 281 double w = quaternion[3]; |
| 272 | |
| 273 matrix.preTranslate(SkDoubleToMScalar(decomp.translate[0]), | |
| 274 SkDoubleToMScalar(decomp.translate[1]), | |
| 275 SkDoubleToMScalar(decomp.translate[2])); | |
| 276 | |
| 277 double x = decomp.quaternion[0]; | |
| 278 double y = decomp.quaternion[1]; | |
| 279 double z = decomp.quaternion[2]; | |
| 280 double w = decomp.quaternion[3]; | |
| 281 | 282 |
| 282 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); | 283 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); |
| 283 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), | 284 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), |
| 284 2.0 * (x * y + z * w), | 285 2.0 * (x * y + z * w), |
| 285 2.0 * (x * z - y * w), | 286 2.0 * (x * z - y * w), |
| 286 2.0 * (x * y - z * w), | 287 2.0 * (x * y - z * w), |
| 287 1.0 - 2.0 * (x * x + z * z), | 288 1.0 - 2.0 * (x * x + z * z), |
| 288 2.0 * (y * z + x * w), | 289 2.0 * (y * z + x * w), |
| 289 2.0 * (x * z + y * w), | 290 2.0 * (x * z + y * w), |
| 290 2.0 * (y * z - x * w), | 291 2.0 * (y * z - x * w), |
| 291 1.0 - 2.0 * (x * x + y * y)); | 292 1.0 - 2.0 * (x * x + y * y)); |
| 292 | 293 |
| 293 matrix.preConcat(rotation_matrix); | 294 return rotation_matrix; |
| 295 } | |
| 294 | 296 |
| 297 void ApplyPerspectiveTranslation(SkMatrix44* matrix, | |
| 298 const DecomposedTransform& decomp) { | |
| 299 for (int i = 0; i < 4; i++) | |
| 300 matrix->setDouble(3, i, decomp.perspective[i]); | |
|
Ian Vollick
2013/09/11 12:19:48
If we're going to be breaking things up into separ
avallee
2013/09/14 01:32:25
Followed the comment from the bottom. Create all t
| |
| 301 | |
| 302 matrix->preTranslate(SkDoubleToMScalar(decomp.translate[0]), | |
| 303 SkDoubleToMScalar(decomp.translate[1]), | |
| 304 SkDoubleToMScalar(decomp.translate[2])); | |
| 305 } | |
| 306 | |
| 307 void ApplySkewScale(SkMatrix44* matrix, | |
| 308 const DecomposedTransform& decomp) { | |
| 295 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | 309 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); |
| 296 if (decomp.skew[2]) { | 310 if (decomp.skew[2]) { |
| 297 temp.setDouble(1, 2, decomp.skew[2]); | 311 temp.setDouble(1, 2, decomp.skew[2]); |
| 298 matrix.preConcat(temp); | 312 matrix->preConcat(temp); |
| 299 } | 313 } |
| 300 | 314 |
| 301 if (decomp.skew[1]) { | 315 if (decomp.skew[1]) { |
| 302 temp.setDouble(1, 2, 0); | 316 temp.setDouble(1, 2, 0); |
| 303 temp.setDouble(0, 2, decomp.skew[1]); | 317 temp.setDouble(0, 2, decomp.skew[1]); |
| 304 matrix.preConcat(temp); | 318 matrix->preConcat(temp); |
| 305 } | 319 } |
| 306 | 320 |
| 307 if (decomp.skew[0]) { | 321 if (decomp.skew[0]) { |
| 308 temp.setDouble(0, 2, 0); | 322 temp.setDouble(0, 2, 0); |
| 309 temp.setDouble(0, 1, decomp.skew[0]); | 323 temp.setDouble(0, 1, decomp.skew[0]); |
| 310 matrix.preConcat(temp); | 324 matrix->preConcat(temp); |
| 311 } | 325 } |
|
Ian Vollick
2013/09/11 12:19:48
Same with skew and scale.
avallee
2013/09/14 01:32:25
Done.
| |
| 312 | 326 |
| 313 matrix.preScale(SkDoubleToMScalar(decomp.scale[0]), | 327 matrix->preScale(SkDoubleToMScalar(decomp.scale[0]), |
| 314 SkDoubleToMScalar(decomp.scale[1]), | 328 SkDoubleToMScalar(decomp.scale[1]), |
| 315 SkDoubleToMScalar(decomp.scale[2])); | 329 SkDoubleToMScalar(decomp.scale[2])); |
| 330 } | |
| 331 | |
| 332 // Taken from http://www.w3.org/TR/css3-transforms/. | |
| 333 Transform ComposeTransform(const DecomposedTransform& decomp) { | |
| 334 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | |
| 335 ApplyPerspectiveTranslation(&matrix, decomp); | |
| 336 | |
| 337 matrix.preConcat(QuaternionToRotationMatrix(decomp.quaternion)); | |
| 338 | |
| 339 ApplySkewScale(&matrix, decomp); | |
| 316 | 340 |
| 317 Transform to_return; | 341 Transform to_return; |
| 318 to_return.matrix() = matrix; | 342 to_return.matrix() = matrix; |
| 319 return to_return; | 343 return to_return; |
| 320 } | 344 } |
| 321 | 345 |
| 346 Transform ComposeWithRotation(const DecomposedTransform& decomp, | |
| 347 const SkMatrix44& rotation_matrix) { | |
| 348 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | |
| 349 ApplyPerspectiveTranslation(&matrix, decomp); | |
| 350 matrix.preConcat(rotation_matrix); | |
| 351 | |
| 352 ApplySkewScale(&matrix, decomp); | |
| 353 | |
| 354 Transform to_return; | |
| 355 to_return.matrix() = matrix; | |
| 356 return to_return; | |
| 357 } | |
| 358 | |
| 359 bool CheckTansformPoint(const PointF& point, const Transform& transform_a, | |
| 360 const Transform& transform_b) { | |
| 361 Point3F point_a, point_b; | |
| 362 point_a = point_b = Point3F(point.x(), point.y(), 0.f); | |
| 363 | |
| 364 // Can't use TransformRect here since it would give us the axis-aligned | |
| 365 // bounding rect of the 4 points in the initial rectable which is not what we | |
| 366 // want. | |
| 367 bool invertible = true; | |
| 368 invertible &= transform_a.TransformPointReverse(&point_a); | |
| 369 invertible &= transform_b.TransformPointReverse(&point_b); | |
| 370 DCHECK(invertible) << "Non-invertible transform, cannot snap."; | |
| 371 | |
| 372 if (!(NearInteger(point_b.x()) && NearInteger(point_b.y()))) { | |
| 373 // Integers should get mapped back into integer points. | |
| 374 return false; | |
| 375 } | |
| 376 | |
| 377 if ((point_b - point_a).Length() > 1.0) { | |
| 378 // The changed distance should not be more than 1 pixel. | |
| 379 return false; | |
| 380 } | |
| 381 return true; | |
| 382 } | |
| 383 | |
| 384 bool SnapRotation(DecomposedTransform* out, const DecomposedTransform& in, | |
| 385 const RectF& viewport) { | |
| 386 | |
| 387 // Create snapped rotation. | |
| 388 SkMatrix44 rotation_matrix = QuaternionToRotationMatrix(in.quaternion); | |
| 389 for (int i = 0; i < 3; ++i) { | |
| 390 for (int j = 0; j < 3; ++j) { | |
| 391 SkMScalar value = rotation_matrix.get(i, j); | |
| 392 // Snap values to -1, 0 or 1. | |
| 393 if (value < -0.5f) { | |
| 394 value = -1.0f; | |
| 395 } else if (value > 0.5f) { | |
| 396 value = 1.0f; | |
| 397 } else { | |
| 398 value = 0.0f; | |
| 399 } | |
| 400 rotation_matrix.set(i, j, value); | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 // Get full tranforms | |
| 405 Transform original = ComposeTransform(in); | |
|
Ian Vollick
2013/09/11 12:19:48
This is wasteful. The caller will have the compose
avallee
2013/09/14 01:32:25
Done.
| |
| 406 Transform snapped = ComposeWithRotation(in, rotation_matrix); | |
|
Ian Vollick
2013/09/11 12:19:48
This isn't going generalize well when we want to s
avallee
2013/09/14 01:32:25
Done.
| |
| 407 | |
| 408 // Verify that viewport is not moved unnaturally. | |
| 409 | |
| 410 bool snappable = CheckTansformPoint(viewport.origin(), original, snapped) && | |
| 411 CheckTansformPoint(viewport.top_right(), original, snapped) && | |
| 412 CheckTansformPoint(viewport.bottom_left(), original, snapped) && | |
| 413 CheckTansformPoint(viewport.bottom_right(), original, snapped); | |
| 414 if (snappable) { | |
| 415 DecomposeTransform(out, snapped); | |
| 416 } | |
| 417 return snappable; | |
| 418 } | |
| 419 | |
| 322 } // namespace ui | 420 } // namespace ui |
| OLD | NEW |