1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7use crate::{NonZeroRect, Point};
8
9use crate::scalar::{Scalar, SCALAR_NEARLY_ZERO};
10
11#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
12use crate::NoStdFloat;
13
14/// An affine transformation matrix.
15///
16/// Unlike other types, doesn't guarantee to be valid. This is Skia quirk.
17/// Meaning Transform(0, 0, 0, 0, 0, 0) is ok, while it's technically not.
18/// Non-finite values are also not an error.
19#[allow(missing_docs)]
20#[derive(Copy, Clone, PartialEq, Debug)]
21pub struct Transform {
22 pub sx: f32,
23 pub kx: f32,
24 pub ky: f32,
25 pub sy: f32,
26 pub tx: f32,
27 pub ty: f32,
28}
29
30impl Default for Transform {
31 fn default() -> Self {
32 Transform {
33 sx: 1.0,
34 kx: 0.0,
35 ky: 0.0,
36 sy: 1.0,
37 tx: 0.0,
38 ty: 0.0,
39 }
40 }
41}
42
43impl Transform {
44 /// Creates an identity transform.
45 pub fn identity() -> Self {
46 Transform::default()
47 }
48
49 /// Creates a new `Transform`.
50 ///
51 /// We are using column-major-column-vector matrix notation, therefore it's ky-kx, not kx-ky.
52 pub fn from_row(sx: f32, ky: f32, kx: f32, sy: f32, tx: f32, ty: f32) -> Self {
53 Transform {
54 sx,
55 ky,
56 kx,
57 sy,
58 tx,
59 ty,
60 }
61 }
62
63 /// Creates a new translating `Transform`.
64 pub fn from_translate(tx: f32, ty: f32) -> Self {
65 Transform::from_row(1.0, 0.0, 0.0, 1.0, tx, ty)
66 }
67
68 /// Creates a new scaling `Transform`.
69 pub fn from_scale(sx: f32, sy: f32) -> Self {
70 Transform::from_row(sx, 0.0, 0.0, sy, 0.0, 0.0)
71 }
72
73 /// Creates a new skewing `Transform`.
74 pub fn from_skew(kx: f32, ky: f32) -> Self {
75 Transform::from_row(1.0, ky, kx, 1.0, 0.0, 0.0)
76 }
77
78 /// Creates a new rotating `Transform`.
79 ///
80 /// `angle` in degrees.
81 pub fn from_rotate(angle: f32) -> Self {
82 let v = angle.to_radians();
83 let a = v.cos();
84 let b = v.sin();
85 let c = -b;
86 let d = a;
87 Transform::from_row(a, b, c, d, 0.0, 0.0)
88 }
89
90 /// Creates a new rotating `Transform` at the specified position.
91 ///
92 /// `angle` in degrees.
93 pub fn from_rotate_at(angle: f32, tx: f32, ty: f32) -> Self {
94 let mut ts = Self::default();
95 ts = ts.pre_translate(tx, ty);
96 ts = ts.pre_concat(Transform::from_rotate(angle));
97 ts = ts.pre_translate(-tx, -ty);
98 ts
99 }
100
101 /// Converts `Rect` into a bounding box `Transform`.
102 #[inline]
103 pub fn from_bbox(bbox: NonZeroRect) -> Self {
104 Transform::from_row(bbox.width(), 0.0, 0.0, bbox.height(), bbox.x(), bbox.y())
105 }
106
107 /// Checks that transform is finite.
108 pub fn is_finite(&self) -> bool {
109 self.sx.is_finite()
110 && self.ky.is_finite()
111 && self.kx.is_finite()
112 && self.sy.is_finite()
113 && self.tx.is_finite()
114 && self.ty.is_finite()
115 }
116
117 /// Checks that transform is finite and has non-zero scale.
118 pub fn is_valid(&self) -> bool {
119 if self.is_finite() {
120 let (sx, sy) = self.get_scale();
121 !(sx.is_nearly_zero_within_tolerance(f32::EPSILON)
122 || sy.is_nearly_zero_within_tolerance(f32::EPSILON))
123 } else {
124 false
125 }
126 }
127
128 /// Checks that transform is identity.
129 pub fn is_identity(&self) -> bool {
130 *self == Transform::default()
131 }
132
133 /// Checks that transform is scale-only.
134 pub fn is_scale(&self) -> bool {
135 self.has_scale() && !self.has_skew() && !self.has_translate()
136 }
137
138 /// Checks that transform is skew-only.
139 pub fn is_skew(&self) -> bool {
140 !self.has_scale() && self.has_skew() && !self.has_translate()
141 }
142
143 /// Checks that transform is translate-only.
144 pub fn is_translate(&self) -> bool {
145 !self.has_scale() && !self.has_skew() && self.has_translate()
146 }
147
148 /// Checks that transform contains only scale and translate.
149 pub fn is_scale_translate(&self) -> bool {
150 (self.has_scale() || self.has_translate()) && !self.has_skew()
151 }
152
153 /// Checks that transform contains a scale part.
154 pub fn has_scale(&self) -> bool {
155 self.sx != 1.0 || self.sy != 1.0
156 }
157
158 /// Checks that transform contains a skew part.
159 pub fn has_skew(&self) -> bool {
160 self.kx != 0.0 || self.ky != 0.0
161 }
162
163 /// Checks that transform contains a translate part.
164 pub fn has_translate(&self) -> bool {
165 self.tx != 0.0 || self.ty != 0.0
166 }
167
168 /// Returns transform's scale part.
169 pub fn get_scale(&self) -> (f32, f32) {
170 let x_scale = (self.sx * self.sx + self.kx * self.kx).sqrt();
171 let y_scale = (self.ky * self.ky + self.sy * self.sy).sqrt();
172 (x_scale, y_scale)
173 }
174
175 /// Pre-scales the current transform.
176 #[must_use]
177 pub fn pre_scale(&self, sx: f32, sy: f32) -> Self {
178 self.pre_concat(Transform::from_scale(sx, sy))
179 }
180
181 /// Post-scales the current transform.
182 #[must_use]
183 pub fn post_scale(&self, sx: f32, sy: f32) -> Self {
184 self.post_concat(Transform::from_scale(sx, sy))
185 }
186
187 /// Pre-translates the current transform.
188 #[must_use]
189 pub fn pre_translate(&self, tx: f32, ty: f32) -> Self {
190 self.pre_concat(Transform::from_translate(tx, ty))
191 }
192
193 /// Post-translates the current transform.
194 #[must_use]
195 pub fn post_translate(&self, tx: f32, ty: f32) -> Self {
196 self.post_concat(Transform::from_translate(tx, ty))
197 }
198
199 /// Pre-rotates the current transform.
200 ///
201 /// `angle` in degrees.
202 #[must_use]
203 pub fn pre_rotate(&self, angle: f32) -> Self {
204 self.pre_concat(Transform::from_rotate(angle))
205 }
206
207 /// Post-rotates the current transform.
208 ///
209 /// `angle` in degrees.
210 #[must_use]
211 pub fn post_rotate(&self, angle: f32) -> Self {
212 self.post_concat(Transform::from_rotate(angle))
213 }
214
215 /// Pre-rotates the current transform by the specified position.
216 ///
217 /// `angle` in degrees.
218 #[must_use]
219 pub fn pre_rotate_at(&self, angle: f32, tx: f32, ty: f32) -> Self {
220 self.pre_concat(Transform::from_rotate_at(angle, tx, ty))
221 }
222
223 /// Post-rotates the current transform by the specified position.
224 ///
225 /// `angle` in degrees.
226 #[must_use]
227 pub fn post_rotate_at(&self, angle: f32, tx: f32, ty: f32) -> Self {
228 self.post_concat(Transform::from_rotate_at(angle, tx, ty))
229 }
230
231 /// Pre-concats the current transform.
232 #[must_use]
233 pub fn pre_concat(&self, other: Self) -> Self {
234 concat(*self, other)
235 }
236
237 /// Post-concats the current transform.
238 #[must_use]
239 pub fn post_concat(&self, other: Self) -> Self {
240 concat(other, *self)
241 }
242
243 pub(crate) fn from_sin_cos(sin: f32, cos: f32) -> Self {
244 Transform::from_row(cos, sin, -sin, cos, 0.0, 0.0)
245 }
246
247 /// Transforms a points using the current transform.
248 pub fn map_point(&self, point: &mut Point) {
249 if self.is_identity() {
250 // Do nothing.
251 } else if self.is_translate() {
252 point.x += self.tx;
253 point.y += self.ty;
254 } else if self.is_scale_translate() {
255 point.x = point.x * self.sx + self.tx;
256 point.y = point.y * self.sy + self.ty;
257 } else {
258 let x = point.x * self.sx + point.y * self.kx + self.tx;
259 let y = point.x * self.ky + point.y * self.sy + self.ty;
260 point.x = x;
261 point.y = y;
262 }
263 }
264
265 /// Transforms a slice of points using the current transform.
266 pub fn map_points(&self, points: &mut [Point]) {
267 if points.is_empty() {
268 return;
269 }
270
271 // TODO: simd
272
273 if self.is_identity() {
274 // Do nothing.
275 } else if self.is_translate() {
276 for p in points {
277 p.x += self.tx;
278 p.y += self.ty;
279 }
280 } else if self.is_scale_translate() {
281 for p in points {
282 p.x = p.x * self.sx + self.tx;
283 p.y = p.y * self.sy + self.ty;
284 }
285 } else {
286 for p in points {
287 let x = p.x * self.sx + p.y * self.kx + self.tx;
288 let y = p.x * self.ky + p.y * self.sy + self.ty;
289 p.x = x;
290 p.y = y;
291 }
292 }
293 }
294
295 /// Returns an inverted transform.
296 pub fn invert(&self) -> Option<Self> {
297 // Allow the trivial case to be inlined.
298 if self.is_identity() {
299 return Some(*self);
300 }
301
302 invert(self)
303 }
304}
305
306fn invert(ts: &Transform) -> Option<Transform> {
307 debug_assert!(!ts.is_identity());
308
309 if ts.is_scale_translate() {
310 if ts.has_scale() {
311 let inv_x = ts.sx.invert();
312 let inv_y = ts.sy.invert();
313 Some(Transform::from_row(
314 inv_x,
315 0.0,
316 0.0,
317 inv_y,
318 -ts.tx * inv_x,
319 -ts.ty * inv_y,
320 ))
321 } else {
322 // translate only
323 Some(Transform::from_translate(-ts.tx, -ts.ty))
324 }
325 } else {
326 let inv_det = inv_determinant(ts)?;
327 let inv_ts = compute_inv(ts, inv_det);
328
329 if inv_ts.is_finite() {
330 Some(inv_ts)
331 } else {
332 None
333 }
334 }
335}
336
337fn inv_determinant(ts: &Transform) -> Option<f64> {
338 let det: f64 = dcross(a:ts.sx as f64, b:ts.sy as f64, c:ts.kx as f64, d:ts.ky as f64);
339
340 // Since the determinant is on the order of the cube of the matrix members,
341 // compare to the cube of the default nearly-zero constant (although an
342 // estimate of the condition number would be better if it wasn't so expensive).
343 let tolerance: f32 = SCALAR_NEARLY_ZERO * SCALAR_NEARLY_ZERO * SCALAR_NEARLY_ZERO;
344 if (det as f32).is_nearly_zero_within_tolerance(tolerance) {
345 None
346 } else {
347 Some(1.0 / det)
348 }
349}
350
351fn compute_inv(ts: &Transform, inv_det: f64) -> Transform {
352 Transform::from_row(
353 (ts.sy as f64 * inv_det) as f32,
354 (-ts.ky as f64 * inv_det) as f32,
355 (-ts.kx as f64 * inv_det) as f32,
356 (ts.sx as f64 * inv_det) as f32,
357 tx:dcross_dscale(ts.kx, ts.ty, ts.sy, ts.tx, inv_det),
358 ty:dcross_dscale(a:ts.ky, b:ts.tx, c:ts.sx, d:ts.ty, scale:inv_det),
359 )
360}
361
362fn dcross(a: f64, b: f64, c: f64, d: f64) -> f64 {
363 a * b - c * d
364}
365
366fn dcross_dscale(a: f32, b: f32, c: f32, d: f32, scale: f64) -> f32 {
367 (dcross(a as f64, b as f64, c as f64, d as f64) * scale) as f32
368}
369
370fn concat(a: Transform, b: Transform) -> Transform {
371 if a.is_identity() {
372 b
373 } else if b.is_identity() {
374 a
375 } else if !a.has_skew() && !b.has_skew() {
376 // just scale and translate
377 Transform::from_row(
378 a.sx * b.sx,
379 0.0,
380 0.0,
381 a.sy * b.sy,
382 a.sx * b.tx + a.tx,
383 a.sy * b.ty + a.ty,
384 )
385 } else {
386 Transform::from_row(
387 mul_add_mul(a.sx, b.sx, a.kx, b.ky),
388 mul_add_mul(a.ky, b.sx, a.sy, b.ky),
389 mul_add_mul(a.sx, b.kx, a.kx, b.sy),
390 mul_add_mul(a.ky, b.kx, a.sy, b.sy),
391 mul_add_mul(a.sx, b.tx, a.kx, b.ty) + a.tx,
392 mul_add_mul(a.ky, b.tx, a.sy, b.ty) + a.ty,
393 )
394 }
395}
396
397fn mul_add_mul(a: f32, b: f32, c: f32, d: f32) -> f32 {
398 (f64::from(a) * f64::from(b) + f64::from(c) * f64::from(d)) as f32
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 #[test]
406 fn transform() {
407 assert_eq!(
408 Transform::identity(),
409 Transform::from_row(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
410 );
411
412 assert_eq!(
413 Transform::from_scale(1.0, 2.0),
414 Transform::from_row(1.0, 0.0, 0.0, 2.0, 0.0, 0.0)
415 );
416
417 assert_eq!(
418 Transform::from_skew(2.0, 3.0),
419 Transform::from_row(1.0, 3.0, 2.0, 1.0, 0.0, 0.0)
420 );
421
422 assert_eq!(
423 Transform::from_translate(5.0, 6.0),
424 Transform::from_row(1.0, 0.0, 0.0, 1.0, 5.0, 6.0)
425 );
426
427 let ts = Transform::identity();
428 assert_eq!(ts.is_identity(), true);
429 assert_eq!(ts.is_scale(), false);
430 assert_eq!(ts.is_skew(), false);
431 assert_eq!(ts.is_translate(), false);
432 assert_eq!(ts.is_scale_translate(), false);
433 assert_eq!(ts.has_scale(), false);
434 assert_eq!(ts.has_skew(), false);
435 assert_eq!(ts.has_translate(), false);
436
437 let ts = Transform::from_scale(2.0, 3.0);
438 assert_eq!(ts.is_identity(), false);
439 assert_eq!(ts.is_scale(), true);
440 assert_eq!(ts.is_skew(), false);
441 assert_eq!(ts.is_translate(), false);
442 assert_eq!(ts.is_scale_translate(), true);
443 assert_eq!(ts.has_scale(), true);
444 assert_eq!(ts.has_skew(), false);
445 assert_eq!(ts.has_translate(), false);
446
447 let ts = Transform::from_skew(2.0, 3.0);
448 assert_eq!(ts.is_identity(), false);
449 assert_eq!(ts.is_scale(), false);
450 assert_eq!(ts.is_skew(), true);
451 assert_eq!(ts.is_translate(), false);
452 assert_eq!(ts.is_scale_translate(), false);
453 assert_eq!(ts.has_scale(), false);
454 assert_eq!(ts.has_skew(), true);
455 assert_eq!(ts.has_translate(), false);
456
457 let ts = Transform::from_translate(2.0, 3.0);
458 assert_eq!(ts.is_identity(), false);
459 assert_eq!(ts.is_scale(), false);
460 assert_eq!(ts.is_skew(), false);
461 assert_eq!(ts.is_translate(), true);
462 assert_eq!(ts.is_scale_translate(), true);
463 assert_eq!(ts.has_scale(), false);
464 assert_eq!(ts.has_skew(), false);
465 assert_eq!(ts.has_translate(), true);
466
467 let ts = Transform::from_row(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
468 assert_eq!(ts.is_identity(), false);
469 assert_eq!(ts.is_scale(), false);
470 assert_eq!(ts.is_skew(), false);
471 assert_eq!(ts.is_translate(), false);
472 assert_eq!(ts.is_scale_translate(), false);
473 assert_eq!(ts.has_scale(), true);
474 assert_eq!(ts.has_skew(), true);
475 assert_eq!(ts.has_translate(), true);
476
477 let ts = Transform::from_scale(1.0, 1.0);
478 assert_eq!(ts.has_scale(), false);
479
480 let ts = Transform::from_skew(0.0, 0.0);
481 assert_eq!(ts.has_skew(), false);
482
483 let ts = Transform::from_translate(0.0, 0.0);
484 assert_eq!(ts.has_translate(), false);
485 }
486
487 #[test]
488 fn concat() {
489 let mut ts = Transform::from_row(1.2, 3.4, -5.6, -7.8, 1.2, 3.4);
490 ts = ts.pre_scale(2.0, -4.0);
491 assert_eq!(ts, Transform::from_row(2.4, 6.8, 22.4, 31.2, 1.2, 3.4));
492
493 let mut ts = Transform::from_row(1.2, 3.4, -5.6, -7.8, 1.2, 3.4);
494 ts = ts.post_scale(2.0, -4.0);
495 assert_eq!(ts, Transform::from_row(2.4, -13.6, -11.2, 31.2, 2.4, -13.6));
496 }
497}
498