Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(397)

Side by Side Diff: ui/gfx/transform_util.cc

Issue 23444049: Implement transform snapping for gfx::Transforms. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add a test and fixed comments by vollick@. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/gfx/transform_util.h ('k') | ui/gfx/transform_util_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « ui/gfx/transform_util.h ('k') | ui/gfx/transform_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698