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 |
17 SkMScalar Length3(SkMScalar v[3]) { | 20 SkMScalar Length3(SkMScalar v[3]) { |
18 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]), | 21 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]), |
19 SkMScalarToDouble(v[2])}; | 22 SkMScalarToDouble(v[2])}; |
20 return SkDoubleToMScalar( | 23 return SkDoubleToMScalar( |
21 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2])); | 24 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2])); |
(...skipping 24 matching lines...) Expand all Loading... |
46 | 49 |
47 void Cross3(SkMScalar out[3], SkMScalar a[3], SkMScalar b[3]) { | 50 void Cross3(SkMScalar out[3], SkMScalar a[3], SkMScalar b[3]) { |
48 SkMScalar x = a[1] * b[2] - a[2] * b[1]; | 51 SkMScalar x = a[1] * b[2] - a[2] * b[1]; |
49 SkMScalar y = a[2] * b[0] - a[0] * b[2]; | 52 SkMScalar y = a[2] * b[0] - a[0] * b[2]; |
50 SkMScalar z = a[0] * b[1] - a[1] * b[0]; | 53 SkMScalar z = a[0] * b[1] - a[1] * b[0]; |
51 out[0] = x; | 54 out[0] = x; |
52 out[1] = y; | 55 out[1] = y; |
53 out[2] = z; | 56 out[2] = z; |
54 } | 57 } |
55 | 58 |
| 59 SkMScalar Round(SkMScalar n) { |
| 60 return SkDoubleToMScalar(std::floor(SkMScalarToDouble(n) + 0.5)); |
| 61 } |
| 62 |
56 // Taken from http://www.w3.org/TR/css3-transforms/. | 63 // Taken from http://www.w3.org/TR/css3-transforms/. |
57 bool Slerp(SkMScalar out[4], | 64 bool Slerp(SkMScalar out[4], |
58 const SkMScalar q1[4], | 65 const SkMScalar q1[4], |
59 const SkMScalar q2[4], | 66 const SkMScalar q2[4], |
60 double progress) { | 67 double progress) { |
61 double product = Dot<4>(q1, q2); | 68 double product = Dot<4>(q1, q2); |
62 | 69 |
63 // Clamp product to -1.0 <= product <= 1.0. | 70 // Clamp product to -1.0 <= product <= 1.0. |
64 product = std::min(std::max(product, -1.0), 1.0); | 71 product = std::min(std::max(product, -1.0), 1.0); |
65 | 72 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 return false; | 108 return false; |
102 | 109 |
103 SkMScalar scale = 1.0 / m.get(3, 3); | 110 SkMScalar scale = 1.0 / m.get(3, 3); |
104 for (int i = 0; i < 4; i++) | 111 for (int i = 0; i < 4; i++) |
105 for (int j = 0; j < 4; j++) | 112 for (int j = 0; j < 4; j++) |
106 m.set(i, j, m.get(i, j) * scale); | 113 m.set(i, j, m.get(i, j) * scale); |
107 | 114 |
108 return true; | 115 return true; |
109 } | 116 } |
110 | 117 |
| 118 SkMatrix44 BuildPerspectiveMatrix(const DecomposedTransform& decomp) { |
| 119 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); |
| 120 |
| 121 for (int i = 0; i < 4; i++) |
| 122 matrix.setDouble(3, i, decomp.perspective[i]); |
| 123 return matrix; |
| 124 } |
| 125 |
| 126 SkMatrix44 BuildTranslationMatrix(const DecomposedTransform& decomp) { |
| 127 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); |
| 128 // Implicitly calls matrix.setIdentity() |
| 129 matrix.setTranslate(SkDoubleToMScalar(decomp.translate[0]), |
| 130 SkDoubleToMScalar(decomp.translate[1]), |
| 131 SkDoubleToMScalar(decomp.translate[2])); |
| 132 return matrix; |
| 133 } |
| 134 |
| 135 SkMatrix44 BuildSnappedTranslationMatrix(DecomposedTransform decomp) { |
| 136 decomp.translate[0] = Round(decomp.translate[0]); |
| 137 decomp.translate[1] = Round(decomp.translate[1]); |
| 138 decomp.translate[2] = Round(decomp.translate[2]); |
| 139 return BuildTranslationMatrix(decomp); |
| 140 } |
| 141 |
| 142 SkMatrix44 BuildRotationMatrix(const DecomposedTransform& decomp) { |
| 143 double x = decomp.quaternion[0]; |
| 144 double y = decomp.quaternion[1]; |
| 145 double z = decomp.quaternion[2]; |
| 146 double w = decomp.quaternion[3]; |
| 147 |
| 148 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); |
| 149 |
| 150 // Implicitly calls matrix.setIdentity() |
| 151 matrix.set3x3(1.0 - 2.0 * (y * y + z * z), |
| 152 2.0 * (x * y + z * w), |
| 153 2.0 * (x * z - y * w), |
| 154 2.0 * (x * y - z * w), |
| 155 1.0 - 2.0 * (x * x + z * z), |
| 156 2.0 * (y * z + x * w), |
| 157 2.0 * (x * z + y * w), |
| 158 2.0 * (y * z - x * w), |
| 159 1.0 - 2.0 * (x * x + y * y)); |
| 160 return matrix; |
| 161 } |
| 162 |
| 163 SkMatrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) { |
| 164 // Create snapped rotation. |
| 165 SkMatrix44 rotation_matrix = BuildRotationMatrix(decomp); |
| 166 for (int i = 0; i < 3; ++i) { |
| 167 for (int j = 0; j < 3; ++j) { |
| 168 SkMScalar value = rotation_matrix.get(i, j); |
| 169 // Snap values to -1, 0 or 1. |
| 170 if (value < -0.5f) { |
| 171 value = -1.0f; |
| 172 } else if (value > 0.5f) { |
| 173 value = 1.0f; |
| 174 } else { |
| 175 value = 0.0f; |
| 176 } |
| 177 rotation_matrix.set(i, j, value); |
| 178 } |
| 179 } |
| 180 return rotation_matrix; |
| 181 } |
| 182 |
| 183 SkMatrix44 BuildSkewMatrix(const DecomposedTransform& decomp) { |
| 184 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); |
| 185 |
| 186 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); |
| 187 if (decomp.skew[2]) { |
| 188 temp.setDouble(1, 2, decomp.skew[2]); |
| 189 matrix.preConcat(temp); |
| 190 } |
| 191 |
| 192 if (decomp.skew[1]) { |
| 193 temp.setDouble(1, 2, 0); |
| 194 temp.setDouble(0, 2, decomp.skew[1]); |
| 195 matrix.preConcat(temp); |
| 196 } |
| 197 |
| 198 if (decomp.skew[0]) { |
| 199 temp.setDouble(0, 2, 0); |
| 200 temp.setDouble(0, 1, decomp.skew[0]); |
| 201 matrix.preConcat(temp); |
| 202 } |
| 203 return matrix; |
| 204 } |
| 205 |
| 206 SkMatrix44 BuildScaleMatrix(const DecomposedTransform& decomp) { |
| 207 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); |
| 208 matrix.setScale(SkDoubleToMScalar(decomp.scale[0]), |
| 209 SkDoubleToMScalar(decomp.scale[1]), |
| 210 SkDoubleToMScalar(decomp.scale[2])); |
| 211 return matrix; |
| 212 } |
| 213 |
| 214 SkMatrix44 BuildSnappedScaleMatrix(DecomposedTransform decomp) { |
| 215 decomp.scale[0] = Round(decomp.scale[0]); |
| 216 decomp.scale[1] = Round(decomp.scale[1]); |
| 217 decomp.scale[2] = Round(decomp.scale[2]); |
| 218 return BuildScaleMatrix(decomp); |
| 219 } |
| 220 |
| 221 Transform ComposeTransform(const SkMatrix44& perspective, |
| 222 const SkMatrix44& translation, |
| 223 const SkMatrix44& rotation, |
| 224 const SkMatrix44& skew, |
| 225 const SkMatrix44& scale) { |
| 226 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); |
| 227 |
| 228 matrix.preConcat(perspective); |
| 229 matrix.preConcat(translation); |
| 230 matrix.preConcat(rotation); |
| 231 matrix.preConcat(skew); |
| 232 matrix.preConcat(scale); |
| 233 |
| 234 Transform to_return; |
| 235 to_return.matrix() = matrix; |
| 236 return to_return; |
| 237 } |
| 238 |
| 239 bool CheckViewportPointMapsWithinOnePixel(const Point& point, |
| 240 const Transform& transform) { |
| 241 Point3F point_original(point); |
| 242 Point3F point_transformed(point); |
| 243 |
| 244 // Can't use TransformRect here since it would give us the axis-aligned |
| 245 // bounding rect of the 4 points in the initial rectable which is not what we |
| 246 // want. |
| 247 transform.TransformPoint(&point_transformed); |
| 248 |
| 249 if ((point_transformed - point_original).Length() > 1.f) { |
| 250 // The changed distance should not be more than 1 pixel. |
| 251 return false; |
| 252 } |
| 253 return true; |
| 254 } |
| 255 |
| 256 bool CheckTransformsMapsIntViewportWithinOnePixel(const Rect& viewport, |
| 257 const Transform& original, |
| 258 const Transform& snapped) { |
| 259 |
| 260 Transform original_inv(Transform::kSkipInitialization); |
| 261 bool invertible = true; |
| 262 invertible &= original.GetInverse(&original_inv); |
| 263 DCHECK(invertible) << "Non-invertible transform, cannot snap."; |
| 264 |
| 265 Transform combined = snapped * original_inv; |
| 266 |
| 267 return CheckViewportPointMapsWithinOnePixel(viewport.origin(), combined) && |
| 268 CheckViewportPointMapsWithinOnePixel(viewport.top_right(), combined) && |
| 269 CheckViewportPointMapsWithinOnePixel(viewport.bottom_left(), |
| 270 combined) && |
| 271 CheckViewportPointMapsWithinOnePixel(viewport.bottom_right(), |
| 272 combined); |
| 273 } |
| 274 |
111 } // namespace | 275 } // namespace |
112 | 276 |
113 Transform GetScaleTransform(const Point& anchor, float scale) { | 277 Transform GetScaleTransform(const Point& anchor, float scale) { |
114 Transform transform; | 278 Transform transform; |
115 transform.Translate(anchor.x() * (1 - scale), | 279 transform.Translate(anchor.x() * (1 - scale), |
116 anchor.y() * (1 - scale)); | 280 anchor.y() * (1 - scale)); |
117 transform.Scale(scale, scale); | 281 transform.Scale(scale, scale); |
118 return transform; | 282 return transform; |
119 } | 283 } |
120 | 284 |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 if (row[0][2] > row[2][0]) | 427 if (row[0][2] > row[2][0]) |
264 decomp->quaternion[1] = -decomp->quaternion[1]; | 428 decomp->quaternion[1] = -decomp->quaternion[1]; |
265 if (row[1][0] > row[0][1]) | 429 if (row[1][0] > row[0][1]) |
266 decomp->quaternion[2] = -decomp->quaternion[2]; | 430 decomp->quaternion[2] = -decomp->quaternion[2]; |
267 | 431 |
268 return true; | 432 return true; |
269 } | 433 } |
270 | 434 |
271 // Taken from http://www.w3.org/TR/css3-transforms/. | 435 // Taken from http://www.w3.org/TR/css3-transforms/. |
272 Transform ComposeTransform(const DecomposedTransform& decomp) { | 436 Transform ComposeTransform(const DecomposedTransform& decomp) { |
273 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | 437 SkMatrix44 perspective = BuildPerspectiveMatrix(decomp); |
274 for (int i = 0; i < 4; i++) | 438 SkMatrix44 translation = BuildTranslationMatrix(decomp); |
275 matrix.set(3, i, decomp.perspective[i]); | 439 SkMatrix44 rotation = BuildRotationMatrix(decomp); |
| 440 SkMatrix44 skew = BuildSkewMatrix(decomp); |
| 441 SkMatrix44 scale = BuildScaleMatrix(decomp); |
276 | 442 |
277 matrix.preTranslate( | 443 return ComposeTransform(perspective, translation, rotation, skew, scale); |
278 decomp.translate[0], decomp.translate[1], decomp.translate[2]); | 444 } |
279 | 445 |
280 SkMScalar x = decomp.quaternion[0]; | 446 bool SnapTransform(Transform* out, |
281 SkMScalar y = decomp.quaternion[1]; | 447 const Transform& transform, |
282 SkMScalar z = decomp.quaternion[2]; | 448 const Rect& viewport) { |
283 SkMScalar w = decomp.quaternion[3]; | 449 DecomposedTransform decomp; |
| 450 DecomposeTransform(&decomp, transform); |
284 | 451 |
285 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); | 452 SkMatrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp); |
286 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), | 453 SkMatrix44 translation = BuildSnappedTranslationMatrix(decomp); |
287 2.0 * (x * y + z * w), | 454 SkMatrix44 scale = BuildSnappedScaleMatrix(decomp); |
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 | 455 |
296 matrix.preConcat(rotation_matrix); | 456 // Rebuild matrices for other unchanged components. |
| 457 SkMatrix44 perspective = BuildPerspectiveMatrix(decomp); |
297 | 458 |
298 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | 459 // Completely ignore the skew. |
299 if (decomp.skew[2]) { | 460 SkMatrix44 skew(SkMatrix44::kIdentity_Constructor); |
300 temp.set(1, 2, decomp.skew[2]); | 461 |
301 matrix.preConcat(temp); | 462 // Get full tranform |
| 463 Transform snapped = |
| 464 ComposeTransform(perspective, translation, rotation_matrix, skew, scale); |
| 465 |
| 466 // Verify that viewport is not moved unnaturally. |
| 467 bool snappable = |
| 468 CheckTransformsMapsIntViewportWithinOnePixel(viewport, transform, snapped); |
| 469 if (snappable) { |
| 470 *out = snapped; |
302 } | 471 } |
303 | 472 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 } | 473 } |
322 | 474 |
323 std::string DecomposedTransform::ToString() const { | 475 std::string DecomposedTransform::ToString() const { |
324 return base::StringPrintf( | 476 return base::StringPrintf( |
325 "translate: %+0.4f %+0.4f %+0.4f\n" | 477 "translate: %+0.4f %+0.4f %+0.4f\n" |
326 "scale: %+0.4f %+0.4f %+0.4f\n" | 478 "scale: %+0.4f %+0.4f %+0.4f\n" |
327 "skew: %+0.4f %+0.4f %+0.4f\n" | 479 "skew: %+0.4f %+0.4f %+0.4f\n" |
328 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n" | 480 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n" |
329 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n", | 481 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n", |
330 translate[0], | 482 translate[0], |
331 translate[1], | 483 translate[1], |
332 translate[2], | 484 translate[2], |
333 scale[0], | 485 scale[0], |
334 scale[1], | 486 scale[1], |
335 scale[2], | 487 scale[2], |
336 skew[0], | 488 skew[0], |
337 skew[1], | 489 skew[1], |
338 skew[2], | 490 skew[2], |
339 perspective[0], | 491 perspective[0], |
340 perspective[1], | 492 perspective[1], |
341 perspective[2], | 493 perspective[2], |
342 perspective[3], | 494 perspective[3], |
343 quaternion[0], | 495 quaternion[0], |
344 quaternion[1], | 496 quaternion[1], |
345 quaternion[2], | 497 quaternion[2], |
346 quaternion[3]); | 498 quaternion[3]); |
347 } | 499 } |
348 | 500 |
349 } // namespace ui | 501 } // namespace ui |
OLD | NEW |