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 | |
7 | use crate::{NonZeroRect, Point}; |
8 | |
9 | use crate::scalar::{Scalar, SCALAR_NEARLY_ZERO}; |
10 | |
11 | #[cfg (all(not(feature = "std" ), feature = "no-std-float" ))] |
12 | use 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)] |
21 | pub 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 | |
30 | impl 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 | |
43 | impl 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 | |
306 | fn 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 | |
337 | fn 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 | |
351 | fn 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 | |
362 | fn dcross(a: f64, b: f64, c: f64, d: f64) -> f64 { |
363 | a * b - c * d |
364 | } |
365 | |
366 | fn 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 | |
370 | fn 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 | |
397 | fn 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)] |
402 | mod 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 | |