| 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 | |