transform_util.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  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
  3. // found in the LICENSE file.
  4. #include "ui/gfx/geometry/transform_util.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <ostream>
  8. #include <string>
  9. #include "base/check.h"
  10. #include "base/strings/stringprintf.h"
  11. #include "ui/gfx/geometry/point3_f.h"
  12. #include "ui/gfx/geometry/rect.h"
  13. #include "ui/gfx/geometry/rect_f.h"
  14. namespace gfx {
  15. namespace {
  16. SkScalar Length3(SkScalar v[3]) {
  17. double vd[3] = {v[0], v[1], v[2]};
  18. return SkDoubleToScalar(
  19. std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2]));
  20. }
  21. template <int n>
  22. SkScalar Dot(const SkScalar* a, const SkScalar* b) {
  23. double total = 0.0;
  24. for (int i = 0; i < n; ++i)
  25. total += a[i] * b[i];
  26. return SkDoubleToScalar(total);
  27. }
  28. template <int n>
  29. void Combine(SkScalar* out,
  30. const SkScalar* a,
  31. const SkScalar* b,
  32. double scale_a,
  33. double scale_b) {
  34. for (int i = 0; i < n; ++i)
  35. out[i] = SkDoubleToScalar(a[i] * scale_a + b[i] * scale_b);
  36. }
  37. void Cross3(SkScalar out[3], SkScalar a[3], SkScalar b[3]) {
  38. SkScalar x = a[1] * b[2] - a[2] * b[1];
  39. SkScalar y = a[2] * b[0] - a[0] * b[2];
  40. SkScalar z = a[0] * b[1] - a[1] * b[0];
  41. out[0] = x;
  42. out[1] = y;
  43. out[2] = z;
  44. }
  45. SkScalar Round(SkScalar n) {
  46. return SkDoubleToScalar(std::floor(double{n} + 0.5));
  47. }
  48. // Returns false if the matrix cannot be normalized.
  49. bool Normalize(Matrix44& m) {
  50. if (m.rc(3, 3) == 0.0)
  51. // Cannot normalize.
  52. return false;
  53. SkScalar scale = SK_Scalar1 / m.rc(3, 3);
  54. for (int i = 0; i < 4; i++)
  55. for (int j = 0; j < 4; j++)
  56. m.setRC(i, j, m.rc(i, j) * scale);
  57. return true;
  58. }
  59. Matrix44 BuildPerspectiveMatrix(const DecomposedTransform& decomp) {
  60. Matrix44 matrix;
  61. for (int i = 0; i < 4; i++)
  62. matrix.setRC(3, i, decomp.perspective[i]);
  63. return matrix;
  64. }
  65. Matrix44 BuildTranslationMatrix(const DecomposedTransform& decomp) {
  66. Matrix44 matrix(Matrix44::kUninitialized_Constructor);
  67. // Implicitly calls matrix.setIdentity()
  68. matrix.setTranslate(SkDoubleToScalar(decomp.translate[0]),
  69. SkDoubleToScalar(decomp.translate[1]),
  70. SkDoubleToScalar(decomp.translate[2]));
  71. return matrix;
  72. }
  73. Matrix44 BuildSnappedTranslationMatrix(DecomposedTransform decomp) {
  74. decomp.translate[0] = Round(decomp.translate[0]);
  75. decomp.translate[1] = Round(decomp.translate[1]);
  76. decomp.translate[2] = Round(decomp.translate[2]);
  77. return BuildTranslationMatrix(decomp);
  78. }
  79. Matrix44 BuildRotationMatrix(const DecomposedTransform& decomp) {
  80. return Transform(decomp.quaternion).matrix();
  81. }
  82. Matrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) {
  83. // Create snapped rotation.
  84. Matrix44 rotation_matrix = BuildRotationMatrix(decomp);
  85. for (int i = 0; i < 3; ++i) {
  86. for (int j = 0; j < 3; ++j) {
  87. SkScalar value = rotation_matrix.rc(i, j);
  88. // Snap values to -1, 0 or 1.
  89. if (value < -0.5f) {
  90. value = -1.0f;
  91. } else if (value > 0.5f) {
  92. value = 1.0f;
  93. } else {
  94. value = 0.0f;
  95. }
  96. rotation_matrix.setRC(i, j, value);
  97. }
  98. }
  99. return rotation_matrix;
  100. }
  101. Matrix44 BuildSkewMatrix(const DecomposedTransform& decomp) {
  102. Matrix44 matrix;
  103. Matrix44 temp;
  104. if (decomp.skew[2]) {
  105. temp.setRC(1, 2, decomp.skew[2]);
  106. matrix.preConcat(temp);
  107. }
  108. if (decomp.skew[1]) {
  109. temp.setRC(1, 2, 0);
  110. temp.setRC(0, 2, decomp.skew[1]);
  111. matrix.preConcat(temp);
  112. }
  113. if (decomp.skew[0]) {
  114. temp.setRC(0, 2, 0);
  115. temp.setRC(0, 1, decomp.skew[0]);
  116. matrix.preConcat(temp);
  117. }
  118. return matrix;
  119. }
  120. Matrix44 BuildScaleMatrix(const DecomposedTransform& decomp) {
  121. Matrix44 matrix(Matrix44::kUninitialized_Constructor);
  122. matrix.setScale(SkDoubleToScalar(decomp.scale[0]),
  123. SkDoubleToScalar(decomp.scale[1]),
  124. SkDoubleToScalar(decomp.scale[2]));
  125. return matrix;
  126. }
  127. Matrix44 BuildSnappedScaleMatrix(DecomposedTransform decomp) {
  128. decomp.scale[0] = Round(decomp.scale[0]);
  129. decomp.scale[1] = Round(decomp.scale[1]);
  130. decomp.scale[2] = Round(decomp.scale[2]);
  131. return BuildScaleMatrix(decomp);
  132. }
  133. Transform ComposeTransform(const Matrix44& perspective,
  134. const Matrix44& translation,
  135. const Matrix44& rotation,
  136. const Matrix44& skew,
  137. const Matrix44& scale) {
  138. Matrix44 matrix;
  139. matrix.preConcat(perspective);
  140. matrix.preConcat(translation);
  141. matrix.preConcat(rotation);
  142. matrix.preConcat(skew);
  143. matrix.preConcat(scale);
  144. Transform to_return;
  145. to_return.matrix() = matrix;
  146. return to_return;
  147. }
  148. bool CheckViewportPointMapsWithinOnePixel(const Point& point,
  149. const Transform& transform) {
  150. auto point_original = Point3F(PointF(point));
  151. auto point_transformed = Point3F(PointF(point));
  152. // Can't use TransformRect here since it would give us the axis-aligned
  153. // bounding rect of the 4 points in the initial rectable which is not what we
  154. // want.
  155. transform.TransformPoint(&point_transformed);
  156. if ((point_transformed - point_original).Length() > 1.f) {
  157. // The changed distance should not be more than 1 pixel.
  158. return false;
  159. }
  160. return true;
  161. }
  162. bool CheckTransformsMapsIntViewportWithinOnePixel(const Rect& viewport,
  163. const Transform& original,
  164. const Transform& snapped) {
  165. Transform original_inv(Transform::kSkipInitialization);
  166. bool invertible = true;
  167. invertible &= original.GetInverse(&original_inv);
  168. DCHECK(invertible) << "Non-invertible transform, cannot snap.";
  169. Transform combined = snapped * original_inv;
  170. return CheckViewportPointMapsWithinOnePixel(viewport.origin(), combined) &&
  171. CheckViewportPointMapsWithinOnePixel(viewport.top_right(), combined) &&
  172. CheckViewportPointMapsWithinOnePixel(viewport.bottom_left(),
  173. combined) &&
  174. CheckViewportPointMapsWithinOnePixel(viewport.bottom_right(),
  175. combined);
  176. }
  177. bool Is2dTransform(const Transform& transform) {
  178. const Matrix44 matrix = transform.matrix();
  179. if (matrix.hasPerspective())
  180. return false;
  181. return matrix.rc(2, 0) == 0 && matrix.rc(2, 1) == 0 && matrix.rc(0, 2) == 0 &&
  182. matrix.rc(1, 2) == 0 && matrix.rc(2, 2) == 1 && matrix.rc(3, 2) == 0 &&
  183. matrix.rc(2, 3) == 0;
  184. }
  185. bool Decompose2DTransform(DecomposedTransform* decomp,
  186. const Transform& transform) {
  187. if (!Is2dTransform(transform)) {
  188. return false;
  189. }
  190. const Matrix44 matrix = transform.matrix();
  191. double m11 = matrix.rc(0, 0);
  192. double m21 = matrix.rc(0, 1);
  193. double m12 = matrix.rc(1, 0);
  194. double m22 = matrix.rc(1, 1);
  195. double determinant = m11 * m22 - m12 * m21;
  196. // Test for matrix being singular.
  197. if (determinant == 0) {
  198. return false;
  199. }
  200. // Translation transform.
  201. // [m11 m21 0 m41] [1 0 0 Tx] [m11 m21 0 0]
  202. // [m12 m22 0 m42] = [0 1 0 Ty] [m12 m22 0 0]
  203. // [ 0 0 1 0 ] [0 0 1 0 ] [ 0 0 1 0]
  204. // [ 0 0 0 1 ] [0 0 0 1 ] [ 0 0 0 1]
  205. decomp->translate[0] = matrix.rc(0, 3);
  206. decomp->translate[1] = matrix.rc(1, 3);
  207. // For the remainder of the decomposition process, we can focus on the upper
  208. // 2x2 submatrix
  209. // [m11 m21] = [cos(R) -sin(R)] [1 K] [Sx 0 ]
  210. // [m12 m22] [sin(R) cos(R)] [0 1] [0 Sy]
  211. // = [Sx*cos(R) Sy*(K*cos(R) - sin(R))]
  212. // [Sx*sin(R) Sy*(K*sin(R) + cos(R))]
  213. // Determine sign of the x and y scale.
  214. if (determinant < 0) {
  215. // If the determinant is negative, we need to flip either the x or y scale.
  216. // Flipping both is equivalent to rotating by 180 degrees.
  217. if (m11 < m22) {
  218. decomp->scale[0] *= -1;
  219. } else {
  220. decomp->scale[1] *= -1;
  221. }
  222. }
  223. // X Scale.
  224. // m11^2 + m12^2 = Sx^2*(cos^2(R) + sin^2(R)) = Sx^2.
  225. // Sx = +/-sqrt(m11^2 + m22^2)
  226. decomp->scale[0] *= sqrt(m11 * m11 + m12 * m12);
  227. m11 /= decomp->scale[0];
  228. m12 /= decomp->scale[0];
  229. // Post normalization, the submatrix is now of the form:
  230. // [m11 m21] = [cos(R) Sy*(K*cos(R) - sin(R))]
  231. // [m12 m22] [sin(R) Sy*(K*sin(R) + cos(R))]
  232. // XY Shear.
  233. // m11 * m21 + m12 * m22 = Sy*K*cos^2(R) - Sy*sin(R)*cos(R) +
  234. // Sy*K*sin^2(R) + Sy*cos(R)*sin(R)
  235. // = Sy*K
  236. double scaledShear = m11 * m21 + m12 * m22;
  237. m21 -= m11 * scaledShear;
  238. m22 -= m12 * scaledShear;
  239. // Post normalization, the submatrix is now of the form:
  240. // [m11 m21] = [cos(R) -Sy*sin(R)]
  241. // [m12 m22] [sin(R) Sy*cos(R)]
  242. // Y Scale.
  243. // Similar process to determining x-scale.
  244. decomp->scale[1] *= sqrt(m21 * m21 + m22 * m22);
  245. m21 /= decomp->scale[1];
  246. m22 /= decomp->scale[1];
  247. decomp->skew[0] = scaledShear / decomp->scale[1];
  248. // Rotation transform.
  249. // [1-2(yy+zz) 2(xy-zw) 2(xz+yw) ] [cos(R) -sin(R) 0]
  250. // [2(xy+zw) 1-2(xx+zz) 2(yz-xw) ] = [sin(R) cos(R) 0]
  251. // [2(xz-yw) 2*(yz+xw) 1-2(xx+yy)] [ 0 0 1]
  252. // Comparing terms, we can conclude that x = y = 0.
  253. // [1-2zz -2zw 0] [cos(R) -sin(R) 0]
  254. // [ 2zw 1-2zz 0] = [sin(R) cos(R) 0]
  255. // [ 0 0 1] [ 0 0 1]
  256. // cos(R) = 1 - 2*z^2
  257. // From the double angle formula: cos(2a) = 1 - 2 sin(a)^2
  258. // cos(R) = 1 - 2*sin(R/2)^2 = 1 - 2*z^2 ==> z = sin(R/2)
  259. // sin(R) = 2*z*w
  260. // But sin(2a) = 2 sin(a) cos(a)
  261. // sin(R) = 2 sin(R/2) cos(R/2) = 2*z*w ==> w = cos(R/2)
  262. double angle = atan2(m12, m11);
  263. decomp->quaternion.set_x(0);
  264. decomp->quaternion.set_y(0);
  265. decomp->quaternion.set_z(sin(0.5 * angle));
  266. decomp->quaternion.set_w(cos(0.5 * angle));
  267. return true;
  268. }
  269. } // namespace
  270. Transform GetScaleTransform(const Point& anchor, float scale) {
  271. Transform transform;
  272. transform.Translate(anchor.x() * (1 - scale), anchor.y() * (1 - scale));
  273. transform.Scale(scale, scale);
  274. return transform;
  275. }
  276. DecomposedTransform::DecomposedTransform() {
  277. translate[0] = translate[1] = translate[2] = 0.0;
  278. scale[0] = scale[1] = scale[2] = 1.0;
  279. skew[0] = skew[1] = skew[2] = 0.0;
  280. perspective[0] = perspective[1] = perspective[2] = 0.0;
  281. perspective[3] = 1.0;
  282. }
  283. DecomposedTransform BlendDecomposedTransforms(const DecomposedTransform& to,
  284. const DecomposedTransform& from,
  285. double progress) {
  286. DecomposedTransform out;
  287. double scalea = progress;
  288. double scaleb = 1.0 - progress;
  289. Combine<3>(out.translate, to.translate, from.translate, scalea, scaleb);
  290. Combine<3>(out.scale, to.scale, from.scale, scalea, scaleb);
  291. Combine<3>(out.skew, to.skew, from.skew, scalea, scaleb);
  292. Combine<4>(out.perspective, to.perspective, from.perspective, scalea, scaleb);
  293. out.quaternion = from.quaternion.Slerp(to.quaternion, progress);
  294. return out;
  295. }
  296. // Taken from http://www.w3.org/TR/css3-transforms/.
  297. // TODO(crbug/937296): This implementation is virtually identical to the
  298. // implementation in blink::TransformationMatrix with the main difference being
  299. // the representation of the underlying matrix. These implementations should be
  300. // consolidated.
  301. bool DecomposeTransform(DecomposedTransform* decomp,
  302. const Transform& transform) {
  303. if (!decomp)
  304. return false;
  305. if (Decompose2DTransform(decomp, transform))
  306. return true;
  307. // We'll operate on a copy of the matrix.
  308. Matrix44 matrix = transform.matrix();
  309. // If we cannot normalize the matrix, then bail early as we cannot decompose.
  310. if (!Normalize(matrix))
  311. return false;
  312. Matrix44 perspectiveMatrix = matrix;
  313. for (int i = 0; i < 3; ++i)
  314. perspectiveMatrix.setRC(3, i, 0.0);
  315. perspectiveMatrix.setRC(3, 3, 1.0);
  316. // If the perspective matrix is not invertible, we are also unable to
  317. // decompose, so we'll bail early. Constant taken from Matrix44::invert.
  318. if (std::abs(perspectiveMatrix.determinant()) < 1e-8)
  319. return false;
  320. if (matrix.rc(3, 0) != 0.0 || matrix.rc(3, 1) != 0.0 ||
  321. matrix.rc(3, 2) != 0.0) {
  322. // rhs is the right hand side of the equation.
  323. SkScalar rhs[4] = {matrix.rc(3, 0), matrix.rc(3, 1), matrix.rc(3, 2),
  324. matrix.rc(3, 3)};
  325. // Solve the equation by inverting perspectiveMatrix and multiplying
  326. // rhs by the inverse.
  327. Matrix44 inversePerspectiveMatrix(Matrix44::kUninitialized_Constructor);
  328. if (!perspectiveMatrix.invert(&inversePerspectiveMatrix))
  329. return false;
  330. Matrix44 transposedInversePerspectiveMatrix = inversePerspectiveMatrix;
  331. transposedInversePerspectiveMatrix.transpose();
  332. transposedInversePerspectiveMatrix.mapScalars(rhs);
  333. for (int i = 0; i < 4; ++i)
  334. decomp->perspective[i] = rhs[i];
  335. } else {
  336. // No perspective.
  337. for (int i = 0; i < 3; ++i)
  338. decomp->perspective[i] = 0.0;
  339. decomp->perspective[3] = 1.0;
  340. }
  341. for (int i = 0; i < 3; i++)
  342. decomp->translate[i] = matrix.rc(i, 3);
  343. // Copy of matrix is stored in column major order to facilitate column-level
  344. // operations.
  345. SkScalar column[3][3];
  346. for (int i = 0; i < 3; i++)
  347. for (int j = 0; j < 3; ++j)
  348. column[i][j] = matrix.rc(j, i);
  349. // Compute X scale factor and normalize first column.
  350. decomp->scale[0] = Length3(column[0]);
  351. if (decomp->scale[0] != 0.0) {
  352. column[0][0] /= decomp->scale[0];
  353. column[0][1] /= decomp->scale[0];
  354. column[0][2] /= decomp->scale[0];
  355. }
  356. // Compute XY shear factor and make 2nd column orthogonal to 1st.
  357. decomp->skew[0] = Dot<3>(column[0], column[1]);
  358. Combine<3>(column[1], column[1], column[0], 1.0, -decomp->skew[0]);
  359. // Now, compute Y scale and normalize 2nd column.
  360. decomp->scale[1] = Length3(column[1]);
  361. if (decomp->scale[1] != 0.0) {
  362. column[1][0] /= decomp->scale[1];
  363. column[1][1] /= decomp->scale[1];
  364. column[1][2] /= decomp->scale[1];
  365. }
  366. decomp->skew[0] /= decomp->scale[1];
  367. // Compute XZ and YZ shears, orthogonalize the 3rd column.
  368. decomp->skew[1] = Dot<3>(column[0], column[2]);
  369. Combine<3>(column[2], column[2], column[0], 1.0, -decomp->skew[1]);
  370. decomp->skew[2] = Dot<3>(column[1], column[2]);
  371. Combine<3>(column[2], column[2], column[1], 1.0, -decomp->skew[2]);
  372. // Next, get Z scale and normalize the 3rd column.
  373. decomp->scale[2] = Length3(column[2]);
  374. if (decomp->scale[2] != 0.0) {
  375. column[2][0] /= decomp->scale[2];
  376. column[2][1] /= decomp->scale[2];
  377. column[2][2] /= decomp->scale[2];
  378. }
  379. decomp->skew[1] /= decomp->scale[2];
  380. decomp->skew[2] /= decomp->scale[2];
  381. // At this point, the matrix is orthonormal.
  382. // Check for a coordinate system flip. If the determinant
  383. // is -1, then negate the matrix and the scaling factors.
  384. // TODO(kevers): This is inconsistent from the 2D specification, in which
  385. // only 1 axis is flipped when the determinant is negative. Verify if it is
  386. // correct to flip all of the scales and matrix elements, as this introduces
  387. // rotation for the simple case of a single axis scale inversion.
  388. SkScalar pdum3[3];
  389. Cross3(pdum3, column[1], column[2]);
  390. if (Dot<3>(column[0], pdum3) < 0) {
  391. for (int i = 0; i < 3; i++) {
  392. decomp->scale[i] *= -1.0;
  393. for (int j = 0; j < 3; ++j)
  394. column[i][j] *= -1.0;
  395. }
  396. }
  397. // See https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion.
  398. // Note: deviating from spec (http://www.w3.org/TR/css3-transforms/)
  399. // which has a degenerate case of zero off-diagonal elements in the
  400. // orthonormal matrix, which leads to errors in determining the sign
  401. // of the quaternions.
  402. double q_xx = column[0][0];
  403. double q_xy = column[1][0];
  404. double q_xz = column[2][0];
  405. double q_yx = column[0][1];
  406. double q_yy = column[1][1];
  407. double q_yz = column[2][1];
  408. double q_zx = column[0][2];
  409. double q_zy = column[1][2];
  410. double q_zz = column[2][2];
  411. double r, s, t, x, y, z, w;
  412. t = q_xx + q_yy + q_zz;
  413. if (t > 0) {
  414. r = std::sqrt(1.0 + t);
  415. s = 0.5 / r;
  416. w = 0.5 * r;
  417. x = (q_zy - q_yz) * s;
  418. y = (q_xz - q_zx) * s;
  419. z = (q_yx - q_xy) * s;
  420. } else if (q_xx > q_yy && q_xx > q_zz) {
  421. r = std::sqrt(1.0 + q_xx - q_yy - q_zz);
  422. s = 0.5 / r;
  423. x = 0.5 * r;
  424. y = (q_xy + q_yx) * s;
  425. z = (q_xz + q_zx) * s;
  426. w = (q_zy - q_yz) * s;
  427. } else if (q_yy > q_zz) {
  428. r = std::sqrt(1.0 - q_xx + q_yy - q_zz);
  429. s = 0.5 / r;
  430. x = (q_xy + q_yx) * s;
  431. y = 0.5 * r;
  432. z = (q_yz + q_zy) * s;
  433. w = (q_xz - q_zx) * s;
  434. } else {
  435. r = std::sqrt(1.0 - q_xx - q_yy + q_zz);
  436. s = 0.5 / r;
  437. x = (q_xz + q_zx) * s;
  438. y = (q_yz + q_zy) * s;
  439. z = 0.5 * r;
  440. w = (q_yx - q_xy) * s;
  441. }
  442. decomp->quaternion.set_x(SkDoubleToScalar(x));
  443. decomp->quaternion.set_y(SkDoubleToScalar(y));
  444. decomp->quaternion.set_z(SkDoubleToScalar(z));
  445. decomp->quaternion.set_w(SkDoubleToScalar(w));
  446. return true;
  447. }
  448. // Taken from http://www.w3.org/TR/css3-transforms/.
  449. Transform ComposeTransform(const DecomposedTransform& decomp) {
  450. Matrix44 perspective = BuildPerspectiveMatrix(decomp);
  451. Matrix44 translation = BuildTranslationMatrix(decomp);
  452. Matrix44 rotation = BuildRotationMatrix(decomp);
  453. Matrix44 skew = BuildSkewMatrix(decomp);
  454. Matrix44 scale = BuildScaleMatrix(decomp);
  455. return ComposeTransform(perspective, translation, rotation, skew, scale);
  456. }
  457. bool SnapTransform(Transform* out,
  458. const Transform& transform,
  459. const Rect& viewport) {
  460. DecomposedTransform decomp;
  461. DecomposeTransform(&decomp, transform);
  462. Matrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp);
  463. Matrix44 translation = BuildSnappedTranslationMatrix(decomp);
  464. Matrix44 scale = BuildSnappedScaleMatrix(decomp);
  465. // Rebuild matrices for other unchanged components.
  466. Matrix44 perspective = BuildPerspectiveMatrix(decomp);
  467. // Completely ignore the skew.
  468. Matrix44 skew;
  469. // Get full transform.
  470. Transform snapped =
  471. ComposeTransform(perspective, translation, rotation_matrix, skew, scale);
  472. // Verify that viewport is not moved unnaturally.
  473. bool snappable = CheckTransformsMapsIntViewportWithinOnePixel(
  474. viewport, transform, snapped);
  475. if (snappable) {
  476. *out = snapped;
  477. }
  478. return snappable;
  479. }
  480. Transform TransformAboutPivot(const Point& pivot, const Transform& transform) {
  481. Transform result;
  482. result.Translate(pivot.x(), pivot.y());
  483. result.PreconcatTransform(transform);
  484. result.Translate(-pivot.x(), -pivot.y());
  485. return result;
  486. }
  487. Transform TransformBetweenRects(const RectF& src, const RectF& dst) {
  488. DCHECK(!src.IsEmpty());
  489. Transform result;
  490. result.Translate(dst.origin() - src.origin());
  491. result.Scale(dst.width() / src.width(), dst.height() / src.height());
  492. return result;
  493. }
  494. std::string DecomposedTransform::ToString() const {
  495. return base::StringPrintf(
  496. "translate: %+0.4f %+0.4f %+0.4f\n"
  497. "scale: %+0.4f %+0.4f %+0.4f\n"
  498. "skew: %+0.4f %+0.4f %+0.4f\n"
  499. "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n"
  500. "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n",
  501. translate[0], translate[1], translate[2], scale[0], scale[1], scale[2],
  502. skew[0], skew[1], skew[2], perspective[0], perspective[1], perspective[2],
  503. perspective[3], quaternion.x(), quaternion.y(), quaternion.z(),
  504. quaternion.w());
  505. }
  506. Transform OrthoProjectionMatrix(float left,
  507. float right,
  508. float bottom,
  509. float top) {
  510. // Use the standard formula to map the clipping frustum to the cube from
  511. // [-1, -1, -1] to [1, 1, 1].
  512. float delta_x = right - left;
  513. float delta_y = top - bottom;
  514. Transform proj;
  515. if (!delta_x || !delta_y)
  516. return proj;
  517. proj.matrix().setRC(0, 0, 2.0f / delta_x);
  518. proj.matrix().setRC(0, 3, -(right + left) / delta_x);
  519. proj.matrix().setRC(1, 1, 2.0f / delta_y);
  520. proj.matrix().setRC(1, 3, -(top + bottom) / delta_y);
  521. // Z component of vertices is always set to zero as we don't use the depth
  522. // buffer while drawing.
  523. proj.matrix().setRC(2, 2, 0);
  524. return proj;
  525. }
  526. Transform WindowMatrix(int x, int y, int width, int height) {
  527. Transform canvas;
  528. // Map to window position and scale up to pixel coordinates.
  529. canvas.Translate3d(x, y, 0);
  530. canvas.Scale3d(width, height, 0);
  531. // Map from ([-1, -1] to [1, 1]) -> ([0, 0] to [1, 1])
  532. canvas.Translate3d(0.5, 0.5, 0.5);
  533. canvas.Scale3d(0.5, 0.5, 0.5);
  534. return canvas;
  535. }
  536. static inline bool NearlyZero(double value) {
  537. return std::abs(value) < std::numeric_limits<double>::epsilon();
  538. }
  539. static inline float ScaleOnAxis(double a, double b, double c) {
  540. if (NearlyZero(b) && NearlyZero(c))
  541. return std::abs(a);
  542. if (NearlyZero(a) && NearlyZero(c))
  543. return std::abs(b);
  544. if (NearlyZero(a) && NearlyZero(b))
  545. return std::abs(c);
  546. // Do the sqrt as a double to not lose precision.
  547. return static_cast<float>(std::sqrt(a * a + b * b + c * c));
  548. }
  549. absl::optional<Vector2dF> TryComputeTransform2dScaleComponents(
  550. const Transform& transform) {
  551. const auto& matrix = transform.matrix();
  552. if (matrix.rc(3, 0) != 0.0f || matrix.rc(3, 1) != 0.0f) {
  553. return absl::nullopt;
  554. }
  555. float w = matrix.rc(3, 3);
  556. if (!std::isnormal(w)) {
  557. return absl::nullopt;
  558. }
  559. float w_scale = 1.0f / w;
  560. // In theory, this shouldn't be using the matrix.getDouble(2, 0) and
  561. // .getDouble(1, 0) values; creating a large transfer from input x or
  562. // y (in the layer) to output z has no visible difference when the
  563. // transform being considered is a transform to device space, since
  564. // the resulting z values are ignored. However, ignoring them here
  565. // might be risky because it would mean that we would have more
  566. // variation in the results under animation of rotateX() or rotateY(),
  567. // and we'd be relying more heavily on code to compute correct scales
  568. // during animation. Currently some such code only considers the
  569. // endpoints, which would become problematic for cases like animation
  570. // from rotateY(-60deg) to rotateY(60deg).
  571. float x_scale =
  572. ScaleOnAxis(matrix.rc(0, 0), matrix.rc(1, 0), matrix.rc(2, 0));
  573. float y_scale =
  574. ScaleOnAxis(matrix.rc(0, 1), matrix.rc(1, 1), matrix.rc(2, 1));
  575. return Vector2dF(x_scale * w_scale, y_scale * w_scale);
  576. }
  577. Vector2dF ComputeTransform2dScaleComponents(const Transform& transform,
  578. float fallback_value) {
  579. absl::optional<Vector2dF> scale =
  580. TryComputeTransform2dScaleComponents(transform);
  581. if (scale) {
  582. return *scale;
  583. }
  584. return Vector2dF(fallback_value, fallback_value);
  585. }
  586. float ComputeApproximateMaxScale(const Transform& transform) {
  587. RectF unit(0.f, 0.f, 1.f, 1.f);
  588. transform.TransformRect(&unit);
  589. return std::max(unit.width(), unit.height());
  590. }
  591. } // namespace gfx