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

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: Remove std::round and make epsilon a float literal. Created 7 years, 2 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 "base/strings/stringprintf.h" 11 #include "base/strings/stringprintf.h"
11 #include "ui/gfx/point.h" 12 #include "ui/gfx/point.h"
13 #include "ui/gfx/point3_f.h"
14 #include "ui/gfx/rect.h"
12 15
13 namespace gfx { 16 namespace gfx {
14 17
15 namespace { 18 namespace {
16 19
20 // TODO(avallee): Move this somewhere appropriate in Skia.
21 // Taken from SkMatrix44
22 const float kNearIntEpsilon = 1e-8f;
23
24 bool NearInteger(float f) {
25 f = std::abs(f);
26 return (std::abs(f - std::floor(f + 0.5f)) < kNearIntEpsilon);
Ian Vollick 2013/10/18 14:54:46 We should be able to ditch this, I think. See belo
avallee 2013/10/21 15:15:44 Done.
27 }
28
17 SkMScalar Length3(SkMScalar v[3]) { 29 SkMScalar Length3(SkMScalar v[3]) {
18 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]), 30 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]),
19 SkMScalarToDouble(v[2])}; 31 SkMScalarToDouble(v[2])};
20 return SkDoubleToMScalar( 32 return SkDoubleToMScalar(
21 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2])); 33 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2]));
22 } 34 }
23 35
24 void Scale3(SkMScalar v[3], SkMScalar scale) { 36 void Scale3(SkMScalar v[3], SkMScalar scale) {
25 for (int i = 0; i < 3; ++i) 37 for (int i = 0; i < 3; ++i)
26 v[i] *= scale; 38 v[i] *= scale;
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 return false; 113 return false;
102 114
103 SkMScalar scale = 1.0 / m.get(3, 3); 115 SkMScalar scale = 1.0 / m.get(3, 3);
104 for (int i = 0; i < 4; i++) 116 for (int i = 0; i < 4; i++)
105 for (int j = 0; j < 4; j++) 117 for (int j = 0; j < 4; j++)
106 m.set(i, j, m.get(i, j) * scale); 118 m.set(i, j, m.get(i, j) * scale);
107 119
108 return true; 120 return true;
109 } 121 }
110 122
123 void BuildPerspectiveMatrix(SkMatrix44* matrix,
124 const DecomposedTransform& decomp) {
125 matrix->setIdentity();
126
127 for (int i = 0; i < 4; i++)
128 matrix->setDouble(3, i, decomp.perspective[i]);
129 }
130
131 void BuildTranslationMatrix(SkMatrix44 * matrix,
132 const DecomposedTransform& decomp) {
133 matrix->setTranslate(SkDoubleToMScalar(decomp.translate[0]),
134 SkDoubleToMScalar(decomp.translate[1]),
135 SkDoubleToMScalar(decomp.translate[2]));
136 }
137
138 void BuildRotationMatrix(SkMatrix44* matrix,
139 const DecomposedTransform& decomp) {
140 double x = decomp.quaternion[0];
141 double y = decomp.quaternion[1];
142 double z = decomp.quaternion[2];
143 double w = decomp.quaternion[3];
144
145 matrix->set3x3(1.0 - 2.0 * (y * y + z * z),
146 2.0 * (x * y + z * w),
147 2.0 * (x * z - y * w),
148 2.0 * (x * y - z * w),
149 1.0 - 2.0 * (x * x + z * z),
150 2.0 * (y * z + x * w),
151 2.0 * (x * z + y * w),
152 2.0 * (y * z - x * w),
153 1.0 - 2.0 * (x * x + y * y));
154 }
155
156 SkMatrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) {
157 // Create snapped rotation.
158 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor);
159 BuildRotationMatrix(&rotation_matrix, decomp);
160 for (int i = 0; i < 3; ++i) {
161 for (int j = 0; j < 3; ++j) {
162 SkMScalar value = rotation_matrix.get(i, j);
163 // Snap values to -1, 0 or 1.
164 if (value < -0.5f) {
165 value = -1.0f;
166 } else if (value > 0.5f) {
167 value = 1.0f;
168 } else {
169 value = 0.0f;
170 }
171 rotation_matrix.set(i, j, value);
172 }
173 }
174 return rotation_matrix;
175 }
176
177 void BuildSkewMatrix(SkMatrix44* matrix, const DecomposedTransform& decomp) {
178 matrix->setIdentity();
179
180 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor);
181 if (decomp.skew[2]) {
182 temp.setDouble(1, 2, decomp.skew[2]);
183 matrix->preConcat(temp);
184 }
185
186 if (decomp.skew[1]) {
187 temp.setDouble(1, 2, 0);
188 temp.setDouble(0, 2, decomp.skew[1]);
189 matrix->preConcat(temp);
190 }
191
192 if (decomp.skew[0]) {
193 temp.setDouble(0, 2, 0);
194 temp.setDouble(0, 1, decomp.skew[0]);
195 matrix->preConcat(temp);
196 }
197 }
198
199 void BuildScaleMatrix(SkMatrix44* matrix, const DecomposedTransform& decomp) {
200 matrix->setIdentity();
201 matrix->setScale(SkDoubleToMScalar(decomp.scale[0]),
202 SkDoubleToMScalar(decomp.scale[1]),
203 SkDoubleToMScalar(decomp.scale[2]));
204 }
205
206 Transform ComposeTransform(const SkMatrix44& perspective,
207 const SkMatrix44& translation,
208 const SkMatrix44& rotation,
209 const SkMatrix44& skew,
210 const SkMatrix44& scale) {
211 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
212
213 matrix.preConcat(perspective);
214 matrix.preConcat(translation);
215 matrix.preConcat(rotation);
216 matrix.preConcat(skew);
217 matrix.preConcat(scale);
218
219 Transform to_return;
220 to_return.matrix() = matrix;
221 return to_return;
222 }
223
224 bool CheckViewportPointMapsWithinAPixel(const Point& point,
225 const Transform& transform_a,
226 const Transform& transform_b) {
227 Point3F point_a, point_b;
228 point_a = point_b = Point3F(point);
229
230 // Can't use TransformRect here since it would give us the axis-aligned
231 // bounding rect of the 4 points in the initial rectable which is not what we
232 // want.
233 transform_a.TransformPoint(&point_a);
234 transform_b.TransformPoint(&point_b);
235
236 if (!(NearInteger(point_b.x()) && NearInteger(point_b.y()))) {
Ian Vollick 2013/10/18 14:54:46 If we snap everything (not just rotation), then we
avallee 2013/10/21 15:15:44 Gone. Implemented full snapping, (skew snapped to
237 // Integers should get mapped back into integer points.
238 return false;
239 }
240
241 if ((point_b - point_a).Length() > 1.0) {
242 // The changed distance should not be more than 1 pixel.
243 return false;
244 }
245 return true;
246 }
247
248 bool CheckTransformsMapsIntViewportWithinAPixel(const Rect& viewport,
249 const Transform& original,
250 const Transform& snapped) {
251
252 Transform original_inv(Transform::kSkipInitialization);
253 Transform snapped_inv(Transform::kSkipInitialization);
254 bool invertible = true;
255 invertible &= original.GetInverse(&original_inv);
256 invertible &= snapped.GetInverse(&snapped_inv);
257 DCHECK(invertible) << "Non-invertible transform, cannot snap.";
258
259 return CheckViewportPointMapsWithinAPixel(
260 viewport.origin(), original_inv, snapped_inv) &&
261 CheckViewportPointMapsWithinAPixel(
262 viewport.top_right(), original_inv, snapped_inv) &&
263 CheckViewportPointMapsWithinAPixel(
264 viewport.bottom_left(), original_inv, snapped_inv) &&
265 CheckViewportPointMapsWithinAPixel(
266 viewport.bottom_right(), original_inv, snapped_inv);
267 }
268
111 } // namespace 269 } // namespace
112 270
113 Transform GetScaleTransform(const Point& anchor, float scale) { 271 Transform GetScaleTransform(const Point& anchor, float scale) {
114 Transform transform; 272 Transform transform;
115 transform.Translate(anchor.x() * (1 - scale), 273 transform.Translate(anchor.x() * (1 - scale),
116 anchor.y() * (1 - scale)); 274 anchor.y() * (1 - scale));
117 transform.Scale(scale, scale); 275 transform.Scale(scale, scale);
118 return transform; 276 return transform;
119 } 277 }
120 278
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 if (row[0][2] > row[2][0]) 421 if (row[0][2] > row[2][0])
264 decomp->quaternion[1] = -decomp->quaternion[1]; 422 decomp->quaternion[1] = -decomp->quaternion[1];
265 if (row[1][0] > row[0][1]) 423 if (row[1][0] > row[0][1])
266 decomp->quaternion[2] = -decomp->quaternion[2]; 424 decomp->quaternion[2] = -decomp->quaternion[2];
267 425
268 return true; 426 return true;
269 } 427 }
270 428
271 // Taken from http://www.w3.org/TR/css3-transforms/. 429 // Taken from http://www.w3.org/TR/css3-transforms/.
272 Transform ComposeTransform(const DecomposedTransform& decomp) { 430 Transform ComposeTransform(const DecomposedTransform& decomp) {
273 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); 431 SkMatrix44 perspective(SkMatrix44::kUninitialized_Constructor);
274 for (int i = 0; i < 4; i++) 432 BuildPerspectiveMatrix(&perspective, decomp);
275 matrix.set(3, i, decomp.perspective[i]);
276 433
277 matrix.preTranslate( 434 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor);
278 decomp.translate[0], decomp.translate[1], decomp.translate[2]); 435 BuildTranslationMatrix(&translation, decomp);
279 436
280 SkMScalar x = decomp.quaternion[0]; 437 SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor);
281 SkMScalar y = decomp.quaternion[1]; 438 BuildRotationMatrix(&rotation, decomp);
282 SkMScalar z = decomp.quaternion[2];
283 SkMScalar w = decomp.quaternion[3];
284 439
285 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); 440 SkMatrix44 skew(SkMatrix44::kUninitialized_Constructor);
286 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), 441 BuildSkewMatrix(&skew, decomp);
287 2.0 * (x * y + z * w),
288 2.0 * (x * z - y * w),
289 2.0 * (x * y - z * w),
290 1.0 - 2.0 * (x * x + z * z),
291 2.0 * (y * z + x * w),
292 2.0 * (x * z + y * w),
293 2.0 * (y * z - x * w),
294 1.0 - 2.0 * (x * x + y * y));
295 442
296 matrix.preConcat(rotation_matrix); 443 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor);
444 BuildScaleMatrix(&scale, decomp);
297 445
298 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); 446 return ComposeTransform(perspective, translation, rotation, skew, scale);
299 if (decomp.skew[2]) { 447 }
300 temp.set(1, 2, decomp.skew[2]); 448
301 matrix.preConcat(temp); 449 bool SnapRotation(Transform* out,
Ian Vollick 2013/10/18 14:54:46 s/SnapRotation/SnapTransform/ and snap everything
avallee 2013/10/21 15:15:44 Done.
450 const Transform& transform,
451 const Rect& viewport) {
452 DecomposedTransform decomp;
453 DecomposeTransform(&decomp, transform);
454
455 SkMatrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp);
456
457 // Rebuild matrices for other unchanged components.
458 SkMatrix44 perspective(SkMatrix44::kUninitialized_Constructor);
459 BuildPerspectiveMatrix(&perspective, decomp);
460
461 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor);
462 BuildTranslationMatrix(&translation, decomp);
463
464 SkMatrix44 skew(SkMatrix44::kUninitialized_Constructor);
465 BuildSkewMatrix(&skew, decomp);
466
467 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor);
468 BuildScaleMatrix(&scale, decomp);
469
470 // Get full tranform
471 Transform snapped =
472 ComposeTransform(perspective, translation, rotation_matrix, skew, scale);
473
474 // Verify that viewport is not moved unnaturally.
475 bool snappable =
476 CheckTransformsMapsIntViewportWithinAPixel(viewport, transform, snapped);
477 if (snappable) {
478 *out = snapped;
302 } 479 }
303 480 return snappable;
304 if (decomp.skew[1]) {
305 temp.set(1, 2, 0);
306 temp.set(0, 2, decomp.skew[1]);
307 matrix.preConcat(temp);
308 }
309
310 if (decomp.skew[0]) {
311 temp.set(0, 2, 0);
312 temp.set(0, 1, decomp.skew[0]);
313 matrix.preConcat(temp);
314 }
315
316 matrix.preScale(decomp.scale[0], decomp.scale[1], decomp.scale[2]);
317
318 Transform to_return;
319 to_return.matrix() = matrix;
320 return to_return;
321 } 481 }
322 482
323 std::string DecomposedTransform::ToString() const { 483 std::string DecomposedTransform::ToString() const {
324 return base::StringPrintf( 484 return base::StringPrintf(
325 "translate: %+0.4f %+0.4f %+0.4f\n" 485 "translate: %+0.4f %+0.4f %+0.4f\n"
326 "scale: %+0.4f %+0.4f %+0.4f\n" 486 "scale: %+0.4f %+0.4f %+0.4f\n"
327 "skew: %+0.4f %+0.4f %+0.4f\n" 487 "skew: %+0.4f %+0.4f %+0.4f\n"
328 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n" 488 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n"
329 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n", 489 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n",
330 translate[0], 490 translate[0],
331 translate[1], 491 translate[1],
332 translate[2], 492 translate[2],
333 scale[0], 493 scale[0],
334 scale[1], 494 scale[1],
335 scale[2], 495 scale[2],
336 skew[0], 496 skew[0],
337 skew[1], 497 skew[1],
338 skew[2], 498 skew[2],
339 perspective[0], 499 perspective[0],
340 perspective[1], 500 perspective[1],
341 perspective[2], 501 perspective[2],
342 perspective[3], 502 perspective[3],
343 quaternion[0], 503 quaternion[0],
344 quaternion[1], 504 quaternion[1],
345 quaternion[2], 505 quaternion[2],
346 quaternion[3]); 506 quaternion[3]);
347 } 507 }
348 508
349 } // namespace ui 509 } // 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