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

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