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 "ui/gfx/point.h" | 11 #include "ui/gfx/point.h" |
| 12 #include "ui/gfx/point3_f.h" | |
| 13 #include "ui/gfx/rect_f.h" | |
| 11 | 14 |
| 12 namespace gfx { | 15 namespace gfx { |
| 13 | 16 |
| 14 namespace { | 17 namespace { |
| 15 | 18 |
| 19 // TODO(avallee): Move this somewhere appropriate in Skia. | |
| 20 // Taken from SkMatrix44 | |
| 21 const float kNearIntEpsilon = 1e-8; | |
| 22 | |
| 23 bool NearInteger(float f) { | |
| 24 return (std::abs(f - std::round(f)) < kNearIntEpsilon); | |
| 25 } | |
| 26 | |
| 16 SkMScalar Length3(SkMScalar v[3]) { | 27 SkMScalar Length3(SkMScalar v[3]) { |
| 17 return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); | 28 return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); |
| 18 } | 29 } |
| 19 | 30 |
| 20 void Scale3(SkMScalar v[3], SkMScalar scale) { | 31 void Scale3(SkMScalar v[3], SkMScalar scale) { |
| 21 for (int i = 0; i < 3; ++i) | 32 for (int i = 0; i < 3; ++i) |
| 22 v[i] *= scale; | 33 v[i] *= scale; |
| 23 } | 34 } |
| 24 | 35 |
| 25 template <int n> | 36 template <int n> |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 return false; | 108 return false; |
| 98 | 109 |
| 99 SkMScalar scale = 1.0 / m.get(3, 3); | 110 SkMScalar scale = 1.0 / m.get(3, 3); |
| 100 for (int i = 0; i < 4; i++) | 111 for (int i = 0; i < 4; i++) |
| 101 for (int j = 0; j < 4; j++) | 112 for (int j = 0; j < 4; j++) |
| 102 m.set(i, j, m.get(i, j) * scale); | 113 m.set(i, j, m.get(i, j) * scale); |
| 103 | 114 |
| 104 return true; | 115 return true; |
| 105 } | 116 } |
| 106 | 117 |
| 118 void BuildPerspectiveMatrix(SkMatrix44* matrix, | |
| 119 const DecomposedTransform& decomp) { | |
| 120 matrix->setIdentity(); | |
| 121 | |
| 122 for (int i = 0; i < 4; i++) | |
| 123 matrix->setDouble(3, i, decomp.perspective[i]); | |
| 124 } | |
| 125 | |
| 126 void BuildTranslationMatrix(SkMatrix44 * matrix, | |
| 127 const DecomposedTransform& decomp) { | |
| 128 matrix->setTranslate(SkDoubleToMScalar(decomp.translate[0]), | |
| 129 SkDoubleToMScalar(decomp.translate[1]), | |
| 130 SkDoubleToMScalar(decomp.translate[2])); | |
| 131 } | |
| 132 | |
| 133 void BuildRotationMatrix(SkMatrix44* matrix, | |
| 134 const DecomposedTransform& decomp) { | |
| 135 double x = decomp.quaternion[0]; | |
| 136 double y = decomp.quaternion[1]; | |
| 137 double z = decomp.quaternion[2]; | |
| 138 double w = decomp.quaternion[3]; | |
| 139 | |
| 140 matrix->set3x3(1.0 - 2.0 * (y * y + z * z), | |
| 141 2.0 * (x * y + z * w), | |
| 142 2.0 * (x * z - y * w), | |
| 143 2.0 * (x * y - z * w), | |
| 144 1.0 - 2.0 * (x * x + z * z), | |
| 145 2.0 * (y * z + x * w), | |
| 146 2.0 * (x * z + y * w), | |
| 147 2.0 * (y * z - x * w), | |
| 148 1.0 - 2.0 * (x * x + y * y)); | |
| 149 } | |
| 150 | |
| 151 void BuildSkewMatrix(SkMatrix44* matrix, const DecomposedTransform& decomp) { | |
| 152 matrix->setIdentity(); | |
| 153 | |
| 154 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | |
| 155 if (decomp.skew[2]) { | |
| 156 temp.setDouble(1, 2, decomp.skew[2]); | |
| 157 matrix->preConcat(temp); | |
| 158 } | |
| 159 | |
| 160 if (decomp.skew[1]) { | |
| 161 temp.setDouble(1, 2, 0); | |
| 162 temp.setDouble(0, 2, decomp.skew[1]); | |
| 163 matrix->preConcat(temp); | |
| 164 } | |
| 165 | |
| 166 if (decomp.skew[0]) { | |
| 167 temp.setDouble(0, 2, 0); | |
| 168 temp.setDouble(0, 1, decomp.skew[0]); | |
| 169 matrix->preConcat(temp); | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 void BuildScaleMatrix(SkMatrix44* matrix, const DecomposedTransform& decomp) { | |
| 174 matrix->setIdentity(); | |
| 175 matrix->setScale(SkDoubleToMScalar(decomp.scale[0]), | |
| 176 SkDoubleToMScalar(decomp.scale[1]), | |
| 177 SkDoubleToMScalar(decomp.scale[2])); | |
| 178 } | |
| 179 | |
| 180 Transform ComposeTransform(const SkMatrix44& perspective, | |
| 181 const SkMatrix44& translation, | |
| 182 const SkMatrix44& rotation, | |
| 183 const SkMatrix44& skew, | |
| 184 const SkMatrix44& scale) { | |
| 185 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | |
| 186 | |
| 187 matrix.preConcat(perspective); | |
| 188 matrix.preConcat(translation); | |
| 189 matrix.preConcat(rotation); | |
| 190 matrix.preConcat(skew); | |
| 191 matrix.preConcat(scale); | |
| 192 | |
| 193 Transform to_return; | |
| 194 to_return.matrix() = matrix; | |
| 195 return to_return; | |
| 196 } | |
| 197 | |
| 198 bool CheckTansformPoint(const PointF& point, | |
|
Ian Vollick
2013/09/14 23:45:31
nit: typo, but even still, it would be nice if the
avallee
2013/10/15 19:22:48
Done.
| |
| 199 const Transform& transform_a, | |
| 200 const Transform& transform_b) { | |
| 201 Point3F point_a, point_b; | |
| 202 point_a = point_b = Point3F(point.x(), point.y(), 0.f); | |
|
Ian Vollick
2013/09/14 23:45:31
nit: Use the Point3F ctor that takes a PointF.
avallee
2013/10/15 19:22:48
Done.
| |
| 203 | |
| 204 // Can't use TransformRect here since it would give us the axis-aligned | |
| 205 // bounding rect of the 4 points in the initial rectable which is not what we | |
| 206 // want. | |
| 207 bool invertible = true; | |
| 208 invertible &= transform_a.TransformPointReverse(&point_a); | |
| 209 invertible &= transform_b.TransformPointReverse(&point_b); | |
|
Ian Vollick
2013/09/14 23:45:31
It's a bummer that we recompute the inverse of the
avallee
2013/10/15 19:22:48
Done.
| |
| 210 DCHECK(invertible) << "Non-invertible transform, cannot snap."; | |
| 211 | |
| 212 if (!(NearInteger(point_b.x()) && NearInteger(point_b.y()))) { | |
| 213 // Integers should get mapped back into integer points. | |
| 214 return false; | |
| 215 } | |
| 216 | |
| 217 if ((point_b - point_a).Length() > 1.0) { | |
| 218 // The changed distance should not be more than 1 pixel. | |
| 219 return false; | |
| 220 } | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 107 } // namespace | 224 } // namespace |
| 108 | 225 |
| 109 Transform GetScaleTransform(const Point& anchor, float scale) { | 226 Transform GetScaleTransform(const Point& anchor, float scale) { |
| 110 Transform transform; | 227 Transform transform; |
| 111 transform.Translate(anchor.x() * (1 - scale), | 228 transform.Translate(anchor.x() * (1 - scale), |
| 112 anchor.y() * (1 - scale)); | 229 anchor.y() * (1 - scale)); |
| 113 transform.Scale(scale, scale); | 230 transform.Scale(scale, scale); |
| 114 return transform; | 231 return transform; |
| 115 } | 232 } |
| 116 | 233 |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 259 if (row[0][2] > row[2][0]) | 376 if (row[0][2] > row[2][0]) |
| 260 decomp->quaternion[1] = -decomp->quaternion[1]; | 377 decomp->quaternion[1] = -decomp->quaternion[1]; |
| 261 if (row[1][0] > row[0][1]) | 378 if (row[1][0] > row[0][1]) |
| 262 decomp->quaternion[2] = -decomp->quaternion[2]; | 379 decomp->quaternion[2] = -decomp->quaternion[2]; |
| 263 | 380 |
| 264 return true; | 381 return true; |
| 265 } | 382 } |
| 266 | 383 |
| 267 // Taken from http://www.w3.org/TR/css3-transforms/. | 384 // Taken from http://www.w3.org/TR/css3-transforms/. |
| 268 Transform ComposeTransform(const DecomposedTransform& decomp) { | 385 Transform ComposeTransform(const DecomposedTransform& decomp) { |
| 269 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | 386 SkMatrix44 perspective(SkMatrix44::kUninitialized_Constructor); |
| 270 for (int i = 0; i < 4; i++) | 387 BuildPerspectiveMatrix(&perspective, decomp); |
| 271 matrix.set(3, i, decomp.perspective[i]); | |
| 272 | 388 |
| 273 matrix.preTranslate( | 389 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor); |
| 274 decomp.translate[0], decomp.translate[1], decomp.translate[2]); | 390 BuildTranslationMatrix(&translation, decomp); |
| 275 | 391 |
| 276 SkMScalar x = decomp.quaternion[0]; | 392 SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor); |
| 277 SkMScalar y = decomp.quaternion[1]; | 393 BuildRotationMatrix(&rotation, decomp); |
| 278 SkMScalar z = decomp.quaternion[2]; | |
| 279 SkMScalar w = decomp.quaternion[3]; | |
| 280 | 394 |
| 395 SkMatrix44 skew(SkMatrix44::kUninitialized_Constructor); | |
| 396 BuildSkewMatrix(&skew, decomp); | |
| 397 | |
| 398 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor); | |
| 399 BuildScaleMatrix(&scale, decomp); | |
| 400 | |
| 401 return ComposeTransform(perspective, translation, rotation, skew, scale); | |
| 402 } | |
| 403 | |
| 404 bool SnapRotation(DecomposedTransform* out, | |
| 405 const DecomposedTransform& decomp, | |
| 406 const Transform& transform, | |
|
Ian Vollick
2013/09/14 23:45:31
This is not the signature I expected. I was hoping
avallee
2013/10/15 19:22:48
Done.
I don't think you want other snapping yet,
| |
| 407 const RectF& viewport) { | |
| 408 | |
| 409 // Create snapped rotation. | |
| 281 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); | 410 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); |
| 282 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), | 411 BuildRotationMatrix(&rotation_matrix, decomp); |
| 283 2.0 * (x * y + z * w), | 412 for (int i = 0; i < 3; ++i) { |
| 284 2.0 * (x * z - y * w), | 413 for (int j = 0; j < 3; ++j) { |
| 285 2.0 * (x * y - z * w), | 414 SkMScalar value = rotation_matrix.get(i, j); |
| 286 1.0 - 2.0 * (x * x + z * z), | 415 // Snap values to -1, 0 or 1. |
| 287 2.0 * (y * z + x * w), | 416 if (value < -0.5f) { |
| 288 2.0 * (x * z + y * w), | 417 value = -1.0f; |
| 289 2.0 * (y * z - x * w), | 418 } else if (value > 0.5f) { |
| 290 1.0 - 2.0 * (x * x + y * y)); | 419 value = 1.0f; |
| 291 | 420 } else { |
| 292 matrix.preConcat(rotation_matrix); | 421 value = 0.0f; |
| 293 | 422 } |
| 294 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | 423 rotation_matrix.set(i, j, value); |
| 295 if (decomp.skew[2]) { | 424 } |
| 296 temp.set(1, 2, decomp.skew[2]); | |
| 297 matrix.preConcat(temp); | |
| 298 } | 425 } |
|
Ian Vollick
2013/09/14 23:45:31
Please make a BuildSnappedRotationMatrix helper (a
avallee
2013/10/15 19:22:48
Added Snapped rotation unless we also want snapped
| |
| 299 | 426 |
| 300 if (decomp.skew[1]) { | 427 // Rebuild matrices for other unchanged components. |
| 301 temp.set(1, 2, 0); | 428 SkMatrix44 perspective(SkMatrix44::kUninitialized_Constructor); |
| 302 temp.set(0, 2, decomp.skew[1]); | 429 BuildPerspectiveMatrix(&perspective, decomp); |
| 303 matrix.preConcat(temp); | 430 |
| 431 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor); | |
| 432 BuildTranslationMatrix(&translation, decomp); | |
| 433 | |
| 434 SkMatrix44 skew(SkMatrix44::kUninitialized_Constructor); | |
| 435 BuildSkewMatrix(&skew, decomp); | |
| 436 | |
| 437 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor); | |
| 438 BuildScaleMatrix(&scale, decomp); | |
| 439 | |
| 440 // Get full tranform | |
| 441 Transform snapped = | |
| 442 ComposeTransform(perspective, translation, rotation_matrix, skew, scale); | |
| 443 | |
| 444 // Verify that viewport is not moved unnaturally. | |
| 445 bool snappable = | |
| 446 CheckTansformPoint(viewport.origin(), transform, snapped) && | |
| 447 CheckTansformPoint(viewport.top_right(), transform, snapped) && | |
| 448 CheckTansformPoint(viewport.bottom_left(), transform, snapped) && | |
| 449 CheckTansformPoint(viewport.bottom_right(), transform, snapped); | |
| 450 if (snappable) { | |
| 451 DecomposeTransform(out, snapped); | |
| 304 } | 452 } |
| 305 | 453 return snappable; |
| 306 if (decomp.skew[0]) { | |
| 307 temp.set(0, 2, 0); | |
| 308 temp.set(0, 1, decomp.skew[0]); | |
| 309 matrix.preConcat(temp); | |
| 310 } | |
| 311 | |
| 312 matrix.preScale(decomp.scale[0], decomp.scale[1], decomp.scale[2]); | |
| 313 | |
| 314 Transform to_return; | |
| 315 to_return.matrix() = matrix; | |
| 316 return to_return; | |
| 317 } | 454 } |
| 318 | 455 |
| 319 } // namespace ui | 456 } // namespace ui |
| OLD | NEW |