ressa_path/
transform.rs

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::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    pub fn from_rotate(angle: f32) -> Self {
80        let v = angle.to_radians();
81        let a = v.cos();
82        let b = v.sin();
83        let c = -b;
84        let d = a;
85        Transform::from_row(a, b, c, d, 0.0, 0.0)
86    }
87
88    /// Creates a new rotating `Transform` at the specified position.
89    pub fn from_rotate_at(angle: f32, tx: f32, ty: f32) -> Self {
90        let mut ts = Self::default();
91        ts = ts.pre_translate(tx, ty);
92        ts = ts.pre_concat(Transform::from_rotate(angle));
93        ts = ts.pre_translate(-tx, -ty);
94        ts
95    }
96
97    /// Checks that transform is finite.
98    pub fn is_finite(&self) -> bool {
99        self.sx.is_finite()
100            && self.ky.is_finite()
101            && self.kx.is_finite()
102            && self.sy.is_finite()
103            && self.tx.is_finite()
104            && self.ty.is_finite()
105    }
106
107    /// Checks that transform is identity.
108    ///
109    /// The transform type is detected on creation, so this method is essentially free.
110    pub fn is_identity(&self) -> bool {
111        *self == Transform::default()
112    }
113
114    /// Checks that transform is scale-only.
115    ///
116    /// The transform type is detected on creation, so this method is essentially free.
117    pub fn is_scale(&self) -> bool {
118        self.has_scale() && !self.has_skew() && !self.has_translate()
119    }
120
121    /// Checks that transform is skew-only.
122    ///
123    /// The transform type is detected on creation, so this method is essentially free.
124    pub fn is_skew(&self) -> bool {
125        !self.has_scale() && self.has_skew() && !self.has_translate()
126    }
127
128    /// Checks that transform is translate-only.
129    ///
130    /// The transform type is detected on creation, so this method is essentially free.
131    pub fn is_translate(&self) -> bool {
132        !self.has_scale() && !self.has_skew() && self.has_translate()
133    }
134
135    /// Checks that transform contains only scale and translate.
136    ///
137    /// The transform type is detected on creation, so this method is essentially free.
138    pub fn is_scale_translate(&self) -> bool {
139        (self.has_scale() || self.has_translate()) && !self.has_skew()
140    }
141
142    /// Checks that transform contains a scale part.
143    ///
144    /// The transform type is detected on creation, so this method is essentially free.
145    pub fn has_scale(&self) -> bool {
146        self.sx != 1.0 || self.sy != 1.0
147    }
148
149    /// Checks that transform contains a skew part.
150    ///
151    /// The transform type is detected on creation, so this method is essentially free.
152    pub fn has_skew(&self) -> bool {
153        self.kx != 0.0 || self.ky != 0.0
154    }
155
156    /// Checks that transform contains a translate part.
157    ///
158    /// The transform type is detected on creation, so this method is essentially free.
159    pub fn has_translate(&self) -> bool {
160        self.tx != 0.0 || self.ty != 0.0
161    }
162
163    /// Pre-scales the current transform.
164    #[must_use]
165    pub fn pre_scale(&self, sx: f32, sy: f32) -> Self {
166        self.pre_concat(Transform::from_scale(sx, sy))
167    }
168
169    /// Post-scales the current transform.
170    #[must_use]
171    pub fn post_scale(&self, sx: f32, sy: f32) -> Self {
172        self.post_concat(Transform::from_scale(sx, sy))
173    }
174
175    /// Pre-translates the current transform.
176    #[must_use]
177    pub fn pre_translate(&self, tx: f32, ty: f32) -> Self {
178        self.pre_concat(Transform::from_translate(tx, ty))
179    }
180
181    /// Post-translates the current transform.
182    #[must_use]
183    pub fn post_translate(&self, tx: f32, ty: f32) -> Self {
184        self.post_concat(Transform::from_translate(tx, ty))
185    }
186
187    /// Pre-concats the current transform.
188    #[must_use]
189    pub fn pre_concat(&self, other: Self) -> Self {
190        concat(*self, other)
191    }
192
193    /// Post-concats the current transform.
194    #[must_use]
195    pub fn post_concat(&self, other: Self) -> Self {
196        concat(other, *self)
197    }
198
199    pub(crate) fn from_sin_cos(sin: f32, cos: f32) -> Self {
200        Transform::from_row(cos, sin, -sin, cos, 0.0, 0.0)
201    }
202
203    /// Transforms a slice of points using the current transform.
204    pub fn map_points(&self, points: &mut [Point]) {
205        if points.is_empty() {
206            return;
207        }
208
209        // TODO: simd
210
211        if self.is_identity() {
212            // Do nothing.
213        } else if self.is_translate() {
214            for p in points {
215                p.x += self.tx;
216                p.y += self.ty;
217            }
218        } else if self.is_scale_translate() {
219            for p in points {
220                p.x = p.x * self.sx + self.tx;
221                p.y = p.y * self.sy + self.ty;
222            }
223        } else {
224            for p in points {
225                let x = p.x * self.sx + p.y * self.kx + self.tx;
226                let y = p.x * self.ky + p.y * self.sy + self.ty;
227                p.x = x;
228                p.y = y;
229            }
230        }
231    }
232
233    /// Returns an inverted transform.
234    pub fn invert(&self) -> Option<Self> {
235        // Allow the trivial case to be inlined.
236        if self.is_identity() {
237            return Some(*self);
238        }
239
240        invert(self)
241    }
242}
243
244fn invert(ts: &Transform) -> Option<Transform> {
245    debug_assert!(!ts.is_identity());
246
247    if ts.is_scale_translate() {
248        if ts.has_scale() {
249            let inv_x = ts.sx.invert();
250            let inv_y = ts.sy.invert();
251            Some(Transform::from_row(
252                inv_x,
253                0.0,
254                0.0,
255                inv_y,
256                -ts.tx * inv_x,
257                -ts.ty * inv_y,
258            ))
259        } else {
260            // translate only
261            Some(Transform::from_translate(-ts.tx, -ts.ty))
262        }
263    } else {
264        let inv_det = inv_determinant(ts)?;
265        let inv_ts = compute_inv(ts, inv_det);
266
267        if inv_ts.is_finite() {
268            Some(inv_ts)
269        } else {
270            None
271        }
272    }
273}
274
275fn inv_determinant(ts: &Transform) -> Option<f64> {
276    let det = dcross(ts.sx as f64, ts.sy as f64, ts.kx as f64, ts.ky as f64);
277
278    // Since the determinant is on the order of the cube of the matrix members,
279    // compare to the cube of the default nearly-zero constant (although an
280    // estimate of the condition number would be better if it wasn't so expensive).
281    let tolerance = SCALAR_NEARLY_ZERO * SCALAR_NEARLY_ZERO * SCALAR_NEARLY_ZERO;
282    if (det as f32).is_nearly_zero_within_tolerance(tolerance) {
283        None
284    } else {
285        Some(1.0 / det)
286    }
287}
288
289fn compute_inv(ts: &Transform, inv_det: f64) -> Transform {
290    Transform::from_row(
291        (ts.sy as f64 * inv_det) as f32,
292        (-ts.ky as f64 * inv_det) as f32,
293        (-ts.kx as f64 * inv_det) as f32,
294        (ts.sx as f64 * inv_det) as f32,
295        dcross_dscale(ts.kx, ts.ty, ts.sy, ts.tx, inv_det),
296        dcross_dscale(ts.ky, ts.tx, ts.sx, ts.ty, inv_det),
297    )
298}
299
300fn dcross(a: f64, b: f64, c: f64, d: f64) -> f64 {
301    a * b - c * d
302}
303
304fn dcross_dscale(a: f32, b: f32, c: f32, d: f32, scale: f64) -> f32 {
305    (dcross(a as f64, b as f64, c as f64, d as f64) * scale) as f32
306}
307
308fn concat(a: Transform, b: Transform) -> Transform {
309    if a.is_identity() {
310        b
311    } else if b.is_identity() {
312        a
313    } else if !a.has_skew() && !b.has_skew() {
314        // just scale and translate
315        Transform::from_row(
316            a.sx * b.sx,
317            0.0,
318            0.0,
319            a.sy * b.sy,
320            a.sx * b.tx + a.tx,
321            a.sy * b.ty + a.ty,
322        )
323    } else {
324        Transform::from_row(
325            mul_add_mul(a.sx, b.sx, a.kx, b.ky),
326            mul_add_mul(a.ky, b.sx, a.sy, b.ky),
327            mul_add_mul(a.sx, b.kx, a.kx, b.sy),
328            mul_add_mul(a.ky, b.kx, a.sy, b.sy),
329            mul_add_mul(a.sx, b.tx, a.kx, b.ty) + a.tx,
330            mul_add_mul(a.ky, b.tx, a.sy, b.ty) + a.ty,
331        )
332    }
333}
334
335fn mul_add_mul(a: f32, b: f32, c: f32, d: f32) -> f32 {
336    (f64::from(a) * f64::from(b) + f64::from(c) * f64::from(d)) as f32
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    #[test]
344    fn transform() {
345        assert_eq!(
346            Transform::identity(),
347            Transform::from_row(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
348        );
349
350        assert_eq!(
351            Transform::from_scale(1.0, 2.0),
352            Transform::from_row(1.0, 0.0, 0.0, 2.0, 0.0, 0.0)
353        );
354
355        assert_eq!(
356            Transform::from_skew(2.0, 3.0),
357            Transform::from_row(1.0, 3.0, 2.0, 1.0, 0.0, 0.0)
358        );
359
360        assert_eq!(
361            Transform::from_translate(5.0, 6.0),
362            Transform::from_row(1.0, 0.0, 0.0, 1.0, 5.0, 6.0)
363        );
364
365        let ts = Transform::identity();
366        assert_eq!(ts.is_identity(), true);
367        assert_eq!(ts.is_scale(), false);
368        assert_eq!(ts.is_skew(), false);
369        assert_eq!(ts.is_translate(), false);
370        assert_eq!(ts.is_scale_translate(), false);
371        assert_eq!(ts.has_scale(), false);
372        assert_eq!(ts.has_skew(), false);
373        assert_eq!(ts.has_translate(), false);
374
375        let ts = Transform::from_scale(2.0, 3.0);
376        assert_eq!(ts.is_identity(), false);
377        assert_eq!(ts.is_scale(), true);
378        assert_eq!(ts.is_skew(), false);
379        assert_eq!(ts.is_translate(), false);
380        assert_eq!(ts.is_scale_translate(), true);
381        assert_eq!(ts.has_scale(), true);
382        assert_eq!(ts.has_skew(), false);
383        assert_eq!(ts.has_translate(), false);
384
385        let ts = Transform::from_skew(2.0, 3.0);
386        assert_eq!(ts.is_identity(), false);
387        assert_eq!(ts.is_scale(), false);
388        assert_eq!(ts.is_skew(), true);
389        assert_eq!(ts.is_translate(), false);
390        assert_eq!(ts.is_scale_translate(), false);
391        assert_eq!(ts.has_scale(), false);
392        assert_eq!(ts.has_skew(), true);
393        assert_eq!(ts.has_translate(), false);
394
395        let ts = Transform::from_translate(2.0, 3.0);
396        assert_eq!(ts.is_identity(), false);
397        assert_eq!(ts.is_scale(), false);
398        assert_eq!(ts.is_skew(), false);
399        assert_eq!(ts.is_translate(), true);
400        assert_eq!(ts.is_scale_translate(), true);
401        assert_eq!(ts.has_scale(), false);
402        assert_eq!(ts.has_skew(), false);
403        assert_eq!(ts.has_translate(), true);
404
405        let ts = Transform::from_row(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
406        assert_eq!(ts.is_identity(), false);
407        assert_eq!(ts.is_scale(), false);
408        assert_eq!(ts.is_skew(), false);
409        assert_eq!(ts.is_translate(), false);
410        assert_eq!(ts.is_scale_translate(), false);
411        assert_eq!(ts.has_scale(), true);
412        assert_eq!(ts.has_skew(), true);
413        assert_eq!(ts.has_translate(), true);
414
415        let ts = Transform::from_scale(1.0, 1.0);
416        assert_eq!(ts.has_scale(), false);
417
418        let ts = Transform::from_skew(0.0, 0.0);
419        assert_eq!(ts.has_skew(), false);
420
421        let ts = Transform::from_translate(0.0, 0.0);
422        assert_eq!(ts.has_translate(), false);
423    }
424
425    #[test]
426    fn concat() {
427        let mut ts = Transform::from_row(1.2, 3.4, -5.6, -7.8, 1.2, 3.4);
428        ts = ts.pre_scale(2.0, -4.0);
429        assert_eq!(ts, Transform::from_row(2.4, 6.8, 22.4, 31.2, 1.2, 3.4));
430
431        let mut ts = Transform::from_row(1.2, 3.4, -5.6, -7.8, 1.2, 3.4);
432        ts = ts.post_scale(2.0, -4.0);
433        assert_eq!(ts, Transform::from_row(2.4, -13.6, -11.2, 31.2, 2.4, -13.6));
434    }
435}