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 <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 | 9 |
| 10 #include "base/logging.h" | |
| 10 #include "base/strings/stringprintf.h" | 11 #include "base/strings/stringprintf.h" |
| 11 #include "ui/gfx/point.h" | 12 #include "ui/gfx/point.h" |
| 13 #include "ui/gfx/point3_f.h" | |
| 14 #include "ui/gfx/rect.h" | |
| 12 | 15 |
| 13 namespace gfx { | 16 namespace gfx { |
| 14 | 17 |
| 15 namespace { | 18 namespace { |
| 16 | 19 |
| 20 // TODO(avallee): Move this somewhere appropriate in Skia. | |
| 21 // Taken from SkMatrix44 | |
| 22 const float kNearIntEpsilon = 1e-8f; | |
| 23 | |
| 24 bool NearInteger(float f) { | |
| 25 f = std::abs(f); | |
| 26 return (std::abs(f - std::floor(f + 0.5f)) < kNearIntEpsilon); | |
|
Ian Vollick
2013/10/18 14:54:46
We should be able to ditch this, I think. See belo
avallee
2013/10/21 15:15:44
Done.
| |
| 27 } | |
| 28 | |
| 17 SkMScalar Length3(SkMScalar v[3]) { | 29 SkMScalar Length3(SkMScalar v[3]) { |
| 18 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]), | 30 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]), |
| 19 SkMScalarToDouble(v[2])}; | 31 SkMScalarToDouble(v[2])}; |
| 20 return SkDoubleToMScalar( | 32 return SkDoubleToMScalar( |
| 21 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2])); | 33 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2])); |
| 22 } | 34 } |
| 23 | 35 |
| 24 void Scale3(SkMScalar v[3], SkMScalar scale) { | 36 void Scale3(SkMScalar v[3], SkMScalar scale) { |
| 25 for (int i = 0; i < 3; ++i) | 37 for (int i = 0; i < 3; ++i) |
| 26 v[i] *= scale; | 38 v[i] *= scale; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 101 return false; | 113 return false; |
| 102 | 114 |
| 103 SkMScalar scale = 1.0 / m.get(3, 3); | 115 SkMScalar scale = 1.0 / m.get(3, 3); |
| 104 for (int i = 0; i < 4; i++) | 116 for (int i = 0; i < 4; i++) |
| 105 for (int j = 0; j < 4; j++) | 117 for (int j = 0; j < 4; j++) |
| 106 m.set(i, j, m.get(i, j) * scale); | 118 m.set(i, j, m.get(i, j) * scale); |
| 107 | 119 |
| 108 return true; | 120 return true; |
| 109 } | 121 } |
| 110 | 122 |
| 123 void BuildPerspectiveMatrix(SkMatrix44* matrix, | |
| 124 const DecomposedTransform& decomp) { | |
| 125 matrix->setIdentity(); | |
| 126 | |
| 127 for (int i = 0; i < 4; i++) | |
| 128 matrix->setDouble(3, i, decomp.perspective[i]); | |
| 129 } | |
| 130 | |
| 131 void BuildTranslationMatrix(SkMatrix44 * matrix, | |
| 132 const DecomposedTransform& decomp) { | |
| 133 matrix->setTranslate(SkDoubleToMScalar(decomp.translate[0]), | |
| 134 SkDoubleToMScalar(decomp.translate[1]), | |
| 135 SkDoubleToMScalar(decomp.translate[2])); | |
| 136 } | |
| 137 | |
| 138 void BuildRotationMatrix(SkMatrix44* matrix, | |
| 139 const DecomposedTransform& decomp) { | |
| 140 double x = decomp.quaternion[0]; | |
| 141 double y = decomp.quaternion[1]; | |
| 142 double z = decomp.quaternion[2]; | |
| 143 double w = decomp.quaternion[3]; | |
| 144 | |
| 145 matrix->set3x3(1.0 - 2.0 * (y * y + z * z), | |
| 146 2.0 * (x * y + z * w), | |
| 147 2.0 * (x * z - y * w), | |
| 148 2.0 * (x * y - z * w), | |
| 149 1.0 - 2.0 * (x * x + z * z), | |
| 150 2.0 * (y * z + x * w), | |
| 151 2.0 * (x * z + y * w), | |
| 152 2.0 * (y * z - x * w), | |
| 153 1.0 - 2.0 * (x * x + y * y)); | |
| 154 } | |
| 155 | |
| 156 SkMatrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) { | |
| 157 // Create snapped rotation. | |
| 158 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); | |
| 159 BuildRotationMatrix(&rotation_matrix, decomp); | |
| 160 for (int i = 0; i < 3; ++i) { | |
| 161 for (int j = 0; j < 3; ++j) { | |
| 162 SkMScalar value = rotation_matrix.get(i, j); | |
| 163 // Snap values to -1, 0 or 1. | |
| 164 if (value < -0.5f) { | |
| 165 value = -1.0f; | |
| 166 } else if (value > 0.5f) { | |
| 167 value = 1.0f; | |
| 168 } else { | |
| 169 value = 0.0f; | |
| 170 } | |
| 171 rotation_matrix.set(i, j, value); | |
| 172 } | |
| 173 } | |
| 174 return rotation_matrix; | |
| 175 } | |
| 176 | |
| 177 void BuildSkewMatrix(SkMatrix44* matrix, const DecomposedTransform& decomp) { | |
| 178 matrix->setIdentity(); | |
| 179 | |
| 180 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | |
| 181 if (decomp.skew[2]) { | |
| 182 temp.setDouble(1, 2, decomp.skew[2]); | |
| 183 matrix->preConcat(temp); | |
| 184 } | |
| 185 | |
| 186 if (decomp.skew[1]) { | |
| 187 temp.setDouble(1, 2, 0); | |
| 188 temp.setDouble(0, 2, decomp.skew[1]); | |
| 189 matrix->preConcat(temp); | |
| 190 } | |
| 191 | |
| 192 if (decomp.skew[0]) { | |
| 193 temp.setDouble(0, 2, 0); | |
| 194 temp.setDouble(0, 1, decomp.skew[0]); | |
| 195 matrix->preConcat(temp); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 void BuildScaleMatrix(SkMatrix44* matrix, const DecomposedTransform& decomp) { | |
| 200 matrix->setIdentity(); | |
| 201 matrix->setScale(SkDoubleToMScalar(decomp.scale[0]), | |
| 202 SkDoubleToMScalar(decomp.scale[1]), | |
| 203 SkDoubleToMScalar(decomp.scale[2])); | |
| 204 } | |
| 205 | |
| 206 Transform ComposeTransform(const SkMatrix44& perspective, | |
| 207 const SkMatrix44& translation, | |
| 208 const SkMatrix44& rotation, | |
| 209 const SkMatrix44& skew, | |
| 210 const SkMatrix44& scale) { | |
| 211 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | |
| 212 | |
| 213 matrix.preConcat(perspective); | |
| 214 matrix.preConcat(translation); | |
| 215 matrix.preConcat(rotation); | |
| 216 matrix.preConcat(skew); | |
| 217 matrix.preConcat(scale); | |
| 218 | |
| 219 Transform to_return; | |
| 220 to_return.matrix() = matrix; | |
| 221 return to_return; | |
| 222 } | |
| 223 | |
| 224 bool CheckViewportPointMapsWithinAPixel(const Point& point, | |
| 225 const Transform& transform_a, | |
| 226 const Transform& transform_b) { | |
| 227 Point3F point_a, point_b; | |
| 228 point_a = point_b = Point3F(point); | |
| 229 | |
| 230 // Can't use TransformRect here since it would give us the axis-aligned | |
| 231 // bounding rect of the 4 points in the initial rectable which is not what we | |
| 232 // want. | |
| 233 transform_a.TransformPoint(&point_a); | |
| 234 transform_b.TransformPoint(&point_b); | |
| 235 | |
| 236 if (!(NearInteger(point_b.x()) && NearInteger(point_b.y()))) { | |
|
Ian Vollick
2013/10/18 14:54:46
If we snap everything (not just rotation), then we
avallee
2013/10/21 15:15:44
Gone. Implemented full snapping, (skew snapped to
| |
| 237 // Integers should get mapped back into integer points. | |
| 238 return false; | |
| 239 } | |
| 240 | |
| 241 if ((point_b - point_a).Length() > 1.0) { | |
| 242 // The changed distance should not be more than 1 pixel. | |
| 243 return false; | |
| 244 } | |
| 245 return true; | |
| 246 } | |
| 247 | |
| 248 bool CheckTransformsMapsIntViewportWithinAPixel(const Rect& viewport, | |
| 249 const Transform& original, | |
| 250 const Transform& snapped) { | |
| 251 | |
| 252 Transform original_inv(Transform::kSkipInitialization); | |
| 253 Transform snapped_inv(Transform::kSkipInitialization); | |
| 254 bool invertible = true; | |
| 255 invertible &= original.GetInverse(&original_inv); | |
| 256 invertible &= snapped.GetInverse(&snapped_inv); | |
| 257 DCHECK(invertible) << "Non-invertible transform, cannot snap."; | |
| 258 | |
| 259 return CheckViewportPointMapsWithinAPixel( | |
| 260 viewport.origin(), original_inv, snapped_inv) && | |
| 261 CheckViewportPointMapsWithinAPixel( | |
| 262 viewport.top_right(), original_inv, snapped_inv) && | |
| 263 CheckViewportPointMapsWithinAPixel( | |
| 264 viewport.bottom_left(), original_inv, snapped_inv) && | |
| 265 CheckViewportPointMapsWithinAPixel( | |
| 266 viewport.bottom_right(), original_inv, snapped_inv); | |
| 267 } | |
| 268 | |
| 111 } // namespace | 269 } // namespace |
| 112 | 270 |
| 113 Transform GetScaleTransform(const Point& anchor, float scale) { | 271 Transform GetScaleTransform(const Point& anchor, float scale) { |
| 114 Transform transform; | 272 Transform transform; |
| 115 transform.Translate(anchor.x() * (1 - scale), | 273 transform.Translate(anchor.x() * (1 - scale), |
| 116 anchor.y() * (1 - scale)); | 274 anchor.y() * (1 - scale)); |
| 117 transform.Scale(scale, scale); | 275 transform.Scale(scale, scale); |
| 118 return transform; | 276 return transform; |
| 119 } | 277 } |
| 120 | 278 |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 if (row[0][2] > row[2][0]) | 421 if (row[0][2] > row[2][0]) |
| 264 decomp->quaternion[1] = -decomp->quaternion[1]; | 422 decomp->quaternion[1] = -decomp->quaternion[1]; |
| 265 if (row[1][0] > row[0][1]) | 423 if (row[1][0] > row[0][1]) |
| 266 decomp->quaternion[2] = -decomp->quaternion[2]; | 424 decomp->quaternion[2] = -decomp->quaternion[2]; |
| 267 | 425 |
| 268 return true; | 426 return true; |
| 269 } | 427 } |
| 270 | 428 |
| 271 // Taken from http://www.w3.org/TR/css3-transforms/. | 429 // Taken from http://www.w3.org/TR/css3-transforms/. |
| 272 Transform ComposeTransform(const DecomposedTransform& decomp) { | 430 Transform ComposeTransform(const DecomposedTransform& decomp) { |
| 273 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | 431 SkMatrix44 perspective(SkMatrix44::kUninitialized_Constructor); |
| 274 for (int i = 0; i < 4; i++) | 432 BuildPerspectiveMatrix(&perspective, decomp); |
| 275 matrix.set(3, i, decomp.perspective[i]); | |
| 276 | 433 |
| 277 matrix.preTranslate( | 434 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor); |
| 278 decomp.translate[0], decomp.translate[1], decomp.translate[2]); | 435 BuildTranslationMatrix(&translation, decomp); |
| 279 | 436 |
| 280 SkMScalar x = decomp.quaternion[0]; | 437 SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor); |
| 281 SkMScalar y = decomp.quaternion[1]; | 438 BuildRotationMatrix(&rotation, decomp); |
| 282 SkMScalar z = decomp.quaternion[2]; | |
| 283 SkMScalar w = decomp.quaternion[3]; | |
| 284 | 439 |
| 285 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); | 440 SkMatrix44 skew(SkMatrix44::kUninitialized_Constructor); |
| 286 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), | 441 BuildSkewMatrix(&skew, decomp); |
| 287 2.0 * (x * y + z * w), | |
| 288 2.0 * (x * z - y * w), | |
| 289 2.0 * (x * y - z * w), | |
| 290 1.0 - 2.0 * (x * x + z * z), | |
| 291 2.0 * (y * z + x * w), | |
| 292 2.0 * (x * z + y * w), | |
| 293 2.0 * (y * z - x * w), | |
| 294 1.0 - 2.0 * (x * x + y * y)); | |
| 295 | 442 |
| 296 matrix.preConcat(rotation_matrix); | 443 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor); |
| 444 BuildScaleMatrix(&scale, decomp); | |
| 297 | 445 |
| 298 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | 446 return ComposeTransform(perspective, translation, rotation, skew, scale); |
| 299 if (decomp.skew[2]) { | 447 } |
| 300 temp.set(1, 2, decomp.skew[2]); | 448 |
| 301 matrix.preConcat(temp); | 449 bool SnapRotation(Transform* out, |
|
Ian Vollick
2013/10/18 14:54:46
s/SnapRotation/SnapTransform/ and snap everything
avallee
2013/10/21 15:15:44
Done.
| |
| 450 const Transform& transform, | |
| 451 const Rect& viewport) { | |
| 452 DecomposedTransform decomp; | |
| 453 DecomposeTransform(&decomp, transform); | |
| 454 | |
| 455 SkMatrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp); | |
| 456 | |
| 457 // Rebuild matrices for other unchanged components. | |
| 458 SkMatrix44 perspective(SkMatrix44::kUninitialized_Constructor); | |
| 459 BuildPerspectiveMatrix(&perspective, decomp); | |
| 460 | |
| 461 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor); | |
| 462 BuildTranslationMatrix(&translation, decomp); | |
| 463 | |
| 464 SkMatrix44 skew(SkMatrix44::kUninitialized_Constructor); | |
| 465 BuildSkewMatrix(&skew, decomp); | |
| 466 | |
| 467 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor); | |
| 468 BuildScaleMatrix(&scale, decomp); | |
| 469 | |
| 470 // Get full tranform | |
| 471 Transform snapped = | |
| 472 ComposeTransform(perspective, translation, rotation_matrix, skew, scale); | |
| 473 | |
| 474 // Verify that viewport is not moved unnaturally. | |
| 475 bool snappable = | |
| 476 CheckTransformsMapsIntViewportWithinAPixel(viewport, transform, snapped); | |
| 477 if (snappable) { | |
| 478 *out = snapped; | |
| 302 } | 479 } |
| 303 | 480 return snappable; |
| 304 if (decomp.skew[1]) { | |
| 305 temp.set(1, 2, 0); | |
| 306 temp.set(0, 2, decomp.skew[1]); | |
| 307 matrix.preConcat(temp); | |
| 308 } | |
| 309 | |
| 310 if (decomp.skew[0]) { | |
| 311 temp.set(0, 2, 0); | |
| 312 temp.set(0, 1, decomp.skew[0]); | |
| 313 matrix.preConcat(temp); | |
| 314 } | |
| 315 | |
| 316 matrix.preScale(decomp.scale[0], decomp.scale[1], decomp.scale[2]); | |
| 317 | |
| 318 Transform to_return; | |
| 319 to_return.matrix() = matrix; | |
| 320 return to_return; | |
| 321 } | 481 } |
| 322 | 482 |
| 323 std::string DecomposedTransform::ToString() const { | 483 std::string DecomposedTransform::ToString() const { |
| 324 return base::StringPrintf( | 484 return base::StringPrintf( |
| 325 "translate: %+0.4f %+0.4f %+0.4f\n" | 485 "translate: %+0.4f %+0.4f %+0.4f\n" |
| 326 "scale: %+0.4f %+0.4f %+0.4f\n" | 486 "scale: %+0.4f %+0.4f %+0.4f\n" |
| 327 "skew: %+0.4f %+0.4f %+0.4f\n" | 487 "skew: %+0.4f %+0.4f %+0.4f\n" |
| 328 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n" | 488 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n" |
| 329 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n", | 489 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n", |
| 330 translate[0], | 490 translate[0], |
| 331 translate[1], | 491 translate[1], |
| 332 translate[2], | 492 translate[2], |
| 333 scale[0], | 493 scale[0], |
| 334 scale[1], | 494 scale[1], |
| 335 scale[2], | 495 scale[2], |
| 336 skew[0], | 496 skew[0], |
| 337 skew[1], | 497 skew[1], |
| 338 skew[2], | 498 skew[2], |
| 339 perspective[0], | 499 perspective[0], |
| 340 perspective[1], | 500 perspective[1], |
| 341 perspective[2], | 501 perspective[2], |
| 342 perspective[3], | 502 perspective[3], |
| 343 quaternion[0], | 503 quaternion[0], |
| 344 quaternion[1], | 504 quaternion[1], |
| 345 quaternion[2], | 505 quaternion[2], |
| 346 quaternion[3]); | 506 quaternion[3]); |
| 347 } | 507 } |
| 348 | 508 |
| 349 } // namespace ui | 509 } // namespace ui |
| OLD | NEW |