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

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: 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
17 SkMScalar Length3(SkMScalar v[3]) { 20 SkMScalar Length3(SkMScalar v[3]) {
18 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]), 21 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]),
19 SkMScalarToDouble(v[2])}; 22 SkMScalarToDouble(v[2])};
20 return SkDoubleToMScalar( 23 return SkDoubleToMScalar(
21 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2])); 24 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2]));
(...skipping 24 matching lines...) Expand all
46 49
47 void Cross3(SkMScalar out[3], SkMScalar a[3], SkMScalar b[3]) { 50 void Cross3(SkMScalar out[3], SkMScalar a[3], SkMScalar b[3]) {
48 SkMScalar x = a[1] * b[2] - a[2] * b[1]; 51 SkMScalar x = a[1] * b[2] - a[2] * b[1];
49 SkMScalar y = a[2] * b[0] - a[0] * b[2]; 52 SkMScalar y = a[2] * b[0] - a[0] * b[2];
50 SkMScalar z = a[0] * b[1] - a[1] * b[0]; 53 SkMScalar z = a[0] * b[1] - a[1] * b[0];
51 out[0] = x; 54 out[0] = x;
52 out[1] = y; 55 out[1] = y;
53 out[2] = z; 56 out[2] = z;
54 } 57 }
55 58
59 SkMScalar Round(SkMScalar n) {
60 return SkDoubleToMScalar(std::floor(SkMScalarToDouble(n) + 0.5));
61 }
62
56 // Taken from http://www.w3.org/TR/css3-transforms/. 63 // Taken from http://www.w3.org/TR/css3-transforms/.
57 bool Slerp(SkMScalar out[4], 64 bool Slerp(SkMScalar out[4],
58 const SkMScalar q1[4], 65 const SkMScalar q1[4],
59 const SkMScalar q2[4], 66 const SkMScalar q2[4],
60 double progress) { 67 double progress) {
61 double product = Dot<4>(q1, q2); 68 double product = Dot<4>(q1, q2);
62 69
63 // Clamp product to -1.0 <= product <= 1.0. 70 // Clamp product to -1.0 <= product <= 1.0.
64 product = std::min(std::max(product, -1.0), 1.0); 71 product = std::min(std::max(product, -1.0), 1.0);
65 72
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 return false; 108 return false;
102 109
103 SkMScalar scale = 1.0 / m.get(3, 3); 110 SkMScalar scale = 1.0 / m.get(3, 3);
104 for (int i = 0; i < 4; i++) 111 for (int i = 0; i < 4; i++)
105 for (int j = 0; j < 4; j++) 112 for (int j = 0; j < 4; j++)
106 m.set(i, j, m.get(i, j) * scale); 113 m.set(i, j, m.get(i, j) * scale);
107 114
108 return true; 115 return true;
109 } 116 }
110 117
118 SkMatrix44 BuildPerspectiveMatrix(const DecomposedTransform& decomp) {
119 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
120
121 for (int i = 0; i < 4; i++)
122 matrix.setDouble(3, i, decomp.perspective[i]);
123 return matrix;
124 }
125
126 SkMatrix44 BuildTranslationMatrix(const DecomposedTransform& decomp) {
127 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
128 // Implicitly calls matrix.setIdentity()
129 matrix.setTranslate(SkDoubleToMScalar(decomp.translate[0]),
130 SkDoubleToMScalar(decomp.translate[1]),
131 SkDoubleToMScalar(decomp.translate[2]));
132 return matrix;
133 }
134
135 SkMatrix44 BuildSnappedTranslationMatrix(DecomposedTransform decomp) {
136 decomp.translate[0] = Round(decomp.translate[0]);
137 decomp.translate[1] = Round(decomp.translate[1]);
138 decomp.translate[2] = Round(decomp.translate[2]);
139 return BuildTranslationMatrix(decomp);
140 }
141
142 SkMatrix44 BuildRotationMatrix(const DecomposedTransform& decomp) {
143 double x = decomp.quaternion[0];
144 double y = decomp.quaternion[1];
145 double z = decomp.quaternion[2];
146 double w = decomp.quaternion[3];
147
148 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
149
150 // Implicitly calls matrix.setIdentity()
151 matrix.set3x3(1.0 - 2.0 * (y * y + z * z),
152 2.0 * (x * y + z * w),
153 2.0 * (x * z - y * w),
154 2.0 * (x * y - z * w),
155 1.0 - 2.0 * (x * x + z * z),
156 2.0 * (y * z + x * w),
157 2.0 * (x * z + y * w),
158 2.0 * (y * z - x * w),
159 1.0 - 2.0 * (x * x + y * y));
160 return matrix;
161 }
162
163 SkMatrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) {
164 // Create snapped rotation.
165 SkMatrix44 rotation_matrix = BuildRotationMatrix(decomp);
166 for (int i = 0; i < 3; ++i) {
167 for (int j = 0; j < 3; ++j) {
168 SkMScalar value = rotation_matrix.get(i, j);
169 // Snap values to -1, 0 or 1.
170 if (value < -0.5f) {
171 value = -1.0f;
172 } else if (value > 0.5f) {
173 value = 1.0f;
174 } else {
175 value = 0.0f;
176 }
177 rotation_matrix.set(i, j, value);
178 }
179 }
180 return rotation_matrix;
181 }
182
183 SkMatrix44 BuildSkewMatrix(const DecomposedTransform& decomp) {
184 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
185
186 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor);
187 if (decomp.skew[2]) {
188 temp.setDouble(1, 2, decomp.skew[2]);
189 matrix.preConcat(temp);
190 }
191
192 if (decomp.skew[1]) {
193 temp.setDouble(1, 2, 0);
194 temp.setDouble(0, 2, decomp.skew[1]);
195 matrix.preConcat(temp);
196 }
197
198 if (decomp.skew[0]) {
199 temp.setDouble(0, 2, 0);
200 temp.setDouble(0, 1, decomp.skew[0]);
201 matrix.preConcat(temp);
202 }
203 return matrix;
204 }
205
206 SkMatrix44 BuildScaleMatrix(const DecomposedTransform& decomp) {
207 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
208 matrix.setScale(SkDoubleToMScalar(decomp.scale[0]),
209 SkDoubleToMScalar(decomp.scale[1]),
210 SkDoubleToMScalar(decomp.scale[2]));
211 return matrix;
212 }
213
214 SkMatrix44 BuildSnappedScaleMatrix(DecomposedTransform decomp) {
215 decomp.scale[0] = Round(decomp.scale[0]);
216 decomp.scale[1] = Round(decomp.scale[1]);
217 decomp.scale[2] = Round(decomp.scale[2]);
218 return BuildScaleMatrix(decomp);
219 }
220
221 Transform ComposeTransform(const SkMatrix44& perspective,
222 const SkMatrix44& translation,
223 const SkMatrix44& rotation,
224 const SkMatrix44& skew,
225 const SkMatrix44& scale) {
226 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
227
228 matrix.preConcat(perspective);
229 matrix.preConcat(translation);
230 matrix.preConcat(rotation);
231 matrix.preConcat(skew);
232 matrix.preConcat(scale);
233
234 Transform to_return;
235 to_return.matrix() = matrix;
236 return to_return;
237 }
238
239 bool CheckViewportPointMapsWithinOnePixel(const Point& point,
240 const Transform& transform) {
241 Point3F point_original(point);
242 Point3F point_transformed(point);
243
244 // Can't use TransformRect here since it would give us the axis-aligned
245 // bounding rect of the 4 points in the initial rectable which is not what we
246 // want.
247 transform.TransformPoint(&point_transformed);
248
249 if ((point_transformed - point_original).Length() > 1.f) {
250 // The changed distance should not be more than 1 pixel.
251 return false;
252 }
253 return true;
254 }
255
256 bool CheckTransformsMapsIntViewportWithinOnePixel(const Rect& viewport,
257 const Transform& original,
258 const Transform& snapped) {
259
260 Transform original_inv(Transform::kSkipInitialization);
261 bool invertible = true;
262 invertible &= original.GetInverse(&original_inv);
263 DCHECK(invertible) << "Non-invertible transform, cannot snap.";
264
265 Transform combined = snapped * original_inv;
266
267 return CheckViewportPointMapsWithinOnePixel(viewport.origin(), combined) &&
268 CheckViewportPointMapsWithinOnePixel(viewport.top_right(), combined) &&
269 CheckViewportPointMapsWithinOnePixel(viewport.bottom_left(),
270 combined) &&
271 CheckViewportPointMapsWithinOnePixel(viewport.bottom_right(),
272 combined);
273 }
274
111 } // namespace 275 } // namespace
112 276
113 Transform GetScaleTransform(const Point& anchor, float scale) { 277 Transform GetScaleTransform(const Point& anchor, float scale) {
114 Transform transform; 278 Transform transform;
115 transform.Translate(anchor.x() * (1 - scale), 279 transform.Translate(anchor.x() * (1 - scale),
116 anchor.y() * (1 - scale)); 280 anchor.y() * (1 - scale));
117 transform.Scale(scale, scale); 281 transform.Scale(scale, scale);
118 return transform; 282 return transform;
119 } 283 }
120 284
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 if (row[0][2] > row[2][0]) 427 if (row[0][2] > row[2][0])
264 decomp->quaternion[1] = -decomp->quaternion[1]; 428 decomp->quaternion[1] = -decomp->quaternion[1];
265 if (row[1][0] > row[0][1]) 429 if (row[1][0] > row[0][1])
266 decomp->quaternion[2] = -decomp->quaternion[2]; 430 decomp->quaternion[2] = -decomp->quaternion[2];
267 431
268 return true; 432 return true;
269 } 433 }
270 434
271 // Taken from http://www.w3.org/TR/css3-transforms/. 435 // Taken from http://www.w3.org/TR/css3-transforms/.
272 Transform ComposeTransform(const DecomposedTransform& decomp) { 436 Transform ComposeTransform(const DecomposedTransform& decomp) {
273 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); 437 SkMatrix44 perspective = BuildPerspectiveMatrix(decomp);
274 for (int i = 0; i < 4; i++) 438 SkMatrix44 translation = BuildTranslationMatrix(decomp);
275 matrix.set(3, i, decomp.perspective[i]); 439 SkMatrix44 rotation = BuildRotationMatrix(decomp);
440 SkMatrix44 skew = BuildSkewMatrix(decomp);
441 SkMatrix44 scale = BuildScaleMatrix(decomp);
276 442
277 matrix.preTranslate( 443 return ComposeTransform(perspective, translation, rotation, skew, scale);
278 decomp.translate[0], decomp.translate[1], decomp.translate[2]); 444 }
279 445
280 SkMScalar x = decomp.quaternion[0]; 446 bool SnapTransform(Transform* out,
281 SkMScalar y = decomp.quaternion[1]; 447 const Transform& transform,
282 SkMScalar z = decomp.quaternion[2]; 448 const Rect& viewport) {
283 SkMScalar w = decomp.quaternion[3]; 449 DecomposedTransform decomp;
450 DecomposeTransform(&decomp, transform);
284 451
285 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); 452 SkMatrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp);
286 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), 453 SkMatrix44 translation = BuildSnappedTranslationMatrix(decomp);
287 2.0 * (x * y + z * w), 454 SkMatrix44 scale = BuildSnappedScaleMatrix(decomp);
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 455
296 matrix.preConcat(rotation_matrix); 456 // Rebuild matrices for other unchanged components.
457 SkMatrix44 perspective = BuildPerspectiveMatrix(decomp);
297 458
298 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); 459 // Completely ignore the skew.
299 if (decomp.skew[2]) { 460 SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
300 temp.set(1, 2, decomp.skew[2]); 461
301 matrix.preConcat(temp); 462 // Get full tranform
463 Transform snapped =
464 ComposeTransform(perspective, translation, rotation_matrix, skew, scale);
465
466 // Verify that viewport is not moved unnaturally.
467 bool snappable =
468 CheckTransformsMapsIntViewportWithinOnePixel(viewport, transform, snapped);
469 if (snappable) {
470 *out = snapped;
302 } 471 }
303 472 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 } 473 }
322 474
323 std::string DecomposedTransform::ToString() const { 475 std::string DecomposedTransform::ToString() const {
324 return base::StringPrintf( 476 return base::StringPrintf(
325 "translate: %+0.4f %+0.4f %+0.4f\n" 477 "translate: %+0.4f %+0.4f %+0.4f\n"
326 "scale: %+0.4f %+0.4f %+0.4f\n" 478 "scale: %+0.4f %+0.4f %+0.4f\n"
327 "skew: %+0.4f %+0.4f %+0.4f\n" 479 "skew: %+0.4f %+0.4f %+0.4f\n"
328 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n" 480 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n"
329 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n", 481 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n",
330 translate[0], 482 translate[0],
331 translate[1], 483 translate[1],
332 translate[2], 484 translate[2],
333 scale[0], 485 scale[0],
334 scale[1], 486 scale[1],
335 scale[2], 487 scale[2],
336 skew[0], 488 skew[0],
337 skew[1], 489 skew[1],
338 skew[2], 490 skew[2],
339 perspective[0], 491 perspective[0],
340 perspective[1], 492 perspective[1],
341 perspective[2], 493 perspective[2],
342 perspective[3], 494 perspective[3],
343 quaternion[0], 495 quaternion[0],
344 quaternion[1], 496 quaternion[1],
345 quaternion[2], 497 quaternion[2],
346 quaternion[3]); 498 quaternion[3]);
347 } 499 }
348 500
349 } // namespace ui 501 } // 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