Index: ui/gfx/transform_util.cc |
diff --git a/ui/gfx/transform_util.cc b/ui/gfx/transform_util.cc |
index 90c8b56e5547937f76686bfa6934064e74207112..721bf4fc7eca81263db43d936d19962f41687bda 100644 |
--- a/ui/gfx/transform_util.cc |
+++ b/ui/gfx/transform_util.cc |
@@ -6,12 +6,22 @@ |
#include <cmath> |
+#include "base/logging.h" |
#include "ui/gfx/point.h" |
+#include "ui/gfx/point3_f.h" |
+#include "ui/gfx/rect_f.h" |
namespace gfx { |
namespace { |
+// Taken from SkMatrix44 |
+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.
|
+ |
+bool NearInteger(float f) { |
+ return (std::abs(f-std::round(f)) < kNearIntEpsilon); |
+} |
+ |
double Length3(double v[3]) { |
return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); |
} |
@@ -264,20 +274,11 @@ bool DecomposeTransform(DecomposedTransform* decomp, |
return true; |
} |
-// Taken from http://www.w3.org/TR/css3-transforms/. |
-Transform ComposeTransform(const DecomposedTransform& decomp) { |
- SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); |
- for (int i = 0; i < 4; i++) |
- matrix.setDouble(3, i, decomp.perspective[i]); |
- |
- matrix.preTranslate(SkDoubleToMScalar(decomp.translate[0]), |
- SkDoubleToMScalar(decomp.translate[1]), |
- SkDoubleToMScalar(decomp.translate[2])); |
- |
- double x = decomp.quaternion[0]; |
- double y = decomp.quaternion[1]; |
- double z = decomp.quaternion[2]; |
- double w = decomp.quaternion[3]; |
+SkMatrix44 QuaternionToRotationMatrix(const double (& quaternion)[4]) { |
+ double x = quaternion[0]; |
+ double y = quaternion[1]; |
+ double z = quaternion[2]; |
+ double w = quaternion[3]; |
SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); |
rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), |
@@ -290,33 +291,130 @@ Transform ComposeTransform(const DecomposedTransform& decomp) { |
2.0 * (y * z - x * w), |
1.0 - 2.0 * (x * x + y * y)); |
- matrix.preConcat(rotation_matrix); |
+ return rotation_matrix; |
+} |
+ |
+void ApplyPerspectiveTranslation(SkMatrix44* matrix, |
+ const DecomposedTransform& decomp) { |
+ for (int i = 0; i < 4; i++) |
+ 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
|
+ |
+ matrix->preTranslate(SkDoubleToMScalar(decomp.translate[0]), |
+ SkDoubleToMScalar(decomp.translate[1]), |
+ SkDoubleToMScalar(decomp.translate[2])); |
+} |
+void ApplySkewScale(SkMatrix44* matrix, |
+ const DecomposedTransform& decomp) { |
SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); |
if (decomp.skew[2]) { |
temp.setDouble(1, 2, decomp.skew[2]); |
- matrix.preConcat(temp); |
+ matrix->preConcat(temp); |
} |
if (decomp.skew[1]) { |
temp.setDouble(1, 2, 0); |
temp.setDouble(0, 2, decomp.skew[1]); |
- matrix.preConcat(temp); |
+ matrix->preConcat(temp); |
} |
if (decomp.skew[0]) { |
temp.setDouble(0, 2, 0); |
temp.setDouble(0, 1, decomp.skew[0]); |
- matrix.preConcat(temp); |
+ matrix->preConcat(temp); |
} |
Ian Vollick
2013/09/11 12:19:48
Same with skew and scale.
avallee
2013/09/14 01:32:25
Done.
|
- matrix.preScale(SkDoubleToMScalar(decomp.scale[0]), |
- SkDoubleToMScalar(decomp.scale[1]), |
- SkDoubleToMScalar(decomp.scale[2])); |
+ matrix->preScale(SkDoubleToMScalar(decomp.scale[0]), |
+ SkDoubleToMScalar(decomp.scale[1]), |
+ SkDoubleToMScalar(decomp.scale[2])); |
+} |
+ |
+// Taken from http://www.w3.org/TR/css3-transforms/. |
+Transform ComposeTransform(const DecomposedTransform& decomp) { |
+ SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); |
+ ApplyPerspectiveTranslation(&matrix, decomp); |
+ |
+ matrix.preConcat(QuaternionToRotationMatrix(decomp.quaternion)); |
+ |
+ ApplySkewScale(&matrix, decomp); |
Transform to_return; |
to_return.matrix() = matrix; |
return to_return; |
} |
+Transform ComposeWithRotation(const DecomposedTransform& decomp, |
+ const SkMatrix44& rotation_matrix) { |
+ SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); |
+ ApplyPerspectiveTranslation(&matrix, decomp); |
+ matrix.preConcat(rotation_matrix); |
+ |
+ ApplySkewScale(&matrix, decomp); |
+ |
+ Transform to_return; |
+ to_return.matrix() = matrix; |
+ return to_return; |
+} |
+ |
+bool CheckTansformPoint(const PointF& point, const Transform& transform_a, |
+ const Transform& transform_b) { |
+ Point3F point_a, point_b; |
+ point_a = point_b = Point3F(point.x(), point.y(), 0.f); |
+ |
+ // Can't use TransformRect here since it would give us the axis-aligned |
+ // bounding rect of the 4 points in the initial rectable which is not what we |
+ // want. |
+ bool invertible = true; |
+ invertible &= transform_a.TransformPointReverse(&point_a); |
+ invertible &= transform_b.TransformPointReverse(&point_b); |
+ DCHECK(invertible) << "Non-invertible transform, cannot snap."; |
+ |
+ if (!(NearInteger(point_b.x()) && NearInteger(point_b.y()))) { |
+ // Integers should get mapped back into integer points. |
+ return false; |
+ } |
+ |
+ if ((point_b - point_a).Length() > 1.0) { |
+ // The changed distance should not be more than 1 pixel. |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+bool SnapRotation(DecomposedTransform* out, const DecomposedTransform& in, |
+ const RectF& viewport) { |
+ |
+ // Create snapped rotation. |
+ SkMatrix44 rotation_matrix = QuaternionToRotationMatrix(in.quaternion); |
+ for (int i = 0; i < 3; ++i) { |
+ for (int j = 0; j < 3; ++j) { |
+ SkMScalar value = rotation_matrix.get(i, j); |
+ // Snap values to -1, 0 or 1. |
+ if (value < -0.5f) { |
+ value = -1.0f; |
+ } else if (value > 0.5f) { |
+ value = 1.0f; |
+ } else { |
+ value = 0.0f; |
+ } |
+ rotation_matrix.set(i, j, value); |
+ } |
+ } |
+ |
+ // Get full tranforms |
+ 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.
|
+ 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.
|
+ |
+ // Verify that viewport is not moved unnaturally. |
+ |
+ bool snappable = CheckTansformPoint(viewport.origin(), original, snapped) && |
+ CheckTansformPoint(viewport.top_right(), original, snapped) && |
+ CheckTansformPoint(viewport.bottom_left(), original, snapped) && |
+ CheckTansformPoint(viewport.bottom_right(), original, snapped); |
+ if (snappable) { |
+ DecomposeTransform(out, snapped); |
+ } |
+ return snappable; |
+} |
+ |
} // namespace ui |