blob: 5c9fb50ff159b948cd9911a247a830258c65a652 [file] [log] [blame]
[email protected]665e2b72012-03-14 17:06:591// 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
3// found in the LICENSE file.
4
5#include "ui/gfx/transform_util.h"
6
[email protected]7153e5d32013-09-12 21:50:107#include <algorithm>
[email protected]2fcafa02012-11-15 01:12:558#include <cmath>
bruthig8a260732014-11-24 23:32:029#include <string>
[email protected]2fcafa02012-11-15 01:12:5510
[email protected]dd31d492013-10-24 22:33:2511#include "base/logging.h"
[email protected]6138db702013-09-25 03:25:0512#include "base/strings/stringprintf.h"
tfarina655f81d2014-12-23 02:38:5013#include "ui/gfx/geometry/point.h"
14#include "ui/gfx/geometry/point3_f.h"
tfarina3b0452d2014-12-31 15:20:0915#include "ui/gfx/geometry/rect.h"
[email protected]665e2b72012-03-14 17:06:5916
[email protected]0f0453e2012-10-14 18:15:3517namespace gfx {
[email protected]665e2b72012-03-14 17:06:5918
[email protected]2fcafa02012-11-15 01:12:5519namespace {
20
[email protected]803f6b52013-09-12 00:51:2621SkMScalar Length3(SkMScalar v[3]) {
[email protected]6138db702013-09-25 03:25:0522 double vd[3] = {SkMScalarToDouble(v[0]), SkMScalarToDouble(v[1]),
23 SkMScalarToDouble(v[2])};
24 return SkDoubleToMScalar(
25 std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2]));
[email protected]2fcafa02012-11-15 01:12:5526}
27
[email protected]2fcafa02012-11-15 01:12:5528template <int n>
[email protected]803f6b52013-09-12 00:51:2629SkMScalar Dot(const SkMScalar* a, const SkMScalar* b) {
[email protected]6138db702013-09-25 03:25:0530 double total = 0.0;
[email protected]2fcafa02012-11-15 01:12:5531 for (int i = 0; i < n; ++i)
[email protected]6138db702013-09-25 03:25:0532 total += a[i] * b[i];
33 return SkDoubleToMScalar(total);
[email protected]2fcafa02012-11-15 01:12:5534}
35
36template <int n>
[email protected]803f6b52013-09-12 00:51:2637void Combine(SkMScalar* out,
38 const SkMScalar* a,
39 const SkMScalar* b,
[email protected]6138db702013-09-25 03:25:0540 double scale_a,
41 double scale_b) {
[email protected]2fcafa02012-11-15 01:12:5542 for (int i = 0; i < n; ++i)
[email protected]6138db702013-09-25 03:25:0543 out[i] = SkDoubleToMScalar(a[i] * scale_a + b[i] * scale_b);
[email protected]2fcafa02012-11-15 01:12:5544}
45
[email protected]803f6b52013-09-12 00:51:2646void Cross3(SkMScalar out[3], SkMScalar a[3], SkMScalar b[3]) {
47 SkMScalar x = a[1] * b[2] - a[2] * b[1];
48 SkMScalar y = a[2] * b[0] - a[0] * b[2];
49 SkMScalar z = a[0] * b[1] - a[1] * b[0];
[email protected]2fcafa02012-11-15 01:12:5550 out[0] = x;
51 out[1] = y;
52 out[2] = z;
53}
54
[email protected]dd31d492013-10-24 22:33:2555SkMScalar Round(SkMScalar n) {
56 return SkDoubleToMScalar(std::floor(SkMScalarToDouble(n) + 0.5));
57}
58
[email protected]2fcafa02012-11-15 01:12:5559// Taken from http://www.w3.org/TR/css3-transforms/.
[email protected]803f6b52013-09-12 00:51:2660bool Slerp(SkMScalar out[4],
61 const SkMScalar q1[4],
62 const SkMScalar q2[4],
[email protected]6138db702013-09-25 03:25:0563 double progress) {
64 double product = Dot<4>(q1, q2);
[email protected]2fcafa02012-11-15 01:12:5565
66 // Clamp product to -1.0 <= product <= 1.0.
[email protected]6138db702013-09-25 03:25:0567 product = std::min(std::max(product, -1.0), 1.0);
[email protected]2fcafa02012-11-15 01:12:5568
[email protected]19f0f5c2013-05-09 00:53:4969 // Interpolate angles along the shortest path. For example, to interpolate
70 // between a 175 degree angle and a 185 degree angle, interpolate along the
71 // 10 degree path from 175 to 185, rather than along the 350 degree path in
72 // the opposite direction. This matches WebKit's implementation but not
73 // the current W3C spec. Fixing the spec to match this approach is discussed
74 // at:
75 // http://lists.w3.org/Archives/Public/www-style/2013May/0131.html
[email protected]6138db702013-09-25 03:25:0576 double scale1 = 1.0;
[email protected]19f0f5c2013-05-09 00:53:4977 if (product < 0) {
78 product = -product;
[email protected]6138db702013-09-25 03:25:0579 scale1 = -1.0;
[email protected]19f0f5c2013-05-09 00:53:4980 }
81
[email protected]6138db702013-09-25 03:25:0582 const double epsilon = 1e-5;
83 if (std::abs(product - 1.0) < epsilon) {
[email protected]2fcafa02012-11-15 01:12:5584 for (int i = 0; i < 4; ++i)
85 out[i] = q1[i];
86 return true;
87 }
88
[email protected]6138db702013-09-25 03:25:0589 double denom = std::sqrt(1.0 - product * product);
90 double theta = std::acos(product);
91 double w = std::sin(progress * theta) * (1.0 / denom);
[email protected]2fcafa02012-11-15 01:12:5592
[email protected]19f0f5c2013-05-09 00:53:4993 scale1 *= std::cos(progress * theta) - product * w;
[email protected]6138db702013-09-25 03:25:0594 double scale2 = w;
[email protected]2fcafa02012-11-15 01:12:5595 Combine<4>(out, q1, q2, scale1, scale2);
96
97 return true;
98}
99
100// Returns false if the matrix cannot be normalized.
101bool Normalize(SkMatrix44& m) {
[email protected]803f6b52013-09-12 00:51:26102 if (m.get(3, 3) == 0.0)
[email protected]2fcafa02012-11-15 01:12:55103 // Cannot normalize.
104 return false;
105
pkastingeb00e7e2014-11-04 22:30:28106 SkMScalar scale = SK_MScalar1 / m.get(3, 3);
[email protected]2fcafa02012-11-15 01:12:55107 for (int i = 0; i < 4; i++)
108 for (int j = 0; j < 4; j++)
[email protected]803f6b52013-09-12 00:51:26109 m.set(i, j, m.get(i, j) * scale);
[email protected]2fcafa02012-11-15 01:12:55110
111 return true;
112}
113
[email protected]dd31d492013-10-24 22:33:25114SkMatrix44 BuildPerspectiveMatrix(const DecomposedTransform& decomp) {
115 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
116
117 for (int i = 0; i < 4; i++)
118 matrix.setDouble(3, i, decomp.perspective[i]);
119 return matrix;
120}
121
122SkMatrix44 BuildTranslationMatrix(const DecomposedTransform& decomp) {
123 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
124 // Implicitly calls matrix.setIdentity()
125 matrix.setTranslate(SkDoubleToMScalar(decomp.translate[0]),
126 SkDoubleToMScalar(decomp.translate[1]),
127 SkDoubleToMScalar(decomp.translate[2]));
128 return matrix;
129}
130
131SkMatrix44 BuildSnappedTranslationMatrix(DecomposedTransform decomp) {
132 decomp.translate[0] = Round(decomp.translate[0]);
133 decomp.translate[1] = Round(decomp.translate[1]);
134 decomp.translate[2] = Round(decomp.translate[2]);
135 return BuildTranslationMatrix(decomp);
136}
137
138SkMatrix44 BuildRotationMatrix(const DecomposedTransform& decomp) {
139 double x = decomp.quaternion[0];
140 double y = decomp.quaternion[1];
141 double z = decomp.quaternion[2];
142 double w = decomp.quaternion[3];
143
144 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
145
146 // Implicitly calls matrix.setIdentity()
pkastingeb00e7e2014-11-04 22:30:28147 matrix.set3x3(SkDoubleToMScalar(1.0 - 2.0 * (y * y + z * z)),
148 SkDoubleToMScalar(2.0 * (x * y + z * w)),
149 SkDoubleToMScalar(2.0 * (x * z - y * w)),
150 SkDoubleToMScalar(2.0 * (x * y - z * w)),
151 SkDoubleToMScalar(1.0 - 2.0 * (x * x + z * z)),
152 SkDoubleToMScalar(2.0 * (y * z + x * w)),
153 SkDoubleToMScalar(2.0 * (x * z + y * w)),
154 SkDoubleToMScalar(2.0 * (y * z - x * w)),
155 SkDoubleToMScalar(1.0 - 2.0 * (x * x + y * y)));
[email protected]dd31d492013-10-24 22:33:25156 return matrix;
157}
158
159SkMatrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) {
160 // Create snapped rotation.
161 SkMatrix44 rotation_matrix = BuildRotationMatrix(decomp);
162 for (int i = 0; i < 3; ++i) {
163 for (int j = 0; j < 3; ++j) {
164 SkMScalar value = rotation_matrix.get(i, j);
165 // Snap values to -1, 0 or 1.
166 if (value < -0.5f) {
167 value = -1.0f;
168 } else if (value > 0.5f) {
169 value = 1.0f;
170 } else {
171 value = 0.0f;
172 }
173 rotation_matrix.set(i, j, value);
174 }
175 }
176 return rotation_matrix;
177}
178
179SkMatrix44 BuildSkewMatrix(const DecomposedTransform& decomp) {
180 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
181
182 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor);
183 if (decomp.skew[2]) {
184 temp.setDouble(1, 2, decomp.skew[2]);
185 matrix.preConcat(temp);
186 }
187
188 if (decomp.skew[1]) {
189 temp.setDouble(1, 2, 0);
190 temp.setDouble(0, 2, decomp.skew[1]);
191 matrix.preConcat(temp);
192 }
193
194 if (decomp.skew[0]) {
195 temp.setDouble(0, 2, 0);
196 temp.setDouble(0, 1, decomp.skew[0]);
197 matrix.preConcat(temp);
198 }
199 return matrix;
200}
201
202SkMatrix44 BuildScaleMatrix(const DecomposedTransform& decomp) {
203 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
204 matrix.setScale(SkDoubleToMScalar(decomp.scale[0]),
205 SkDoubleToMScalar(decomp.scale[1]),
206 SkDoubleToMScalar(decomp.scale[2]));
207 return matrix;
208}
209
210SkMatrix44 BuildSnappedScaleMatrix(DecomposedTransform decomp) {
211 decomp.scale[0] = Round(decomp.scale[0]);
212 decomp.scale[1] = Round(decomp.scale[1]);
213 decomp.scale[2] = Round(decomp.scale[2]);
214 return BuildScaleMatrix(decomp);
215}
216
217Transform ComposeTransform(const SkMatrix44& perspective,
218 const SkMatrix44& translation,
219 const SkMatrix44& rotation,
220 const SkMatrix44& skew,
221 const SkMatrix44& scale) {
222 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
223
224 matrix.preConcat(perspective);
225 matrix.preConcat(translation);
226 matrix.preConcat(rotation);
227 matrix.preConcat(skew);
228 matrix.preConcat(scale);
229
230 Transform to_return;
231 to_return.matrix() = matrix;
232 return to_return;
233}
234
235bool CheckViewportPointMapsWithinOnePixel(const Point& point,
236 const Transform& transform) {
237 Point3F point_original(point);
238 Point3F point_transformed(point);
239
240 // Can't use TransformRect here since it would give us the axis-aligned
241 // bounding rect of the 4 points in the initial rectable which is not what we
242 // want.
243 transform.TransformPoint(&point_transformed);
244
245 if ((point_transformed - point_original).Length() > 1.f) {
246 // The changed distance should not be more than 1 pixel.
247 return false;
248 }
249 return true;
250}
251
252bool CheckTransformsMapsIntViewportWithinOnePixel(const Rect& viewport,
253 const Transform& original,
254 const Transform& snapped) {
255
256 Transform original_inv(Transform::kSkipInitialization);
257 bool invertible = true;
258 invertible &= original.GetInverse(&original_inv);
259 DCHECK(invertible) << "Non-invertible transform, cannot snap.";
260
261 Transform combined = snapped * original_inv;
262
263 return CheckViewportPointMapsWithinOnePixel(viewport.origin(), combined) &&
264 CheckViewportPointMapsWithinOnePixel(viewport.top_right(), combined) &&
265 CheckViewportPointMapsWithinOnePixel(viewport.bottom_left(),
266 combined) &&
267 CheckViewportPointMapsWithinOnePixel(viewport.bottom_right(),
268 combined);
269}
270
[email protected]2fcafa02012-11-15 01:12:55271} // namespace
272
[email protected]0f0453e2012-10-14 18:15:35273Transform GetScaleTransform(const Point& anchor, float scale) {
274 Transform transform;
[email protected]f7c321eb2012-11-26 20:13:08275 transform.Translate(anchor.x() * (1 - scale),
276 anchor.y() * (1 - scale));
277 transform.Scale(scale, scale);
[email protected]665e2b72012-03-14 17:06:59278 return transform;
279}
280
[email protected]2fcafa02012-11-15 01:12:55281DecomposedTransform::DecomposedTransform() {
282 translate[0] = translate[1] = translate[2] = 0.0;
283 scale[0] = scale[1] = scale[2] = 1.0;
284 skew[0] = skew[1] = skew[2] = 0.0;
285 perspective[0] = perspective[1] = perspective[2] = 0.0;
286 quaternion[0] = quaternion[1] = quaternion[2] = 0.0;
287 perspective[3] = quaternion[3] = 1.0;
288}
289
290bool BlendDecomposedTransforms(DecomposedTransform* out,
291 const DecomposedTransform& to,
292 const DecomposedTransform& from,
[email protected]6138db702013-09-25 03:25:05293 double progress) {
294 double scalea = progress;
295 double scaleb = 1.0 - progress;
[email protected]2fcafa02012-11-15 01:12:55296 Combine<3>(out->translate, to.translate, from.translate, scalea, scaleb);
297 Combine<3>(out->scale, to.scale, from.scale, scalea, scaleb);
298 Combine<3>(out->skew, to.skew, from.skew, scalea, scaleb);
299 Combine<4>(
300 out->perspective, to.perspective, from.perspective, scalea, scaleb);
301 return Slerp(out->quaternion, from.quaternion, to.quaternion, progress);
302}
303
304// Taken from http://www.w3.org/TR/css3-transforms/.
305bool DecomposeTransform(DecomposedTransform* decomp,
306 const Transform& transform) {
307 if (!decomp)
308 return false;
309
310 // We'll operate on a copy of the matrix.
311 SkMatrix44 matrix = transform.matrix();
312
313 // If we cannot normalize the matrix, then bail early as we cannot decompose.
314 if (!Normalize(matrix))
315 return false;
316
[email protected]2fcafa02012-11-15 01:12:55317 SkMatrix44 perspectiveMatrix = matrix;
318
319 for (int i = 0; i < 3; ++i)
[email protected]803f6b52013-09-12 00:51:26320 perspectiveMatrix.set(3, i, 0.0);
[email protected]2fcafa02012-11-15 01:12:55321
[email protected]803f6b52013-09-12 00:51:26322 perspectiveMatrix.set(3, 3, 1.0);
[email protected]2fcafa02012-11-15 01:12:55323
324 // If the perspective matrix is not invertible, we are also unable to
325 // decompose, so we'll bail early. Constant taken from SkMatrix44::invert.
326 if (std::abs(perspectiveMatrix.determinant()) < 1e-8)
327 return false;
328
[email protected]803f6b52013-09-12 00:51:26329 if (matrix.get(3, 0) != 0.0 || matrix.get(3, 1) != 0.0 ||
330 matrix.get(3, 2) != 0.0) {
[email protected]2fcafa02012-11-15 01:12:55331 // rhs is the right hand side of the equation.
332 SkMScalar rhs[4] = {
333 matrix.get(3, 0),
334 matrix.get(3, 1),
335 matrix.get(3, 2),
336 matrix.get(3, 3)
337 };
338
339 // Solve the equation by inverting perspectiveMatrix and multiplying
340 // rhs by the inverse.
[email protected]20acbd8452013-01-16 17:29:48341 SkMatrix44 inversePerspectiveMatrix(SkMatrix44::kUninitialized_Constructor);
[email protected]2fcafa02012-11-15 01:12:55342 if (!perspectiveMatrix.invert(&inversePerspectiveMatrix))
343 return false;
344
345 SkMatrix44 transposedInversePerspectiveMatrix =
[email protected]45127922012-11-17 12:24:49346 inversePerspectiveMatrix;
[email protected]2fcafa02012-11-15 01:12:55347
[email protected]45127922012-11-17 12:24:49348 transposedInversePerspectiveMatrix.transpose();
[email protected]2fcafa02012-11-15 01:12:55349 transposedInversePerspectiveMatrix.mapMScalars(rhs);
350
351 for (int i = 0; i < 4; ++i)
352 decomp->perspective[i] = rhs[i];
353
354 } else {
355 // No perspective.
356 for (int i = 0; i < 3; ++i)
357 decomp->perspective[i] = 0.0;
358 decomp->perspective[3] = 1.0;
359 }
360
361 for (int i = 0; i < 3; i++)
[email protected]803f6b52013-09-12 00:51:26362 decomp->translate[i] = matrix.get(i, 3);
[email protected]2fcafa02012-11-15 01:12:55363
[email protected]803f6b52013-09-12 00:51:26364 SkMScalar row[3][3];
[email protected]2fcafa02012-11-15 01:12:55365 for (int i = 0; i < 3; i++)
366 for (int j = 0; j < 3; ++j)
[email protected]803f6b52013-09-12 00:51:26367 row[i][j] = matrix.get(j, i);
[email protected]2fcafa02012-11-15 01:12:55368
369 // Compute X scale factor and normalize first row.
370 decomp->scale[0] = Length3(row[0]);
lof84d6e4728f32014-10-30 15:07:28371 if (decomp->scale[0] != 0.0) {
372 row[0][0] /= decomp->scale[0];
373 row[0][1] /= decomp->scale[0];
374 row[0][2] /= decomp->scale[0];
375 }
[email protected]2fcafa02012-11-15 01:12:55376
377 // Compute XY shear factor and make 2nd row orthogonal to 1st.
378 decomp->skew[0] = Dot<3>(row[0], row[1]);
379 Combine<3>(row[1], row[1], row[0], 1.0, -decomp->skew[0]);
380
381 // Now, compute Y scale and normalize 2nd row.
382 decomp->scale[1] = Length3(row[1]);
lof84d6e4728f32014-10-30 15:07:28383 if (decomp->scale[1] != 0.0) {
384 row[1][0] /= decomp->scale[1];
385 row[1][1] /= decomp->scale[1];
386 row[1][2] /= decomp->scale[1];
387 }
[email protected]2fcafa02012-11-15 01:12:55388
389 decomp->skew[0] /= decomp->scale[1];
390
391 // Compute XZ and YZ shears, orthogonalize 3rd row
392 decomp->skew[1] = Dot<3>(row[0], row[2]);
393 Combine<3>(row[2], row[2], row[0], 1.0, -decomp->skew[1]);
394 decomp->skew[2] = Dot<3>(row[1], row[2]);
395 Combine<3>(row[2], row[2], row[1], 1.0, -decomp->skew[2]);
396
397 // Next, get Z scale and normalize 3rd row.
398 decomp->scale[2] = Length3(row[2]);
lof84d6e4728f32014-10-30 15:07:28399 if (decomp->scale[2] != 0.0) {
400 row[2][0] /= decomp->scale[2];
401 row[2][1] /= decomp->scale[2];
402 row[2][2] /= decomp->scale[2];
403 }
[email protected]2fcafa02012-11-15 01:12:55404
405 decomp->skew[1] /= decomp->scale[2];
406 decomp->skew[2] /= decomp->scale[2];
407
408 // At this point, the matrix (in rows) is orthonormal.
409 // Check for a coordinate system flip. If the determinant
410 // is -1, then negate the matrix and the scaling factors.
[email protected]803f6b52013-09-12 00:51:26411 SkMScalar pdum3[3];
[email protected]2fcafa02012-11-15 01:12:55412 Cross3(pdum3, row[1], row[2]);
413 if (Dot<3>(row[0], pdum3) < 0) {
414 for (int i = 0; i < 3; i++) {
415 decomp->scale[i] *= -1.0;
416 for (int j = 0; j < 3; ++j)
417 row[i][j] *= -1.0;
418 }
419 }
420
pkastingeb00e7e2014-11-04 22:30:28421 double row00 = SkMScalarToDouble(row[0][0]);
422 double row11 = SkMScalarToDouble(row[1][1]);
423 double row22 = SkMScalarToDouble(row[2][2]);
424 decomp->quaternion[0] = SkDoubleToMScalar(
425 0.5 * std::sqrt(std::max(1.0 + row00 - row11 - row22, 0.0)));
426 decomp->quaternion[1] = SkDoubleToMScalar(
427 0.5 * std::sqrt(std::max(1.0 - row00 + row11 - row22, 0.0)));
428 decomp->quaternion[2] = SkDoubleToMScalar(
429 0.5 * std::sqrt(std::max(1.0 - row00 - row11 + row22, 0.0)));
430 decomp->quaternion[3] = SkDoubleToMScalar(
431 0.5 * std::sqrt(std::max(1.0 + row00 + row11 + row22, 0.0)));
[email protected]2fcafa02012-11-15 01:12:55432
433 if (row[2][1] > row[1][2])
434 decomp->quaternion[0] = -decomp->quaternion[0];
435 if (row[0][2] > row[2][0])
436 decomp->quaternion[1] = -decomp->quaternion[1];
437 if (row[1][0] > row[0][1])
438 decomp->quaternion[2] = -decomp->quaternion[2];
439
440 return true;
441}
442
443// Taken from http://www.w3.org/TR/css3-transforms/.
444Transform ComposeTransform(const DecomposedTransform& decomp) {
[email protected]dd31d492013-10-24 22:33:25445 SkMatrix44 perspective = BuildPerspectiveMatrix(decomp);
446 SkMatrix44 translation = BuildTranslationMatrix(decomp);
447 SkMatrix44 rotation = BuildRotationMatrix(decomp);
448 SkMatrix44 skew = BuildSkewMatrix(decomp);
449 SkMatrix44 scale = BuildScaleMatrix(decomp);
[email protected]2fcafa02012-11-15 01:12:55450
[email protected]dd31d492013-10-24 22:33:25451 return ComposeTransform(perspective, translation, rotation, skew, scale);
452}
[email protected]2fcafa02012-11-15 01:12:55453
[email protected]dd31d492013-10-24 22:33:25454bool SnapTransform(Transform* out,
455 const Transform& transform,
456 const Rect& viewport) {
457 DecomposedTransform decomp;
458 DecomposeTransform(&decomp, transform);
[email protected]2fcafa02012-11-15 01:12:55459
[email protected]dd31d492013-10-24 22:33:25460 SkMatrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp);
461 SkMatrix44 translation = BuildSnappedTranslationMatrix(decomp);
462 SkMatrix44 scale = BuildSnappedScaleMatrix(decomp);
[email protected]2fcafa02012-11-15 01:12:55463
[email protected]dd31d492013-10-24 22:33:25464 // Rebuild matrices for other unchanged components.
465 SkMatrix44 perspective = BuildPerspectiveMatrix(decomp);
[email protected]2fcafa02012-11-15 01:12:55466
[email protected]dd31d492013-10-24 22:33:25467 // Completely ignore the skew.
468 SkMatrix44 skew(SkMatrix44::kIdentity_Constructor);
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 CheckTransformsMapsIntViewportWithinOnePixel(viewport, transform, snapped);
477 if (snappable) {
478 *out = snapped;
[email protected]2fcafa02012-11-15 01:12:55479 }
[email protected]dd31d492013-10-24 22:33:25480 return snappable;
[email protected]2fcafa02012-11-15 01:12:55481}
482
bruthig8a260732014-11-24 23:32:02483Transform TransformAboutPivot(const gfx::Point& pivot,
484 const gfx::Transform& transform) {
485 gfx::Transform result;
486 result.Translate(pivot.x(), pivot.y());
487 result.PreconcatTransform(transform);
488 result.Translate(-pivot.x(), -pivot.y());
489 return result;
490}
491
[email protected]6138db702013-09-25 03:25:05492std::string DecomposedTransform::ToString() const {
493 return base::StringPrintf(
494 "translate: %+0.4f %+0.4f %+0.4f\n"
495 "scale: %+0.4f %+0.4f %+0.4f\n"
496 "skew: %+0.4f %+0.4f %+0.4f\n"
497 "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n"
498 "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n",
499 translate[0],
500 translate[1],
501 translate[2],
502 scale[0],
503 scale[1],
504 scale[2],
505 skew[0],
506 skew[1],
507 skew[2],
508 perspective[0],
509 perspective[1],
510 perspective[2],
511 perspective[3],
512 quaternion[0],
513 quaternion[1],
514 quaternion[2],
515 quaternion[3]);
516}
517
bruthig8a260732014-11-24 23:32:02518} // namespace gfx