util.rs (54788B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use api::BorderRadius; 6 use api::units::*; 7 use euclid::{Point2D, Rect, Box2D, Size2D, Vector2D, point2, point3}; 8 use euclid::{default, Transform2D, Transform3D, Scale, approxeq::ApproxEq}; 9 use plane_split::{Clipper, Polygon}; 10 use std::{i32, f32, fmt, ptr}; 11 use std::borrow::Cow; 12 use std::num::NonZeroUsize; 13 use std::sync::Arc; 14 use std::mem::replace; 15 16 use crate::internal_types::FrameVec; 17 18 // Matches the definition of SK_ScalarNearlyZero in Skia. 19 const NEARLY_ZERO: f32 = 1.0 / 4096.0; 20 21 /// A typesafe helper that separates new value construction from 22 /// vector growing, allowing LLVM to ideally construct the element in place. 23 pub struct Allocation<'a, T: 'a> { 24 vec: &'a mut Vec<T>, 25 index: usize, 26 } 27 28 impl<'a, T> Allocation<'a, T> { 29 // writing is safe because alloc() ensured enough capacity 30 // and `Allocation` holds a mutable borrow to prevent anyone else 31 // from breaking this invariant. 32 #[inline(always)] 33 pub fn init(self, value: T) -> usize { 34 unsafe { 35 ptr::write(self.vec.as_mut_ptr().add(self.index), value); 36 self.vec.set_len(self.index + 1); 37 } 38 self.index 39 } 40 } 41 42 /// An entry into a vector, similar to `std::collections::hash_map::Entry`. 43 pub enum VecEntry<'a, T: 'a> { 44 Vacant(Allocation<'a, T>), 45 Occupied(&'a mut T), 46 } 47 48 impl<'a, T> VecEntry<'a, T> { 49 #[inline(always)] 50 pub fn set(self, value: T) { 51 match self { 52 VecEntry::Vacant(alloc) => { alloc.init(value); } 53 VecEntry::Occupied(slot) => { *slot = value; } 54 } 55 } 56 } 57 58 pub trait VecHelper<T> { 59 /// Growns the vector by a single entry, returning the allocation. 60 fn alloc(&mut self) -> Allocation<T>; 61 /// Either returns an existing elemenet, or grows the vector by one. 62 /// Doesn't expect indices to be higher than the current length. 63 fn entry(&mut self, index: usize) -> VecEntry<T>; 64 65 /// Equivalent to `mem::replace(&mut vec, Vec::new())` 66 fn take(&mut self) -> Self; 67 68 /// Functionally equivalent to `mem::replace(&mut vec, Vec::new())` but tries 69 /// to keep the allocation in the caller if it is empty or replace it with a 70 /// pre-allocated vector. 71 fn take_and_preallocate(&mut self) -> Self; 72 } 73 74 impl<T> VecHelper<T> for Vec<T> { 75 fn alloc(&mut self) -> Allocation<T> { 76 let index = self.len(); 77 if self.capacity() == index { 78 self.reserve(1); 79 } 80 Allocation { 81 vec: self, 82 index, 83 } 84 } 85 86 fn entry(&mut self, index: usize) -> VecEntry<T> { 87 if index < self.len() { 88 VecEntry::Occupied(unsafe { 89 self.get_unchecked_mut(index) 90 }) 91 } else { 92 assert_eq!(index, self.len()); 93 VecEntry::Vacant(self.alloc()) 94 } 95 } 96 97 fn take(&mut self) -> Self { 98 replace(self, Vec::new()) 99 } 100 101 fn take_and_preallocate(&mut self) -> Self { 102 let len = self.len(); 103 if len == 0 { 104 self.clear(); 105 return Vec::new(); 106 } 107 replace(self, Vec::with_capacity(len + 8)) 108 } 109 } 110 111 // Represents an optimized transform where there is only 112 // a scale and translation (which are guaranteed to maintain 113 // an axis align rectangle under transformation). The 114 // scaling is applied first, followed by the translation. 115 // TODO(gw): We should try and incorporate F <-> T units here, 116 // but it's a bit tricky to do that now with the 117 // way the current spatial tree works. 118 #[repr(C)] 119 #[derive(Debug, Clone, Copy, MallocSizeOf, PartialEq)] 120 #[cfg_attr(feature = "capture", derive(Serialize))] 121 #[cfg_attr(feature = "replay", derive(Deserialize))] 122 pub struct ScaleOffset { 123 pub scale: euclid::Vector2D<f32, euclid::UnknownUnit>, 124 pub offset: euclid::Vector2D<f32, euclid::UnknownUnit>, 125 } 126 127 impl ScaleOffset { 128 pub fn new(sx: f32, sy: f32, tx: f32, ty: f32) -> Self { 129 ScaleOffset { 130 scale: Vector2D::new(sx, sy), 131 offset: Vector2D::new(tx, ty), 132 } 133 } 134 135 pub fn identity() -> Self { 136 ScaleOffset { 137 scale: Vector2D::new(1.0, 1.0), 138 offset: Vector2D::zero(), 139 } 140 } 141 142 // Construct a ScaleOffset from a transform. Returns 143 // None if the matrix is not a pure scale / translation. 144 pub fn from_transform<F, T>( 145 m: &Transform3D<f32, F, T>, 146 ) -> Option<ScaleOffset> { 147 148 // To check that we have a pure scale / translation: 149 // Every field must match an identity matrix, except: 150 // - Any value present in tx,ty 151 // - Any value present in sx,sy 152 153 if m.m12.abs() > NEARLY_ZERO || 154 m.m13.abs() > NEARLY_ZERO || 155 m.m14.abs() > NEARLY_ZERO || 156 m.m21.abs() > NEARLY_ZERO || 157 m.m23.abs() > NEARLY_ZERO || 158 m.m24.abs() > NEARLY_ZERO || 159 m.m31.abs() > NEARLY_ZERO || 160 m.m32.abs() > NEARLY_ZERO || 161 (m.m33 - 1.0).abs() > NEARLY_ZERO || 162 m.m34.abs() > NEARLY_ZERO || 163 m.m43.abs() > NEARLY_ZERO || 164 (m.m44 - 1.0).abs() > NEARLY_ZERO { 165 return None; 166 } 167 168 Some(ScaleOffset { 169 scale: Vector2D::new(m.m11, m.m22), 170 offset: Vector2D::new(m.m41, m.m42), 171 }) 172 } 173 174 pub fn from_offset(offset: default::Vector2D<f32>) -> Self { 175 ScaleOffset { 176 scale: Vector2D::new(1.0, 1.0), 177 offset, 178 } 179 } 180 181 pub fn from_scale(scale: default::Vector2D<f32>) -> Self { 182 ScaleOffset { 183 scale, 184 offset: Vector2D::new(0.0, 0.0), 185 } 186 } 187 188 pub fn inverse(&self) -> Self { 189 // If either of the scale factors is 0, inverse also has scale 0 190 // TODO(gw): Consider making this return Option<Self> in future 191 // so that callers can detect and handle when inverse 192 // fails here. 193 if self.scale.x.approx_eq(&0.0) || self.scale.y.approx_eq(&0.0) { 194 return ScaleOffset::new(0.0, 0.0, 0.0, 0.0); 195 } 196 197 ScaleOffset { 198 scale: Vector2D::new( 199 1.0 / self.scale.x, 200 1.0 / self.scale.y, 201 ), 202 offset: Vector2D::new( 203 -self.offset.x / self.scale.x, 204 -self.offset.y / self.scale.y, 205 ), 206 } 207 } 208 209 pub fn pre_offset(&self, offset: default::Vector2D<f32>) -> Self { 210 self.pre_transform( 211 &ScaleOffset { 212 scale: Vector2D::new(1.0, 1.0), 213 offset, 214 } 215 ) 216 } 217 218 pub fn pre_scale(&self, scale: f32) -> Self { 219 ScaleOffset { 220 scale: self.scale * scale, 221 offset: self.offset, 222 } 223 } 224 225 pub fn then_scale(&self, scale: f32) -> Self { 226 ScaleOffset { 227 scale: self.scale * scale, 228 offset: self.offset * scale, 229 } 230 } 231 232 /// Produce a ScaleOffset that includes both self and other. 233 /// The 'self' ScaleOffset is applied after `other`. 234 /// This is equivalent to `Transform3D::pre_transform`. 235 pub fn pre_transform(&self, other: &ScaleOffset) -> Self { 236 ScaleOffset { 237 scale: Vector2D::new( 238 self.scale.x * other.scale.x, 239 self.scale.y * other.scale.y, 240 ), 241 offset: Vector2D::new( 242 self.offset.x + self.scale.x * other.offset.x, 243 self.offset.y + self.scale.y * other.offset.y, 244 ), 245 } 246 } 247 248 /// Produce a ScaleOffset that includes both self and other. 249 /// The 'other' ScaleOffset is applied after `self`. 250 /// This is equivalent to `Transform3D::then`. 251 #[allow(unused)] 252 pub fn then(&self, other: &ScaleOffset) -> Self { 253 ScaleOffset { 254 scale: Vector2D::new( 255 self.scale.x * other.scale.x, 256 self.scale.y * other.scale.y, 257 ), 258 offset: Vector2D::new( 259 other.scale.x * self.offset.x + other.offset.x, 260 other.scale.y * self.offset.y + other.offset.y, 261 ), 262 } 263 } 264 265 266 pub fn map_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> { 267 // TODO(gw): The logic below can return an unexpected result if the supplied 268 // rect is invalid (has size < 0). Since Gecko currently supplied 269 // invalid rects in some cases, adding a max(0) here ensures that 270 // mapping an invalid rect retains the property that rect.is_empty() 271 // will return true (the mapped rect output will have size 0 instead 272 // of a negative size). In future we could catch / assert / fix 273 // these invalid rects earlier, and assert here instead. 274 275 let w = rect.width().max(0.0); 276 let h = rect.height().max(0.0); 277 278 let mut x0 = rect.min.x * self.scale.x + self.offset.x; 279 let mut y0 = rect.min.y * self.scale.y + self.offset.y; 280 281 let mut sx = w * self.scale.x; 282 let mut sy = h * self.scale.y; 283 // Handle negative scale. Previously, branchless float math was used to find the 284 // min / max vertices and size. However, that sequence of operations was producind 285 // additional floating point accuracy on android emulator builds, causing one test 286 // to fail an assert. Instead, we retain the same math as previously, and adjust 287 // the origin / size if required. 288 289 if self.scale.x < 0.0 { 290 x0 += sx; 291 sx = -sx; 292 } 293 if self.scale.y < 0.0 { 294 y0 += sy; 295 sy = -sy; 296 } 297 298 Box2D::from_origin_and_size( 299 Point2D::new(x0, y0), 300 Size2D::new(sx, sy), 301 ) 302 } 303 304 pub fn unmap_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> { 305 // TODO(gw): The logic below can return an unexpected result if the supplied 306 // rect is invalid (has size < 0). Since Gecko currently supplied 307 // invalid rects in some cases, adding a max(0) here ensures that 308 // mapping an invalid rect retains the property that rect.is_empty() 309 // will return true (the mapped rect output will have size 0 instead 310 // of a negative size). In future we could catch / assert / fix 311 // these invalid rects earlier, and assert here instead. 312 313 let w = rect.width().max(0.0); 314 let h = rect.height().max(0.0); 315 316 let mut x0 = (rect.min.x - self.offset.x) / self.scale.x; 317 let mut y0 = (rect.min.y - self.offset.y) / self.scale.y; 318 319 let mut sx = w / self.scale.x; 320 let mut sy = h / self.scale.y; 321 322 // Handle negative scale. Previously, branchless float math was used to find the 323 // min / max vertices and size. However, that sequence of operations was producind 324 // additional floating point accuracy on android emulator builds, causing one test 325 // to fail an assert. Instead, we retain the same math as previously, and adjust 326 // the origin / size if required. 327 328 if self.scale.x < 0.0 { 329 x0 += sx; 330 sx = -sx; 331 } 332 if self.scale.y < 0.0 { 333 y0 += sy; 334 sy = -sy; 335 } 336 337 Box2D::from_origin_and_size( 338 Point2D::new(x0, y0), 339 Size2D::new(sx, sy), 340 ) 341 } 342 343 pub fn map_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> { 344 Vector2D::new( 345 vector.x * self.scale.x, 346 vector.y * self.scale.y, 347 ) 348 } 349 350 pub fn map_size<F, T>(&self, size: &Size2D<f32, F>) -> Size2D<f32, T> { 351 Size2D::new( 352 size.width * self.scale.x, 353 size.height * self.scale.y, 354 ) 355 } 356 357 pub fn unmap_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> { 358 Vector2D::new( 359 vector.x / self.scale.x, 360 vector.y / self.scale.y, 361 ) 362 } 363 364 pub fn map_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> { 365 Point2D::new( 366 point.x * self.scale.x + self.offset.x, 367 point.y * self.scale.y + self.offset.y, 368 ) 369 } 370 371 pub fn unmap_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> { 372 Point2D::new( 373 (point.x - self.offset.x) / self.scale.x, 374 (point.y - self.offset.y) / self.scale.y, 375 ) 376 } 377 378 pub fn to_transform<F, T>(&self) -> Transform3D<f32, F, T> { 379 Transform3D::new( 380 self.scale.x, 381 0.0, 382 0.0, 383 0.0, 384 385 0.0, 386 self.scale.y, 387 0.0, 388 0.0, 389 390 0.0, 391 0.0, 392 1.0, 393 0.0, 394 395 self.offset.x, 396 self.offset.y, 397 0.0, 398 1.0, 399 ) 400 } 401 } 402 403 // TODO: Implement these in euclid! 404 pub trait MatrixHelpers<Src, Dst> { 405 /// A port of the preserves2dAxisAlignment function in Skia. 406 /// Defined in the SkMatrix44 class. 407 fn preserves_2d_axis_alignment(&self) -> bool; 408 fn has_perspective_component(&self) -> bool; 409 fn has_2d_inverse(&self) -> bool; 410 /// Check if the matrix post-scaling on either the X or Y axes could cause geometry 411 /// transformed by this matrix to have scaling exceeding the supplied limit. 412 fn exceeds_2d_scale(&self, limit: f64) -> bool; 413 fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>>; 414 fn inverse_rect_footprint(&self, rect: &Box2D<f32, Dst>) -> Option<Box2D<f32, Src>>; 415 fn transform_kind(&self) -> TransformedRectKind; 416 fn is_simple_translation(&self) -> bool; 417 fn is_simple_2d_translation(&self) -> bool; 418 fn is_2d_scale_translation(&self) -> bool; 419 /// Return the determinant of the 2D part of the matrix. 420 fn determinant_2d(&self) -> f32; 421 /// Turn Z transformation into identity. This is useful when crossing "flat" 422 /// transform styled stacking contexts upon traversing the coordinate systems. 423 fn flatten_z_output(&mut self); 424 425 fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst>; 426 } 427 428 impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { 429 fn preserves_2d_axis_alignment(&self) -> bool { 430 if self.m14 != 0.0 || self.m24 != 0.0 { 431 return false; 432 } 433 434 let mut col0 = 0; 435 let mut col1 = 0; 436 let mut row0 = 0; 437 let mut row1 = 0; 438 439 if self.m11.abs() > NEARLY_ZERO { 440 col0 += 1; 441 row0 += 1; 442 } 443 if self.m12.abs() > NEARLY_ZERO { 444 col1 += 1; 445 row0 += 1; 446 } 447 if self.m21.abs() > NEARLY_ZERO { 448 col0 += 1; 449 row1 += 1; 450 } 451 if self.m22.abs() > NEARLY_ZERO { 452 col1 += 1; 453 row1 += 1; 454 } 455 456 col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2 457 } 458 459 fn has_perspective_component(&self) -> bool { 460 self.m14.abs() > NEARLY_ZERO || 461 self.m24.abs() > NEARLY_ZERO || 462 self.m34.abs() > NEARLY_ZERO || 463 (self.m44 - 1.0).abs() > NEARLY_ZERO 464 } 465 466 fn has_2d_inverse(&self) -> bool { 467 self.determinant_2d() != 0.0 468 } 469 470 fn exceeds_2d_scale(&self, limit: f64) -> bool { 471 let limit2 = (limit * limit) as f32; 472 self.m11 * self.m11 + self.m12 * self.m12 > limit2 || 473 self.m21 * self.m21 + self.m22 * self.m22 > limit2 474 } 475 476 /// Find out a point in `Src` that would be projected into the `target`. 477 fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>> { 478 // form the linear equation for the hyperplane intersection 479 let m = Transform2D::<f32, Src, Dst>::new( 480 self.m11 - target.x * self.m14, self.m12 - target.y * self.m14, 481 self.m21 - target.x * self.m24, self.m22 - target.y * self.m24, 482 self.m41 - target.x * self.m44, self.m42 - target.y * self.m44, 483 ); 484 let inv = m.inverse()?; 485 // we found the point, now check if it maps to the positive hemisphere 486 if inv.m31 * self.m14 + inv.m32 * self.m24 + self.m44 > 0.0 { 487 Some(Point2D::new(inv.m31, inv.m32)) 488 } else { 489 None 490 } 491 } 492 493 fn inverse_rect_footprint(&self, rect: &Box2D<f32, Dst>) -> Option<Box2D<f32, Src>> { 494 Some(Box2D::from_points(&[ 495 self.inverse_project(&rect.top_left())?, 496 self.inverse_project(&rect.top_right())?, 497 self.inverse_project(&rect.bottom_left())?, 498 self.inverse_project(&rect.bottom_right())?, 499 ])) 500 } 501 502 fn transform_kind(&self) -> TransformedRectKind { 503 if self.preserves_2d_axis_alignment() { 504 TransformedRectKind::AxisAligned 505 } else { 506 TransformedRectKind::Complex 507 } 508 } 509 510 fn is_simple_translation(&self) -> bool { 511 if (self.m11 - 1.0).abs() > NEARLY_ZERO || 512 (self.m22 - 1.0).abs() > NEARLY_ZERO || 513 (self.m33 - 1.0).abs() > NEARLY_ZERO || 514 (self.m44 - 1.0).abs() > NEARLY_ZERO { 515 return false; 516 } 517 518 self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO && 519 self.m14.abs() < NEARLY_ZERO && self.m21.abs() < NEARLY_ZERO && 520 self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO && 521 self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO && 522 self.m34.abs() < NEARLY_ZERO 523 } 524 525 fn is_simple_2d_translation(&self) -> bool { 526 if !self.is_simple_translation() { 527 return false; 528 } 529 530 self.m43.abs() < NEARLY_ZERO 531 } 532 533 /* is this... 534 * X 0 0 0 535 * 0 Y 0 0 536 * 0 0 1 0 537 * a b 0 1 538 */ 539 fn is_2d_scale_translation(&self) -> bool { 540 (self.m33 - 1.0).abs() < NEARLY_ZERO && 541 (self.m44 - 1.0).abs() < NEARLY_ZERO && 542 self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO && self.m14.abs() < NEARLY_ZERO && 543 self.m21.abs() < NEARLY_ZERO && self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO && 544 self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO && self.m34.abs() < NEARLY_ZERO && 545 self.m43.abs() < NEARLY_ZERO 546 } 547 548 fn determinant_2d(&self) -> f32 { 549 self.m11 * self.m22 - self.m12 * self.m21 550 } 551 552 fn flatten_z_output(&mut self) { 553 self.m13 = 0.0; 554 self.m23 = 0.0; 555 self.m33 = 1.0; 556 self.m43 = 0.0; 557 //Note: we used to zero out m3? as well, see "reftests/flatten-all-flat.yaml" test 558 } 559 560 fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst> { 561 Transform3D::new( 562 self.m11, self.m12, self.m13, self.m14, 563 self.m21, self.m22, self.m23, self.m24, 564 self.m31, self.m32, self.m33, self.m34, 565 self.m41, self.m42, self.m43, self.m44, 566 ) 567 } 568 } 569 570 pub trait PointHelpers<U> 571 where 572 Self: Sized, 573 { 574 fn snap(&self) -> Self; 575 } 576 577 impl<U> PointHelpers<U> for Point2D<f32, U> { 578 fn snap(&self) -> Self { 579 Point2D::new( 580 self.x.round(), 581 self.y.round(), 582 ) 583 } 584 } 585 586 pub trait VectorHelpers<U> 587 where 588 Self: Sized, 589 { 590 fn snap(&self) -> Self; 591 } 592 593 impl<U> VectorHelpers<U> for Vector2D<f32, U> { 594 fn snap(&self) -> Self { 595 Vector2D::new( 596 self.x.round(), 597 self.y.round(), 598 ) 599 } 600 } 601 602 pub trait RectHelpers<U> 603 where 604 Self: Sized, 605 { 606 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self; 607 fn snap(&self) -> Self; 608 } 609 610 impl<U> RectHelpers<U> for Rect<f32, U> { 611 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self { 612 Rect::new( 613 Point2D::new(x0, y0), 614 Size2D::new(x1 - x0, y1 - y0), 615 ) 616 } 617 618 fn snap(&self) -> Self { 619 let origin = Point2D::new( 620 (self.origin.x + 0.5).floor(), 621 (self.origin.y + 0.5).floor(), 622 ); 623 Rect::new( 624 origin, 625 Size2D::new( 626 (self.origin.x + self.size.width + 0.5).floor() - origin.x, 627 (self.origin.y + self.size.height + 0.5).floor() - origin.y, 628 ), 629 ) 630 } 631 } 632 633 impl<U> RectHelpers<U> for Box2D<f32, U> { 634 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self { 635 Box2D { 636 min: Point2D::new(x0, y0), 637 max: Point2D::new(x1, y1), 638 } 639 } 640 641 fn snap(&self) -> Self { 642 self.round() 643 } 644 } 645 646 pub fn lerp(a: f32, b: f32, t: f32) -> f32 { 647 (b - a) * t + a 648 } 649 650 #[repr(u32)] 651 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 652 #[cfg_attr(feature = "capture", derive(Serialize))] 653 #[cfg_attr(feature = "replay", derive(Deserialize))] 654 pub enum TransformedRectKind { 655 AxisAligned = 0, 656 Complex = 1, 657 } 658 659 #[inline(always)] 660 pub fn pack_as_float(value: u32) -> f32 { 661 value as f32 + 0.5 662 } 663 664 #[inline] 665 fn extract_inner_rect_impl<U>( 666 rect: &Box2D<f32, U>, 667 radii: &BorderRadius, 668 k: f32, 669 ) -> Option<Box2D<f32, U>> { 670 // `k` defines how much border is taken into account 671 // We enforce the offsets to be rounded to pixel boundaries 672 // by `ceil`-ing and `floor`-ing them 673 674 let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil(); 675 let xr = (rect.width() - k * radii.top_right.width.max(radii.bottom_right.width)).floor(); 676 let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil(); 677 let yb = 678 (rect.height() - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor(); 679 680 if xl <= xr && yt <= yb { 681 Some(Box2D::from_origin_and_size( 682 Point2D::new(rect.min.x + xl, rect.min.y + yt), 683 Size2D::new(xr - xl, yb - yt), 684 )) 685 } else { 686 None 687 } 688 } 689 690 /// Return an aligned rectangle that is inside the clip region and doesn't intersect 691 /// any of the bounding rectangles of the rounded corners. 692 pub fn extract_inner_rect_safe<U>( 693 rect: &Box2D<f32, U>, 694 radii: &BorderRadius, 695 ) -> Option<Box2D<f32, U>> { 696 // value of `k==1.0` is used for extraction of the corner rectangles 697 // see `SEGMENT_CORNER_*` in `clip_shared.glsl` 698 extract_inner_rect_impl(rect, radii, 1.0) 699 } 700 701 /// Return an aligned rectangle that is inside the clip region and doesn't intersect 702 /// any of the bounding rectangles of the rounded corners, with a specific k factor 703 /// to control how much of the rounded corner is included. 704 pub fn extract_inner_rect_k<U>( 705 rect: &Box2D<f32, U>, 706 radii: &BorderRadius, 707 k: f32, 708 ) -> Option<Box2D<f32, U>> { 709 extract_inner_rect_impl(rect, radii, k) 710 } 711 712 #[cfg(test)] 713 use euclid::vec3; 714 715 #[cfg(test)] 716 pub mod test { 717 use super::*; 718 use euclid::default::{Point2D, Size2D, Transform3D}; 719 use euclid::{Angle, approxeq::ApproxEq}; 720 use std::f32::consts::PI; 721 use crate::clip::{is_left_of_line, polygon_contains_point}; 722 use crate::prim_store::PolygonKey; 723 use api::FillRule; 724 725 #[test] 726 fn inverse_project() { 727 let m0 = Transform3D::identity(); 728 let p0 = Point2D::new(1.0, 2.0); 729 // an identical transform doesn't need any inverse projection 730 assert_eq!(m0.inverse_project(&p0), Some(p0)); 731 let m1 = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-PI / 3.0)); 732 // rotation by 60 degrees would imply scaling of X component by a factor of 2 733 assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0))); 734 } 735 736 #[test] 737 fn inverse_project_footprint() { 738 let m = Transform3D::new( 739 0.477499992, 0.135000005, -1.0, 0.000624999986, 740 -0.642787635, 0.766044438, 0.0, 0.0, 741 0.766044438, 0.642787635, 0.0, 0.0, 742 1137.10986, 113.71286, 402.0, 0.748749971, 743 ); 744 let r = Box2D::from_size(Size2D::new(804.0, 804.0)); 745 { 746 let points = &[ 747 r.top_left(), 748 r.top_right(), 749 r.bottom_left(), 750 r.bottom_right(), 751 ]; 752 let mi = m.inverse().unwrap(); 753 // In this section, we do the forward and backward transformation 754 // to confirm that its bijective. 755 // We also do the inverse projection path, and confirm it functions the same way. 756 info!("Points:"); 757 for p in points { 758 let pp = m.transform_point2d_homogeneous(*p); 759 let p3 = pp.to_point3d().unwrap(); 760 let pi = mi.transform_point3d_homogeneous(p3); 761 let px = pi.to_point2d().unwrap(); 762 let py = m.inverse_project(&pp.to_point2d().unwrap()).unwrap(); 763 info!("\t{:?} -> {:?} -> {:?} -> ({:?} -> {:?}, {:?})", p, pp, p3, pi, px, py); 764 assert!(px.approx_eq_eps(p, &Point2D::new(0.001, 0.001))); 765 assert!(py.approx_eq_eps(p, &Point2D::new(0.001, 0.001))); 766 } 767 } 768 // project 769 let rp = project_rect(&m, &r, &Box2D::from_size(Size2D::new(1000.0, 1000.0))).unwrap(); 770 info!("Projected {:?}", rp); 771 // one of the points ends up in the negative hemisphere 772 assert_eq!(m.inverse_project(&rp.min), None); 773 // inverse 774 if let Some(ri) = m.inverse_rect_footprint(&rp) { 775 // inverse footprint should be larger, since it doesn't know the original Z 776 assert!(ri.contains_box(&r), "Inverse {:?}", ri); 777 } 778 } 779 780 fn validate_convert(xref: &LayoutTransform) { 781 let so = ScaleOffset::from_transform(xref).unwrap(); 782 let xf = so.to_transform(); 783 assert!(xref.approx_eq(&xf)); 784 } 785 786 #[test] 787 fn negative_scale_map_unmap() { 788 let xref = LayoutTransform::scale(1.0, -1.0, 1.0) 789 .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); 790 let so = ScaleOffset::from_transform(&xref).unwrap(); 791 let local_rect = Box2D { 792 min: LayoutPoint::new(50.0, -100.0), 793 max: LayoutPoint::new(250.0, 300.0), 794 }; 795 796 let mapped_rect = so.map_rect::<LayoutPixel, DevicePixel>(&local_rect); 797 let xf_rect = project_rect( 798 &xref, 799 &local_rect, 800 &LayoutRect::max_rect(), 801 ).unwrap(); 802 803 assert!(mapped_rect.min.x.approx_eq(&xf_rect.min.x)); 804 assert!(mapped_rect.min.y.approx_eq(&xf_rect.min.y)); 805 assert!(mapped_rect.max.x.approx_eq(&xf_rect.max.x)); 806 assert!(mapped_rect.max.y.approx_eq(&xf_rect.max.y)); 807 808 let unmapped_rect = so.unmap_rect::<DevicePixel, LayoutPixel>(&mapped_rect); 809 assert!(unmapped_rect.min.x.approx_eq(&local_rect.min.x)); 810 assert!(unmapped_rect.min.y.approx_eq(&local_rect.min.y)); 811 assert!(unmapped_rect.max.x.approx_eq(&local_rect.max.x)); 812 assert!(unmapped_rect.max.y.approx_eq(&local_rect.max.y)); 813 } 814 815 #[test] 816 fn scale_offset_convert() { 817 let xref = LayoutTransform::translation(130.0, 200.0, 0.0); 818 validate_convert(&xref); 819 820 let xref = LayoutTransform::scale(13.0, 8.0, 1.0); 821 validate_convert(&xref); 822 823 let xref = LayoutTransform::scale(0.5, 0.5, 1.0) 824 .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); 825 validate_convert(&xref); 826 827 let xref = LayoutTransform::scale(30.0, 11.0, 1.0) 828 .then_translate(vec3(50.0, 240.0, 0.0)); 829 validate_convert(&xref); 830 } 831 832 fn validate_inverse(xref: &LayoutTransform) { 833 let s0 = ScaleOffset::from_transform(xref).unwrap(); 834 let s1 = s0.inverse().pre_transform(&s0); 835 assert!((s1.scale.x - 1.0).abs() < NEARLY_ZERO && 836 (s1.scale.y - 1.0).abs() < NEARLY_ZERO && 837 s1.offset.x.abs() < NEARLY_ZERO && 838 s1.offset.y.abs() < NEARLY_ZERO, 839 "{:?}", 840 s1); 841 } 842 843 #[test] 844 fn scale_offset_inverse() { 845 let xref = LayoutTransform::translation(130.0, 200.0, 0.0); 846 validate_inverse(&xref); 847 848 let xref = LayoutTransform::scale(13.0, 8.0, 1.0); 849 validate_inverse(&xref); 850 851 let xref = LayoutTransform::translation(124.0, 38.0, 0.0). 852 then_scale(0.5, 0.5, 1.0); 853 854 validate_inverse(&xref); 855 856 let xref = LayoutTransform::scale(30.0, 11.0, 1.0) 857 .then_translate(vec3(50.0, 240.0, 0.0)); 858 validate_inverse(&xref); 859 } 860 861 fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) { 862 let x = x1.then(&x0); 863 864 let s0 = ScaleOffset::from_transform(x0).unwrap(); 865 let s1 = ScaleOffset::from_transform(x1).unwrap(); 866 867 let s = s0.pre_transform(&s1).to_transform(); 868 869 assert!(x.approx_eq(&s), "{:?}\n{:?}", x, s); 870 } 871 872 #[test] 873 fn scale_offset_accumulate() { 874 let x0 = LayoutTransform::translation(130.0, 200.0, 0.0); 875 let x1 = LayoutTransform::scale(7.0, 3.0, 1.0); 876 877 validate_accumulate(&x0, &x1); 878 } 879 880 #[test] 881 fn scale_offset_invalid_scale() { 882 let s0 = ScaleOffset::new(0.0, 1.0, 10.0, 20.0); 883 let i0 = s0.inverse(); 884 assert_eq!(i0, ScaleOffset::new(0.0, 0.0, 0.0, 0.0)); 885 886 let s1 = ScaleOffset::new(1.0, 0.0, 10.0, 20.0); 887 let i1 = s1.inverse(); 888 assert_eq!(i1, ScaleOffset::new(0.0, 0.0, 0.0, 0.0)); 889 } 890 891 #[test] 892 fn polygon_clip_is_left_of_point() { 893 // Define points of a line through (1, -3) and (-2, 6) to test against. 894 // If the triplet consisting of these two points and the test point 895 // form a counter-clockwise triangle, then the test point is on the 896 // left. The easiest way to visualize this is with an "ascending" 897 // line from low-Y to high-Y. 898 let p0_x = 1.0; 899 let p0_y = -3.0; 900 let p1_x = -2.0; 901 let p1_y = 6.0; 902 903 // Test some points to the left of the line. 904 assert!(is_left_of_line(-9.0, 0.0, p0_x, p0_y, p1_x, p1_y) > 0.0); 905 assert!(is_left_of_line(-1.0, 1.0, p0_x, p0_y, p1_x, p1_y) > 0.0); 906 assert!(is_left_of_line(1.0, -4.0, p0_x, p0_y, p1_x, p1_y) > 0.0); 907 908 // Test some points on the line. 909 assert!(is_left_of_line(-3.0, 9.0, p0_x, p0_y, p1_x, p1_y) == 0.0); 910 assert!(is_left_of_line(0.0, 0.0, p0_x, p0_y, p1_x, p1_y) == 0.0); 911 assert!(is_left_of_line(100.0, -300.0, p0_x, p0_y, p1_x, p1_y) == 0.0); 912 913 // Test some points to the right of the line. 914 assert!(is_left_of_line(0.0, 1.0, p0_x, p0_y, p1_x, p1_y) < 0.0); 915 assert!(is_left_of_line(-4.0, 13.0, p0_x, p0_y, p1_x, p1_y) < 0.0); 916 assert!(is_left_of_line(5.0, -12.0, p0_x, p0_y, p1_x, p1_y) < 0.0); 917 } 918 919 #[test] 920 fn polygon_clip_contains_point() { 921 // We define the points of a self-overlapping polygon, which we will 922 // use to create polygons with different windings and fill rules. 923 let p0 = LayoutPoint::new(4.0, 4.0); 924 let p1 = LayoutPoint::new(6.0, 4.0); 925 let p2 = LayoutPoint::new(4.0, 7.0); 926 let p3 = LayoutPoint::new(2.0, 1.0); 927 let p4 = LayoutPoint::new(8.0, 1.0); 928 let p5 = LayoutPoint::new(6.0, 7.0); 929 930 let poly_clockwise_nonzero = PolygonKey::new( 931 &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Nonzero 932 ); 933 let poly_clockwise_evenodd = PolygonKey::new( 934 &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Evenodd 935 ); 936 let poly_counter_clockwise_nonzero = PolygonKey::new( 937 &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Nonzero 938 ); 939 let poly_counter_clockwise_evenodd = PolygonKey::new( 940 &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Evenodd 941 ); 942 943 // We define a rect that provides a bounding clip area of 944 // the polygon. 945 let rect = LayoutRect::from_size(LayoutSize::new(10.0, 10.0)); 946 947 // And we'll test three points of interest. 948 let p_inside_once = LayoutPoint::new(5.0, 3.0); 949 let p_inside_twice = LayoutPoint::new(5.0, 5.0); 950 let p_outside = LayoutPoint::new(9.0, 9.0); 951 952 // We should get the same results for both clockwise and 953 // counter-clockwise polygons. 954 // For nonzero polygons, the inside twice point is considered inside. 955 for poly_nonzero in vec![poly_clockwise_nonzero, poly_counter_clockwise_nonzero].iter() { 956 assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_nonzero), true); 957 assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_nonzero), true); 958 assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_nonzero), false); 959 } 960 // For evenodd polygons, the inside twice point is considered outside. 961 for poly_evenodd in vec![poly_clockwise_evenodd, poly_counter_clockwise_evenodd].iter() { 962 assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_evenodd), true); 963 assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_evenodd), false); 964 assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_evenodd), false); 965 } 966 } 967 } 968 969 pub trait MaxRect { 970 fn max_rect() -> Self; 971 } 972 973 impl MaxRect for DeviceIntRect { 974 fn max_rect() -> Self { 975 DeviceIntRect::from_origin_and_size( 976 DeviceIntPoint::new(i32::MIN / 2, i32::MIN / 2), 977 DeviceIntSize::new(i32::MAX, i32::MAX), 978 ) 979 } 980 } 981 982 impl<U> MaxRect for Rect<f32, U> { 983 fn max_rect() -> Self { 984 // Having an unlimited bounding box is fine up until we try 985 // to cast it to `i32`, where we get `-2147483648` for any 986 // values larger than or equal to 2^31. 987 // 988 // Note: clamping to i32::MIN and i32::MAX is not a solution, 989 // with explanation left as an exercise for the reader. 990 const MAX_COORD: f32 = 1.0e9; 991 992 Rect::new( 993 Point2D::new(-MAX_COORD, -MAX_COORD), 994 Size2D::new(2.0 * MAX_COORD, 2.0 * MAX_COORD), 995 ) 996 } 997 } 998 999 impl<U> MaxRect for Box2D<f32, U> { 1000 fn max_rect() -> Self { 1001 // Having an unlimited bounding box is fine up until we try 1002 // to cast it to `i32`, where we get `-2147483648` for any 1003 // values larger than or equal to 2^31. 1004 // 1005 // Note: clamping to i32::MIN and i32::MAX is not a solution, 1006 // with explanation left as an exercise for the reader. 1007 const MAX_COORD: f32 = 1.0e9; 1008 1009 Box2D::new( 1010 Point2D::new(-MAX_COORD, -MAX_COORD), 1011 Point2D::new(MAX_COORD, MAX_COORD), 1012 ) 1013 } 1014 } 1015 1016 /// An enum that tries to avoid expensive transformation matrix calculations 1017 /// when possible when dealing with non-perspective axis-aligned transformations. 1018 #[derive(Debug, MallocSizeOf)] 1019 #[cfg_attr(feature = "capture", derive(Serialize))] 1020 #[cfg_attr(feature = "replay", derive(Deserialize))] 1021 pub enum FastTransform<Src, Dst> { 1022 /// A simple offset, which can be used without doing any matrix math. 1023 Offset(Vector2D<f32, Src>), 1024 1025 /// A 2D transformation with an inverse. 1026 Transform { 1027 transform: Transform3D<f32, Src, Dst>, 1028 inverse: Option<Transform3D<f32, Dst, Src>>, 1029 is_2d: bool, 1030 }, 1031 } 1032 1033 impl<Src, Dst> Clone for FastTransform<Src, Dst> { 1034 fn clone(&self) -> Self { 1035 *self 1036 } 1037 } 1038 1039 impl<Src, Dst> Copy for FastTransform<Src, Dst> { } 1040 1041 impl<Src, Dst> FastTransform<Src, Dst> { 1042 pub fn identity() -> Self { 1043 FastTransform::Offset(Vector2D::zero()) 1044 } 1045 1046 pub fn with_vector(offset: Vector2D<f32, Src>) -> Self { 1047 FastTransform::Offset(offset) 1048 } 1049 1050 pub fn with_scale_offset(scale_offset: ScaleOffset) -> Self { 1051 if scale_offset.scale == Vector2D::new(1.0, 1.0) { 1052 FastTransform::Offset(Vector2D::from_untyped(scale_offset.offset)) 1053 } else { 1054 FastTransform::Transform { 1055 transform: scale_offset.to_transform(), 1056 inverse: Some(scale_offset.inverse().to_transform()), 1057 is_2d: true, 1058 } 1059 } 1060 } 1061 1062 #[inline(always)] 1063 pub fn with_transform(transform: Transform3D<f32, Src, Dst>) -> Self { 1064 if transform.is_simple_2d_translation() { 1065 return FastTransform::Offset(Vector2D::new(transform.m41, transform.m42)); 1066 } 1067 let inverse = transform.inverse(); 1068 let is_2d = transform.is_2d(); 1069 FastTransform::Transform { transform, inverse, is_2d} 1070 } 1071 1072 pub fn to_transform(&self) -> Cow<Transform3D<f32, Src, Dst>> { 1073 match *self { 1074 FastTransform::Offset(offset) => Cow::Owned( 1075 Transform3D::translation(offset.x, offset.y, 0.0) 1076 ), 1077 FastTransform::Transform { ref transform, .. } => Cow::Borrowed(transform), 1078 } 1079 } 1080 1081 /// Return true if this is an identity transform 1082 #[allow(unused)] 1083 pub fn is_identity(&self)-> bool { 1084 match *self { 1085 FastTransform::Offset(offset) => { 1086 offset == Vector2D::zero() 1087 } 1088 FastTransform::Transform { ref transform, .. } => { 1089 *transform == Transform3D::identity() 1090 } 1091 } 1092 } 1093 1094 pub fn then<NewDst>(&self, other: &FastTransform<Dst, NewDst>) -> FastTransform<Src, NewDst> { 1095 match *self { 1096 FastTransform::Offset(offset) => match *other { 1097 FastTransform::Offset(other_offset) => { 1098 FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0)) 1099 } 1100 FastTransform::Transform { transform: ref other_transform, .. } => { 1101 FastTransform::with_transform( 1102 other_transform 1103 .with_source::<Src>() 1104 .pre_translate(offset.to_3d()) 1105 ) 1106 } 1107 } 1108 FastTransform::Transform { ref transform, ref inverse, is_2d } => match *other { 1109 FastTransform::Offset(other_offset) => { 1110 FastTransform::with_transform( 1111 transform 1112 .then_translate(other_offset.to_3d()) 1113 .with_destination::<NewDst>() 1114 ) 1115 } 1116 FastTransform::Transform { transform: ref other_transform, inverse: ref other_inverse, is_2d: other_is_2d } => { 1117 FastTransform::Transform { 1118 transform: transform.then(other_transform), 1119 inverse: inverse.as_ref().and_then(|self_inv| 1120 other_inverse.as_ref().map(|other_inv| other_inv.then(self_inv)) 1121 ), 1122 is_2d: is_2d & other_is_2d, 1123 } 1124 } 1125 } 1126 } 1127 } 1128 1129 pub fn pre_transform<NewSrc>( 1130 &self, 1131 other: &FastTransform<NewSrc, Src> 1132 ) -> FastTransform<NewSrc, Dst> { 1133 other.then(self) 1134 } 1135 1136 pub fn pre_translate(&self, other_offset: Vector2D<f32, Src>) -> Self { 1137 match *self { 1138 FastTransform::Offset(offset) => 1139 FastTransform::Offset(offset + other_offset), 1140 FastTransform::Transform { transform, .. } => 1141 FastTransform::with_transform(transform.pre_translate(other_offset.to_3d())) 1142 } 1143 } 1144 1145 pub fn then_translate(&self, other_offset: Vector2D<f32, Dst>) -> Self { 1146 match *self { 1147 FastTransform::Offset(offset) => { 1148 FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0)) 1149 } 1150 FastTransform::Transform { ref transform, .. } => { 1151 let transform = transform.then_translate(other_offset.to_3d()); 1152 FastTransform::with_transform(transform) 1153 } 1154 } 1155 } 1156 1157 #[inline(always)] 1158 pub fn is_backface_visible(&self) -> bool { 1159 match *self { 1160 FastTransform::Offset(..) => false, 1161 FastTransform::Transform { inverse: None, .. } => false, 1162 //TODO: fix this properly by taking "det|M33| * det|M34| > 0" 1163 // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23014 1164 FastTransform::Transform { inverse: Some(ref inverse), .. } => inverse.m33 < 0.0, 1165 } 1166 } 1167 1168 #[inline(always)] 1169 pub fn transform_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> { 1170 match *self { 1171 FastTransform::Offset(offset) => { 1172 let new_point = point + offset; 1173 Some(Point2D::from_untyped(new_point.to_untyped())) 1174 } 1175 FastTransform::Transform { ref transform, .. } => transform.transform_point2d(point), 1176 } 1177 } 1178 1179 #[inline(always)] 1180 pub fn project_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> { 1181 match* self { 1182 FastTransform::Offset(..) => self.transform_point2d(point), 1183 FastTransform::Transform{ref transform, ..} => { 1184 // Find a value for z that will transform to 0. 1185 1186 // The transformed value of z is computed as: 1187 // z' = point.x * self.m13 + point.y * self.m23 + z * self.m33 + self.m43 1188 1189 // Solving for z when z' = 0 gives us: 1190 let z = -(point.x * transform.m13 + point.y * transform.m23 + transform.m43) / transform.m33; 1191 1192 transform.transform_point3d(point3(point.x, point.y, z)).map(| p3 | point2(p3.x, p3.y)) 1193 } 1194 } 1195 } 1196 1197 #[inline(always)] 1198 pub fn inverse(&self) -> Option<FastTransform<Dst, Src>> { 1199 match *self { 1200 FastTransform::Offset(offset) => 1201 Some(FastTransform::Offset(Vector2D::new(-offset.x, -offset.y))), 1202 FastTransform::Transform { transform, inverse: Some(inverse), is_2d, } => 1203 Some(FastTransform::Transform { 1204 transform: inverse, 1205 inverse: Some(transform), 1206 is_2d 1207 }), 1208 FastTransform::Transform { inverse: None, .. } => None, 1209 1210 } 1211 } 1212 } 1213 1214 impl<Src, Dst> From<Transform3D<f32, Src, Dst>> for FastTransform<Src, Dst> { 1215 fn from(transform: Transform3D<f32, Src, Dst>) -> Self { 1216 FastTransform::with_transform(transform) 1217 } 1218 } 1219 1220 impl<Src, Dst> From<Vector2D<f32, Src>> for FastTransform<Src, Dst> { 1221 fn from(vector: Vector2D<f32, Src>) -> Self { 1222 FastTransform::with_vector(vector) 1223 } 1224 } 1225 1226 pub type LayoutFastTransform = FastTransform<LayoutPixel, LayoutPixel>; 1227 pub type LayoutToWorldFastTransform = FastTransform<LayoutPixel, WorldPixel>; 1228 1229 pub fn project_rect<F, T>( 1230 transform: &Transform3D<f32, F, T>, 1231 rect: &Box2D<f32, F>, 1232 bounds: &Box2D<f32, T>, 1233 ) -> Option<Box2D<f32, T>> 1234 where F: fmt::Debug 1235 { 1236 let homogens = [ 1237 transform.transform_point2d_homogeneous(rect.top_left()), 1238 transform.transform_point2d_homogeneous(rect.top_right()), 1239 transform.transform_point2d_homogeneous(rect.bottom_left()), 1240 transform.transform_point2d_homogeneous(rect.bottom_right()), 1241 ]; 1242 1243 // Note: we only do the full frustum collision when the polygon approaches the camera plane. 1244 // Otherwise, it will be clamped to the screen bounds anyway. 1245 if homogens.iter().any(|h| h.w <= 0.0 || h.w.is_nan()) { 1246 let mut clipper = Clipper::new(); 1247 let polygon = Polygon::from_rect(rect.to_rect().cast().cast_unit(), 1); 1248 1249 let planes = match Clipper::<usize>::frustum_planes( 1250 &transform.cast_unit().cast(), 1251 Some(bounds.to_rect().cast_unit().to_f64()), 1252 ) { 1253 Ok(planes) => planes, 1254 Err(..) => return None, 1255 }; 1256 1257 for plane in planes { 1258 clipper.add(plane); 1259 } 1260 1261 let results = clipper.clip(polygon); 1262 if results.is_empty() { 1263 return None 1264 } 1265 1266 Some(Box2D::from_points(results 1267 .into_iter() 1268 // filter out parts behind the view plane 1269 .flat_map(|poly| &poly.points) 1270 .map(|p| { 1271 let mut homo = transform.transform_point2d_homogeneous(p.to_2d().to_f32().cast_unit()); 1272 homo.w = homo.w.max(0.00000001); // avoid infinite values 1273 homo.to_point2d().unwrap() 1274 }) 1275 )) 1276 } else { 1277 // we just checked for all the points to be in positive hemisphere, so `unwrap` is valid 1278 Some(Box2D::from_points(&[ 1279 homogens[0].to_point2d().unwrap(), 1280 homogens[1].to_point2d().unwrap(), 1281 homogens[2].to_point2d().unwrap(), 1282 homogens[3].to_point2d().unwrap(), 1283 ])) 1284 } 1285 } 1286 1287 /// Run the first callback over all elements in the array. If the callback returns true, 1288 /// the element is removed from the array and moved to a second callback. 1289 /// 1290 /// This is a simple implementation waiting for Vec::drain_filter to be stable. 1291 /// When that happens, code like: 1292 /// 1293 /// let filter = |op| { 1294 /// match *op { 1295 /// Enum::Foo | Enum::Bar => true, 1296 /// Enum::Baz => false, 1297 /// } 1298 /// }; 1299 /// drain_filter( 1300 /// &mut ops, 1301 /// filter, 1302 /// |op| { 1303 /// match op { 1304 /// Enum::Foo => { foo(); } 1305 /// Enum::Bar => { bar(); } 1306 /// Enum::Baz => { unreachable!(); } 1307 /// } 1308 /// }, 1309 /// ); 1310 /// 1311 /// Can be rewritten as: 1312 /// 1313 /// let filter = |op| { 1314 /// match *op { 1315 /// Enum::Foo | Enum::Bar => true, 1316 /// Enum::Baz => false, 1317 /// } 1318 /// }; 1319 /// for op in ops.drain_filter(filter) { 1320 /// match op { 1321 /// Enum::Foo => { foo(); } 1322 /// Enum::Bar => { bar(); } 1323 /// Enum::Baz => { unreachable!(); } 1324 /// } 1325 /// } 1326 /// 1327 /// See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter 1328 pub fn drain_filter<T, Filter, Action>( 1329 vec: &mut Vec<T>, 1330 mut filter: Filter, 1331 mut action: Action, 1332 ) 1333 where 1334 Filter: FnMut(&mut T) -> bool, 1335 Action: FnMut(T) 1336 { 1337 let mut i = 0; 1338 while i != vec.len() { 1339 if filter(&mut vec[i]) { 1340 action(vec.remove(i)); 1341 } else { 1342 i += 1; 1343 } 1344 } 1345 } 1346 1347 1348 #[derive(Debug)] 1349 pub struct Recycler { 1350 pub num_allocations: usize, 1351 } 1352 1353 impl Recycler { 1354 /// Maximum extra capacity that a recycled vector is allowed to have. If the actual capacity 1355 /// is larger, we re-allocate the vector storage with lower capacity. 1356 const MAX_EXTRA_CAPACITY_PERCENT: usize = 200; 1357 /// Minimum extra capacity to keep when re-allocating the vector storage. 1358 const MIN_EXTRA_CAPACITY_PERCENT: usize = 20; 1359 /// Minimum sensible vector length to consider for re-allocation. 1360 const MIN_VECTOR_LENGTH: usize = 16; 1361 1362 pub fn new() -> Self { 1363 Recycler { 1364 num_allocations: 0, 1365 } 1366 } 1367 1368 /// Clear a vector for re-use, while retaining the backing memory buffer. May shrink the buffer 1369 /// if it's currently much larger than was actually used. 1370 pub fn recycle_vec<T>(&mut self, vec: &mut Vec<T>) { 1371 let extra_capacity = (vec.capacity() - vec.len()) * 100 / vec.len().max(Self::MIN_VECTOR_LENGTH); 1372 1373 if extra_capacity > Self::MAX_EXTRA_CAPACITY_PERCENT { 1374 // Reduce capacity of the buffer if it is a lot larger than it needs to be. This prevents 1375 // a frame with exceptionally large allocations to cause subsequent frames to retain 1376 // more memory than they need. 1377 //TODO: use `shrink_to` when it's stable 1378 *vec = Vec::with_capacity(vec.len() + vec.len() * Self::MIN_EXTRA_CAPACITY_PERCENT / 100); 1379 self.num_allocations += 1; 1380 } else { 1381 vec.clear(); 1382 } 1383 } 1384 } 1385 1386 /// Record the size of a data structure to preallocate a similar size 1387 /// at the next frame and avoid growing it too many time. 1388 #[derive(Copy, Clone, Debug)] 1389 pub struct Preallocator { 1390 size: usize, 1391 } 1392 1393 impl Preallocator { 1394 pub fn new(initial_size: usize) -> Self { 1395 Preallocator { 1396 size: initial_size, 1397 } 1398 } 1399 1400 /// Record the size of a vector to preallocate it the next frame. 1401 pub fn record_vec<T>(&mut self, vec: &[T]) { 1402 let len = vec.len(); 1403 if len > self.size { 1404 self.size = len; 1405 } else { 1406 self.size = (self.size + len) / 2; 1407 } 1408 } 1409 1410 /// The size that we'll preallocate the vector with. 1411 pub fn preallocation_size(&self) -> usize { 1412 // Round up to multiple of 16 to avoid small tiny 1413 // variations causing reallocations. 1414 (self.size + 15) & !15 1415 } 1416 1417 /// Preallocate vector storage. 1418 /// 1419 /// The preallocated amount depends on the length recorded in the last 1420 /// record_vec call. 1421 pub fn preallocate_vec<T>(&self, vec: &mut Vec<T>) { 1422 let len = vec.len(); 1423 let cap = self.preallocation_size(); 1424 if len < cap { 1425 vec.reserve(cap - len); 1426 } 1427 } 1428 1429 /// Preallocate vector storage. 1430 /// 1431 /// The preallocated amount depends on the length recorded in the last 1432 /// record_vec call. 1433 pub fn preallocate_framevec<T>(&self, vec: &mut FrameVec<T>) { 1434 let len = vec.len(); 1435 let cap = self.preallocation_size(); 1436 if len < cap { 1437 vec.reserve(cap - len); 1438 } 1439 } 1440 } 1441 1442 impl Default for Preallocator { 1443 fn default() -> Self { 1444 Self::new(0) 1445 } 1446 } 1447 1448 /// Computes the scale factors of this matrix; that is, 1449 /// the amounts each basis vector is scaled by. 1450 /// 1451 /// This code comes from gecko gfx/2d/Matrix.h with the following 1452 /// modifications: 1453 /// 1454 /// * Removed `xMajor` parameter. 1455 /// * All arithmetics is done with double precision. 1456 pub fn scale_factors<Src, Dst>( 1457 mat: &Transform3D<f32, Src, Dst> 1458 ) -> (f32, f32) { 1459 let m11 = mat.m11 as f64; 1460 let m12 = mat.m12 as f64; 1461 // Determinant is just of the 2D component. 1462 let det = m11 * mat.m22 as f64 - m12 * mat.m21 as f64; 1463 if det == 0.0 { 1464 return (0.0, 0.0); 1465 } 1466 1467 // ignore mirroring 1468 let det = det.abs(); 1469 1470 let major = (m11 * m11 + m12 * m12).sqrt(); 1471 let minor = if major != 0.0 { det / major } else { 0.0 }; 1472 1473 (major as f32, minor as f32) 1474 } 1475 1476 #[test] 1477 fn scale_factors_large() { 1478 // https://bugzilla.mozilla.org/show_bug.cgi?id=1748499 1479 let mat = Transform3D::<f32, (), ()>::new( 1480 1.6534229920333123e27, 3.673100922561787e27, 0.0, 0.0, 1481 -3.673100922561787e27, 1.6534229920333123e27, 0.0, 0.0, 1482 0.0, 0.0, 1.0, 0.0, 1483 -828140552192.0, -1771307401216.0, 0.0, 1.0, 1484 ); 1485 let (major, minor) = scale_factors(&mat); 1486 assert!(major.is_normal() && minor.is_normal()); 1487 } 1488 1489 /// Clamp scaling factor to a power of two. 1490 /// 1491 /// This code comes from gecko gfx/thebes/gfxUtils.cpp with the following 1492 /// modification: 1493 /// 1494 /// * logs are taken in base 2 instead of base e. 1495 pub fn clamp_to_scale_factor(val: f32, round_down: bool) -> f32 { 1496 // Arbitary scale factor limitation. We can increase this 1497 // for better scaling performance at the cost of worse 1498 // quality. 1499 const SCALE_RESOLUTION: f32 = 2.0; 1500 1501 // Negative scaling is just a flip and irrelevant to 1502 // our resolution calculation. 1503 let val = val.abs(); 1504 1505 let (val, inverse) = if val < 1.0 { 1506 (1.0 / val, true) 1507 } else { 1508 (val, false) 1509 }; 1510 1511 let power = val.log2() / SCALE_RESOLUTION.log2(); 1512 1513 // If power is within 1e-5 of an integer, round to nearest to 1514 // prevent floating point errors, otherwise round up to the 1515 // next integer value. 1516 let power = if (power - power.round()).abs() < 1e-5 { 1517 power.round() 1518 } else if inverse != round_down { 1519 // Use floor when we are either inverted or rounding down, but 1520 // not both. 1521 power.floor() 1522 } else { 1523 // Otherwise, ceil when we are not inverted and not rounding 1524 // down, or we are inverted and rounding down. 1525 power.ceil() 1526 }; 1527 1528 let scale = SCALE_RESOLUTION.powf(power); 1529 1530 if inverse { 1531 1.0 / scale 1532 } else { 1533 scale 1534 } 1535 } 1536 1537 /// Rounds a value up to the nearest multiple of mul 1538 pub fn round_up_to_multiple(val: usize, mul: NonZeroUsize) -> usize { 1539 match val % mul.get() { 1540 0 => val, 1541 rem => val - rem + mul.get(), 1542 } 1543 } 1544 1545 1546 #[macro_export] 1547 macro_rules! c_str { 1548 ($lit:expr) => { 1549 unsafe { 1550 std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() 1551 as *const std::os::raw::c_char) 1552 } 1553 } 1554 } 1555 1556 /// This is inspired by the `weak-table` crate. 1557 /// It holds a Vec of weak pointers that are garbage collected as the Vec 1558 pub struct WeakTable { 1559 inner: Vec<std::sync::Weak<Vec<u8>>> 1560 } 1561 1562 impl WeakTable { 1563 pub fn new() -> WeakTable { 1564 WeakTable { inner: Vec::new() } 1565 } 1566 pub fn insert(&mut self, x: std::sync::Weak<Vec<u8>>) { 1567 if self.inner.len() == self.inner.capacity() { 1568 self.remove_expired(); 1569 1570 // We want to make sure that we change capacity() 1571 // even if remove_expired() removes some entries 1572 // so that we don't repeatedly hit remove_expired() 1573 if self.inner.len() * 3 < self.inner.capacity() { 1574 // We use a different multiple for shrinking then 1575 // expanding so that we we don't accidentally 1576 // oscilate. 1577 self.inner.shrink_to_fit(); 1578 } else { 1579 // Otherwise double our size 1580 self.inner.reserve(self.inner.len()) 1581 } 1582 } 1583 self.inner.push(x); 1584 } 1585 1586 fn remove_expired(&mut self) { 1587 self.inner.retain(|x| x.strong_count() > 0) 1588 } 1589 1590 pub fn iter(&self) -> impl Iterator<Item = Arc<Vec<u8>>> + '_ { 1591 self.inner.iter().filter_map(|x| x.upgrade()) 1592 } 1593 } 1594 1595 #[test] 1596 fn weak_table() { 1597 let mut tbl = WeakTable::new(); 1598 let mut things = Vec::new(); 1599 let target_count = 50; 1600 for _ in 0..target_count { 1601 things.push(Arc::new(vec![4])); 1602 } 1603 for i in &things { 1604 tbl.insert(Arc::downgrade(i)) 1605 } 1606 assert_eq!(tbl.inner.len(), target_count); 1607 drop(things); 1608 assert_eq!(tbl.iter().count(), 0); 1609 1610 // make sure that we shrink the table if it gets too big 1611 // by adding a bunch of dead items 1612 for _ in 0..target_count*2 { 1613 tbl.insert(Arc::downgrade(&Arc::new(vec![5]))) 1614 } 1615 assert!(tbl.inner.capacity() <= 4); 1616 } 1617 1618 #[test] 1619 fn scale_offset_pre_post() { 1620 let a = ScaleOffset::new(1.0, 2.0, 3.0, 4.0); 1621 let b = ScaleOffset::new(5.0, 6.0, 7.0, 8.0); 1622 1623 assert_eq!(a.then(&b), b.pre_transform(&a)); 1624 assert_eq!(a.then_scale(10.0), a.then(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0)))); 1625 assert_eq!(a.pre_scale(10.0), a.pre_transform(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0)))); 1626 }