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

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: Fix test case to back-map correctly. Address danajk@ comments. 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
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 <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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698