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 |