Chromium Code Reviews| Index: ui/gfx/transform_util.cc |
| diff --git a/ui/gfx/transform_util.cc b/ui/gfx/transform_util.cc |
| index 90c8b56e5547937f76686bfa6934064e74207112..f9ddb32f9bfe8e434f6bf385744eeb1805e56aa8 100644 |
| --- a/ui/gfx/transform_util.cc |
| +++ b/ui/gfx/transform_util.cc |
| @@ -6,12 +6,21 @@ |
| #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 { |
| +const float NEAR_INT_ESPILON = 1e-10; |
|
danakj
2013/09/10 20:29:51
kNearIntEpsilon
I will also note this is at least
avallee
2013/09/10 21:30:13
I'll unify with what's in transform.cc.
Ian Vollick
2013/09/11 12:19:48
This is a bummer, for sure. Is it possible to shar
|
| + |
| +bool NearInteger(float f) { |
| + return (abs(f-round(f)) < NEAR_INT_ESPILON); |
|
danakj
2013/09/10 20:29:51
std::abs
std::round
avallee
2013/09/10 21:30:13
Done.
Ian Vollick
2013/09/11 12:19:48
I think we want the relative error, not the absolu
|
| +} |
| + |
| double Length3(double v[3]) { |
| return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); |
| } |
| @@ -264,20 +273,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 +290,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]); |
| + |
| + 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); |
| } |
| - 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.0); |
|
danakj
2013/09/10 20:29:51
0.f
avallee
2013/09/10 21:30:13
Done.
|
| + |
| + // 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; |
|
danakj
2013/09/10 20:29:51
true || anything = true. did you mean false?
avallee
2013/09/10 21:30:13
Meant and_equals.
|
| + 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) { |
|
danakj
2013/09/10 20:29:51
spaces around operators
avallee
2013/09/10 21:30:13
Done.
|
| + // 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.5) { |
|
danakj
2013/09/10 20:29:51
treat SkMScalar as a float. so compare to -0.5f. s
avallee
2013/09/10 21:30:13
uOn 2013/09/10 20:29:51, danakj wrote:
|
| + value = -1.0; |
| + } else if (value > 0.5) { |
| + value = 1.0; |
| + } else { |
| + value = 0.0; |
| + } |
| + rotation_matrix.set(i, j, value); |
| + } |
| + } |
| + |
| + // Get full tranforms |
| + Transform original = ComposeTransform(in); |
| + Transform snapped = ComposeWithRotation(in, rotation_matrix); |
| + |
| + // 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 |