1use api::BorderRadius;
6use api::units::*;
7use euclid::{Point2D, Rect, Box2D, Size2D, Vector2D, point2, point3};
8use euclid::{default, Transform2D, Transform3D, Scale, approxeq::ApproxEq};
9use plane_split::{Clipper, Polygon};
10use std::{i32, f32, fmt, ptr};
11use std::borrow::Cow;
12use std::num::NonZeroUsize;
13use std::sync::Arc;
14use std::mem::replace;
15
16use crate::internal_types::FrameVec;
17
18const NEARLY_ZERO: f32 = 1.0 / 4096.0;
20
21pub struct Allocation<'a, T: 'a> {
24 vec: &'a mut Vec<T>,
25 index: usize,
26}
27
28impl<'a, T> Allocation<'a, T> {
29 #[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
42pub enum VecEntry<'a, T: 'a> {
44 Vacant(Allocation<'a, T>),
45 Occupied(&'a mut T),
46}
47
48impl<'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
58pub trait VecHelper<T> {
59 fn alloc(&mut self) -> Allocation<T>;
61 fn entry(&mut self, index: usize) -> VecEntry<T>;
64
65 fn take(&mut self) -> Self;
67
68 fn take_and_preallocate(&mut self) -> Self;
72}
73
74impl<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#[repr(C)]
119#[derive(Debug, Clone, Copy, MallocSizeOf, PartialEq)]
120#[cfg_attr(feature = "capture", derive(Serialize))]
121#[cfg_attr(feature = "replay", derive(Deserialize))]
122pub struct ScaleOffset {
123 pub scale: euclid::Vector2D<f32, euclid::UnknownUnit>,
124 pub offset: euclid::Vector2D<f32, euclid::UnknownUnit>,
125}
126
127impl 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 pub fn from_transform<F, T>(
145 m: &Transform3D<f32, F, T>,
146 ) -> Option<ScaleOffset> {
147
148 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 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 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 #[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 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 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 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 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
403pub trait MatrixHelpers<Src, Dst> {
405 fn preserves_2d_axis_alignment(&self) -> bool;
408 fn has_perspective_component(&self) -> bool;
409 fn has_2d_inverse(&self) -> bool;
410 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 fn determinant_2d(&self) -> f32;
421 fn flatten_z_output(&mut self);
424
425 fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst>;
426}
427
428impl<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 fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>> {
478 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 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 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 }
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
570pub trait PointHelpers<U>
571where
572 Self: Sized,
573{
574 fn snap(&self) -> Self;
575}
576
577impl<U> PointHelpers<U> for Point2D<f32, U> {
578 fn snap(&self) -> Self {
579 Point2D::new(
580 (self.x + 0.5).floor(),
581 (self.y + 0.5).floor(),
582 )
583 }
584}
585
586pub trait RectHelpers<U>
587where
588 Self: Sized,
589{
590 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
591 fn snap(&self) -> Self;
592}
593
594impl<U> RectHelpers<U> for Rect<f32, U> {
595 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
596 Rect::new(
597 Point2D::new(x0, y0),
598 Size2D::new(x1 - x0, y1 - y0),
599 )
600 }
601
602 fn snap(&self) -> Self {
603 let origin = Point2D::new(
604 (self.origin.x + 0.5).floor(),
605 (self.origin.y + 0.5).floor(),
606 );
607 Rect::new(
608 origin,
609 Size2D::new(
610 (self.origin.x + self.size.width + 0.5).floor() - origin.x,
611 (self.origin.y + self.size.height + 0.5).floor() - origin.y,
612 ),
613 )
614 }
615}
616
617impl<U> RectHelpers<U> for Box2D<f32, U> {
618 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
619 Box2D {
620 min: Point2D::new(x0, y0),
621 max: Point2D::new(x1, y1),
622 }
623 }
624
625 fn snap(&self) -> Self {
626 self.round()
627 }
628}
629
630pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
631 (b - a) * t + a
632}
633
634#[repr(u32)]
635#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
636#[cfg_attr(feature = "capture", derive(Serialize))]
637#[cfg_attr(feature = "replay", derive(Deserialize))]
638pub enum TransformedRectKind {
639 AxisAligned = 0,
640 Complex = 1,
641}
642
643#[inline(always)]
644pub fn pack_as_float(value: u32) -> f32 {
645 value as f32 + 0.5
646}
647
648#[inline]
649fn extract_inner_rect_impl<U>(
650 rect: &Box2D<f32, U>,
651 radii: &BorderRadius,
652 k: f32,
653) -> Option<Box2D<f32, U>> {
654 let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil();
659 let xr = (rect.width() - k * radii.top_right.width.max(radii.bottom_right.width)).floor();
660 let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil();
661 let yb =
662 (rect.height() - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor();
663
664 if xl <= xr && yt <= yb {
665 Some(Box2D::from_origin_and_size(
666 Point2D::new(rect.min.x + xl, rect.min.y + yt),
667 Size2D::new(xr - xl, yb - yt),
668 ))
669 } else {
670 None
671 }
672}
673
674pub fn extract_inner_rect_safe<U>(
677 rect: &Box2D<f32, U>,
678 radii: &BorderRadius,
679) -> Option<Box2D<f32, U>> {
680 extract_inner_rect_impl(rect, radii, 1.0)
683}
684
685pub fn extract_inner_rect_k<U>(
689 rect: &Box2D<f32, U>,
690 radii: &BorderRadius,
691 k: f32,
692) -> Option<Box2D<f32, U>> {
693 extract_inner_rect_impl(rect, radii, k)
694}
695
696#[cfg(test)]
697use euclid::vec3;
698
699#[cfg(test)]
700pub mod test {
701 use super::*;
702 use euclid::default::{Point2D, Size2D, Transform3D};
703 use euclid::{Angle, approxeq::ApproxEq};
704 use std::f32::consts::PI;
705 use crate::clip::{is_left_of_line, polygon_contains_point};
706 use crate::prim_store::PolygonKey;
707 use api::FillRule;
708
709 #[test]
710 fn inverse_project() {
711 let m0 = Transform3D::identity();
712 let p0 = Point2D::new(1.0, 2.0);
713 assert_eq!(m0.inverse_project(&p0), Some(p0));
715 let m1 = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-PI / 3.0));
716 assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
718 }
719
720 #[test]
721 fn inverse_project_footprint() {
722 let m = Transform3D::new(
723 0.477499992, 0.135000005, -1.0, 0.000624999986,
724 -0.642787635, 0.766044438, 0.0, 0.0,
725 0.766044438, 0.642787635, 0.0, 0.0,
726 1137.10986, 113.71286, 402.0, 0.748749971,
727 );
728 let r = Box2D::from_size(Size2D::new(804.0, 804.0));
729 {
730 let points = &[
731 r.top_left(),
732 r.top_right(),
733 r.bottom_left(),
734 r.bottom_right(),
735 ];
736 let mi = m.inverse().unwrap();
737 info!("Points:");
741 for p in points {
742 let pp = m.transform_point2d_homogeneous(*p);
743 let p3 = pp.to_point3d().unwrap();
744 let pi = mi.transform_point3d_homogeneous(p3);
745 let px = pi.to_point2d().unwrap();
746 let py = m.inverse_project(&pp.to_point2d().unwrap()).unwrap();
747 info!("\t{:?} -> {:?} -> {:?} -> ({:?} -> {:?}, {:?})", p, pp, p3, pi, px, py);
748 assert!(px.approx_eq_eps(p, &Point2D::new(0.001, 0.001)));
749 assert!(py.approx_eq_eps(p, &Point2D::new(0.001, 0.001)));
750 }
751 }
752 let rp = project_rect(&m, &r, &Box2D::from_size(Size2D::new(1000.0, 1000.0))).unwrap();
754 info!("Projected {:?}", rp);
755 assert_eq!(m.inverse_project(&rp.min), None);
757 if let Some(ri) = m.inverse_rect_footprint(&rp) {
759 assert!(ri.contains_box(&r), "Inverse {:?}", ri);
761 }
762 }
763
764 fn validate_convert(xref: &LayoutTransform) {
765 let so = ScaleOffset::from_transform(xref).unwrap();
766 let xf = so.to_transform();
767 assert!(xref.approx_eq(&xf));
768 }
769
770 #[test]
771 fn negative_scale_map_unmap() {
772 let xref = LayoutTransform::scale(1.0, -1.0, 1.0)
773 .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
774 let so = ScaleOffset::from_transform(&xref).unwrap();
775 let local_rect = Box2D {
776 min: LayoutPoint::new(50.0, -100.0),
777 max: LayoutPoint::new(250.0, 300.0),
778 };
779
780 let mapped_rect = so.map_rect::<LayoutPixel, DevicePixel>(&local_rect);
781 let xf_rect = project_rect(
782 &xref,
783 &local_rect,
784 &LayoutRect::max_rect(),
785 ).unwrap();
786
787 assert!(mapped_rect.min.x.approx_eq(&xf_rect.min.x));
788 assert!(mapped_rect.min.y.approx_eq(&xf_rect.min.y));
789 assert!(mapped_rect.max.x.approx_eq(&xf_rect.max.x));
790 assert!(mapped_rect.max.y.approx_eq(&xf_rect.max.y));
791
792 let unmapped_rect = so.unmap_rect::<DevicePixel, LayoutPixel>(&mapped_rect);
793 assert!(unmapped_rect.min.x.approx_eq(&local_rect.min.x));
794 assert!(unmapped_rect.min.y.approx_eq(&local_rect.min.y));
795 assert!(unmapped_rect.max.x.approx_eq(&local_rect.max.x));
796 assert!(unmapped_rect.max.y.approx_eq(&local_rect.max.y));
797 }
798
799 #[test]
800 fn scale_offset_convert() {
801 let xref = LayoutTransform::translation(130.0, 200.0, 0.0);
802 validate_convert(&xref);
803
804 let xref = LayoutTransform::scale(13.0, 8.0, 1.0);
805 validate_convert(&xref);
806
807 let xref = LayoutTransform::scale(0.5, 0.5, 1.0)
808 .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
809 validate_convert(&xref);
810
811 let xref = LayoutTransform::scale(30.0, 11.0, 1.0)
812 .then_translate(vec3(50.0, 240.0, 0.0));
813 validate_convert(&xref);
814 }
815
816 fn validate_inverse(xref: &LayoutTransform) {
817 let s0 = ScaleOffset::from_transform(xref).unwrap();
818 let s1 = s0.inverse().pre_transform(&s0);
819 assert!((s1.scale.x - 1.0).abs() < NEARLY_ZERO &&
820 (s1.scale.y - 1.0).abs() < NEARLY_ZERO &&
821 s1.offset.x.abs() < NEARLY_ZERO &&
822 s1.offset.y.abs() < NEARLY_ZERO,
823 "{:?}",
824 s1);
825 }
826
827 #[test]
828 fn scale_offset_inverse() {
829 let xref = LayoutTransform::translation(130.0, 200.0, 0.0);
830 validate_inverse(&xref);
831
832 let xref = LayoutTransform::scale(13.0, 8.0, 1.0);
833 validate_inverse(&xref);
834
835 let xref = LayoutTransform::translation(124.0, 38.0, 0.0).
836 then_scale(0.5, 0.5, 1.0);
837
838 validate_inverse(&xref);
839
840 let xref = LayoutTransform::scale(30.0, 11.0, 1.0)
841 .then_translate(vec3(50.0, 240.0, 0.0));
842 validate_inverse(&xref);
843 }
844
845 fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) {
846 let x = x1.then(&x0);
847
848 let s0 = ScaleOffset::from_transform(x0).unwrap();
849 let s1 = ScaleOffset::from_transform(x1).unwrap();
850
851 let s = s0.pre_transform(&s1).to_transform();
852
853 assert!(x.approx_eq(&s), "{:?}\n{:?}", x, s);
854 }
855
856 #[test]
857 fn scale_offset_accumulate() {
858 let x0 = LayoutTransform::translation(130.0, 200.0, 0.0);
859 let x1 = LayoutTransform::scale(7.0, 3.0, 1.0);
860
861 validate_accumulate(&x0, &x1);
862 }
863
864 #[test]
865 fn scale_offset_invalid_scale() {
866 let s0 = ScaleOffset::new(0.0, 1.0, 10.0, 20.0);
867 let i0 = s0.inverse();
868 assert_eq!(i0, ScaleOffset::new(0.0, 0.0, 0.0, 0.0));
869
870 let s1 = ScaleOffset::new(1.0, 0.0, 10.0, 20.0);
871 let i1 = s1.inverse();
872 assert_eq!(i1, ScaleOffset::new(0.0, 0.0, 0.0, 0.0));
873 }
874
875 #[test]
876 fn polygon_clip_is_left_of_point() {
877 let p0_x = 1.0;
883 let p0_y = -3.0;
884 let p1_x = -2.0;
885 let p1_y = 6.0;
886
887 assert!(is_left_of_line(-9.0, 0.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
889 assert!(is_left_of_line(-1.0, 1.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
890 assert!(is_left_of_line(1.0, -4.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
891
892 assert!(is_left_of_line(-3.0, 9.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
894 assert!(is_left_of_line(0.0, 0.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
895 assert!(is_left_of_line(100.0, -300.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
896
897 assert!(is_left_of_line(0.0, 1.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
899 assert!(is_left_of_line(-4.0, 13.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
900 assert!(is_left_of_line(5.0, -12.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
901 }
902
903 #[test]
904 fn polygon_clip_contains_point() {
905 let p0 = LayoutPoint::new(4.0, 4.0);
908 let p1 = LayoutPoint::new(6.0, 4.0);
909 let p2 = LayoutPoint::new(4.0, 7.0);
910 let p3 = LayoutPoint::new(2.0, 1.0);
911 let p4 = LayoutPoint::new(8.0, 1.0);
912 let p5 = LayoutPoint::new(6.0, 7.0);
913
914 let poly_clockwise_nonzero = PolygonKey::new(
915 &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Nonzero
916 );
917 let poly_clockwise_evenodd = PolygonKey::new(
918 &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Evenodd
919 );
920 let poly_counter_clockwise_nonzero = PolygonKey::new(
921 &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Nonzero
922 );
923 let poly_counter_clockwise_evenodd = PolygonKey::new(
924 &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Evenodd
925 );
926
927 let rect = LayoutRect::from_size(LayoutSize::new(10.0, 10.0));
930
931 let p_inside_once = LayoutPoint::new(5.0, 3.0);
933 let p_inside_twice = LayoutPoint::new(5.0, 5.0);
934 let p_outside = LayoutPoint::new(9.0, 9.0);
935
936 for poly_nonzero in vec![poly_clockwise_nonzero, poly_counter_clockwise_nonzero].iter() {
940 assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_nonzero), true);
941 assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_nonzero), true);
942 assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_nonzero), false);
943 }
944 for poly_evenodd in vec![poly_clockwise_evenodd, poly_counter_clockwise_evenodd].iter() {
946 assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_evenodd), true);
947 assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_evenodd), false);
948 assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_evenodd), false);
949 }
950 }
951}
952
953pub trait MaxRect {
954 fn max_rect() -> Self;
955}
956
957impl MaxRect for DeviceIntRect {
958 fn max_rect() -> Self {
959 DeviceIntRect::from_origin_and_size(
960 DeviceIntPoint::new(i32::MIN / 2, i32::MIN / 2),
961 DeviceIntSize::new(i32::MAX, i32::MAX),
962 )
963 }
964}
965
966impl<U> MaxRect for Rect<f32, U> {
967 fn max_rect() -> Self {
968 const MAX_COORD: f32 = 1.0e9;
975
976 Rect::new(
977 Point2D::new(-MAX_COORD, -MAX_COORD),
978 Size2D::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
979 )
980 }
981}
982
983impl<U> MaxRect for Box2D<f32, U> {
984 fn max_rect() -> Self {
985 const MAX_COORD: f32 = 1.0e9;
992
993 Box2D::new(
994 Point2D::new(-MAX_COORD, -MAX_COORD),
995 Point2D::new(MAX_COORD, MAX_COORD),
996 )
997 }
998}
999
1000#[derive(Debug, MallocSizeOf)]
1003#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1004pub enum FastTransform<Src, Dst> {
1005 Offset(Vector2D<f32, Src>),
1007
1008 Transform {
1010 transform: Transform3D<f32, Src, Dst>,
1011 inverse: Option<Transform3D<f32, Dst, Src>>,
1012 is_2d: bool,
1013 },
1014}
1015
1016impl<Src, Dst> Clone for FastTransform<Src, Dst> {
1017 fn clone(&self) -> Self {
1018 *self
1019 }
1020}
1021
1022impl<Src, Dst> Copy for FastTransform<Src, Dst> { }
1023
1024impl<Src, Dst> Default for FastTransform<Src, Dst> {
1025 fn default() -> Self {
1026 Self::identity()
1027 }
1028}
1029
1030impl<Src, Dst> FastTransform<Src, Dst> {
1031 pub fn identity() -> Self {
1032 FastTransform::Offset(Vector2D::zero())
1033 }
1034
1035 pub fn with_vector(offset: Vector2D<f32, Src>) -> Self {
1036 FastTransform::Offset(offset)
1037 }
1038
1039 pub fn with_scale_offset(scale_offset: ScaleOffset) -> Self {
1040 if scale_offset.scale == Vector2D::new(1.0, 1.0) {
1041 FastTransform::Offset(Vector2D::from_untyped(scale_offset.offset))
1042 } else {
1043 FastTransform::Transform {
1044 transform: scale_offset.to_transform(),
1045 inverse: Some(scale_offset.inverse().to_transform()),
1046 is_2d: true,
1047 }
1048 }
1049 }
1050
1051 #[inline(always)]
1052 pub fn with_transform(transform: Transform3D<f32, Src, Dst>) -> Self {
1053 if transform.is_simple_2d_translation() {
1054 return FastTransform::Offset(Vector2D::new(transform.m41, transform.m42));
1055 }
1056 let inverse = transform.inverse();
1057 let is_2d = transform.is_2d();
1058 FastTransform::Transform { transform, inverse, is_2d}
1059 }
1060
1061 pub fn to_transform(&self) -> Cow<Transform3D<f32, Src, Dst>> {
1062 match *self {
1063 FastTransform::Offset(offset) => Cow::Owned(
1064 Transform3D::translation(offset.x, offset.y, 0.0)
1065 ),
1066 FastTransform::Transform { ref transform, .. } => Cow::Borrowed(transform),
1067 }
1068 }
1069
1070 #[allow(unused)]
1072 pub fn is_identity(&self)-> bool {
1073 match *self {
1074 FastTransform::Offset(offset) => {
1075 offset == Vector2D::zero()
1076 }
1077 FastTransform::Transform { ref transform, .. } => {
1078 *transform == Transform3D::identity()
1079 }
1080 }
1081 }
1082
1083 pub fn then<NewDst>(&self, other: &FastTransform<Dst, NewDst>) -> FastTransform<Src, NewDst> {
1084 match *self {
1085 FastTransform::Offset(offset) => match *other {
1086 FastTransform::Offset(other_offset) => {
1087 FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0))
1088 }
1089 FastTransform::Transform { transform: ref other_transform, .. } => {
1090 FastTransform::with_transform(
1091 other_transform
1092 .with_source::<Src>()
1093 .pre_translate(offset.to_3d())
1094 )
1095 }
1096 }
1097 FastTransform::Transform { ref transform, ref inverse, is_2d } => match *other {
1098 FastTransform::Offset(other_offset) => {
1099 FastTransform::with_transform(
1100 transform
1101 .then_translate(other_offset.to_3d())
1102 .with_destination::<NewDst>()
1103 )
1104 }
1105 FastTransform::Transform { transform: ref other_transform, inverse: ref other_inverse, is_2d: other_is_2d } => {
1106 FastTransform::Transform {
1107 transform: transform.then(other_transform),
1108 inverse: inverse.as_ref().and_then(|self_inv|
1109 other_inverse.as_ref().map(|other_inv| other_inv.then(self_inv))
1110 ),
1111 is_2d: is_2d & other_is_2d,
1112 }
1113 }
1114 }
1115 }
1116 }
1117
1118 pub fn pre_transform<NewSrc>(
1119 &self,
1120 other: &FastTransform<NewSrc, Src>
1121 ) -> FastTransform<NewSrc, Dst> {
1122 other.then(self)
1123 }
1124
1125 pub fn pre_translate(&self, other_offset: Vector2D<f32, Src>) -> Self {
1126 match *self {
1127 FastTransform::Offset(offset) =>
1128 FastTransform::Offset(offset + other_offset),
1129 FastTransform::Transform { transform, .. } =>
1130 FastTransform::with_transform(transform.pre_translate(other_offset.to_3d()))
1131 }
1132 }
1133
1134 pub fn then_translate(&self, other_offset: Vector2D<f32, Dst>) -> Self {
1135 match *self {
1136 FastTransform::Offset(offset) => {
1137 FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0))
1138 }
1139 FastTransform::Transform { ref transform, .. } => {
1140 let transform = transform.then_translate(other_offset.to_3d());
1141 FastTransform::with_transform(transform)
1142 }
1143 }
1144 }
1145
1146 #[inline(always)]
1147 pub fn is_backface_visible(&self) -> bool {
1148 match *self {
1149 FastTransform::Offset(..) => false,
1150 FastTransform::Transform { inverse: None, .. } => false,
1151 FastTransform::Transform { inverse: Some(ref inverse), .. } => inverse.m33 < 0.0,
1154 }
1155 }
1156
1157 #[inline(always)]
1158 pub fn transform_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> {
1159 match *self {
1160 FastTransform::Offset(offset) => {
1161 let new_point = point + offset;
1162 Some(Point2D::from_untyped(new_point.to_untyped()))
1163 }
1164 FastTransform::Transform { ref transform, .. } => transform.transform_point2d(point),
1165 }
1166 }
1167
1168 #[inline(always)]
1169 pub fn project_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> {
1170 match* self {
1171 FastTransform::Offset(..) => self.transform_point2d(point),
1172 FastTransform::Transform{ref transform, ..} => {
1173 let z = -(point.x * transform.m13 + point.y * transform.m23 + transform.m43) / transform.m33;
1180
1181 transform.transform_point3d(point3(point.x, point.y, z)).map(| p3 | point2(p3.x, p3.y))
1182 }
1183 }
1184 }
1185
1186 #[inline(always)]
1187 pub fn inverse(&self) -> Option<FastTransform<Dst, Src>> {
1188 match *self {
1189 FastTransform::Offset(offset) =>
1190 Some(FastTransform::Offset(Vector2D::new(-offset.x, -offset.y))),
1191 FastTransform::Transform { transform, inverse: Some(inverse), is_2d, } =>
1192 Some(FastTransform::Transform {
1193 transform: inverse,
1194 inverse: Some(transform),
1195 is_2d
1196 }),
1197 FastTransform::Transform { inverse: None, .. } => None,
1198
1199 }
1200 }
1201}
1202
1203impl<Src, Dst> From<Transform3D<f32, Src, Dst>> for FastTransform<Src, Dst> {
1204 fn from(transform: Transform3D<f32, Src, Dst>) -> Self {
1205 FastTransform::with_transform(transform)
1206 }
1207}
1208
1209impl<Src, Dst> From<Vector2D<f32, Src>> for FastTransform<Src, Dst> {
1210 fn from(vector: Vector2D<f32, Src>) -> Self {
1211 FastTransform::with_vector(vector)
1212 }
1213}
1214
1215pub type LayoutFastTransform = FastTransform<LayoutPixel, LayoutPixel>;
1216pub type LayoutToWorldFastTransform = FastTransform<LayoutPixel, WorldPixel>;
1217
1218pub fn project_rect<F, T>(
1219 transform: &Transform3D<f32, F, T>,
1220 rect: &Box2D<f32, F>,
1221 bounds: &Box2D<f32, T>,
1222) -> Option<Box2D<f32, T>>
1223 where F: fmt::Debug
1224{
1225 let homogens = [
1226 transform.transform_point2d_homogeneous(rect.top_left()),
1227 transform.transform_point2d_homogeneous(rect.top_right()),
1228 transform.transform_point2d_homogeneous(rect.bottom_left()),
1229 transform.transform_point2d_homogeneous(rect.bottom_right()),
1230 ];
1231
1232 if homogens.iter().any(|h| h.w <= 0.0 || h.w.is_nan()) {
1235 let mut clipper = Clipper::new();
1236 let polygon = Polygon::from_rect(rect.to_rect().cast().cast_unit(), 1);
1237
1238 let planes = match Clipper::<usize>::frustum_planes(
1239 &transform.cast_unit().cast(),
1240 Some(bounds.to_rect().cast_unit().to_f64()),
1241 ) {
1242 Ok(planes) => planes,
1243 Err(..) => return None,
1244 };
1245
1246 for plane in planes {
1247 clipper.add(plane);
1248 }
1249
1250 let results = clipper.clip(polygon);
1251 if results.is_empty() {
1252 return None
1253 }
1254
1255 Some(Box2D::from_points(results
1256 .into_iter()
1257 .flat_map(|poly| &poly.points)
1259 .map(|p| {
1260 let mut homo = transform.transform_point2d_homogeneous(p.to_2d().to_f32().cast_unit());
1261 homo.w = homo.w.max(0.00000001); homo.to_point2d().unwrap()
1263 })
1264 ))
1265 } else {
1266 Some(Box2D::from_points(&[
1268 homogens[0].to_point2d().unwrap(),
1269 homogens[1].to_point2d().unwrap(),
1270 homogens[2].to_point2d().unwrap(),
1271 homogens[3].to_point2d().unwrap(),
1272 ]))
1273 }
1274}
1275
1276pub fn drain_filter<T, Filter, Action>(
1318 vec: &mut Vec<T>,
1319 mut filter: Filter,
1320 mut action: Action,
1321)
1322where
1323 Filter: FnMut(&mut T) -> bool,
1324 Action: FnMut(T)
1325{
1326 let mut i = 0;
1327 while i != vec.len() {
1328 if filter(&mut vec[i]) {
1329 action(vec.remove(i));
1330 } else {
1331 i += 1;
1332 }
1333 }
1334}
1335
1336
1337#[derive(Debug)]
1338pub struct Recycler {
1339 pub num_allocations: usize,
1340}
1341
1342impl Recycler {
1343 const MAX_EXTRA_CAPACITY_PERCENT: usize = 200;
1346 const MIN_EXTRA_CAPACITY_PERCENT: usize = 20;
1348 const MIN_VECTOR_LENGTH: usize = 16;
1350
1351 pub fn new() -> Self {
1352 Recycler {
1353 num_allocations: 0,
1354 }
1355 }
1356
1357 pub fn recycle_vec<T>(&mut self, vec: &mut Vec<T>) {
1360 let extra_capacity = (vec.capacity() - vec.len()) * 100 / vec.len().max(Self::MIN_VECTOR_LENGTH);
1361
1362 if extra_capacity > Self::MAX_EXTRA_CAPACITY_PERCENT {
1363 *vec = Vec::with_capacity(vec.len() + vec.len() * Self::MIN_EXTRA_CAPACITY_PERCENT / 100);
1368 self.num_allocations += 1;
1369 } else {
1370 vec.clear();
1371 }
1372 }
1373}
1374
1375#[derive(Copy, Clone, Debug)]
1378pub struct Preallocator {
1379 size: usize,
1380}
1381
1382impl Preallocator {
1383 pub fn new(initial_size: usize) -> Self {
1384 Preallocator {
1385 size: initial_size,
1386 }
1387 }
1388
1389 pub fn record_vec<T>(&mut self, vec: &[T]) {
1391 let len = vec.len();
1392 if len > self.size {
1393 self.size = len;
1394 } else {
1395 self.size = (self.size + len) / 2;
1396 }
1397 }
1398
1399 pub fn preallocation_size(&self) -> usize {
1401 (self.size + 15) & !15
1404 }
1405
1406 pub fn preallocate_vec<T>(&self, vec: &mut Vec<T>) {
1411 let len = vec.len();
1412 let cap = self.preallocation_size();
1413 if len < cap {
1414 vec.reserve(cap - len);
1415 }
1416 }
1417
1418 pub fn preallocate_framevec<T>(&self, vec: &mut FrameVec<T>) {
1423 let len = vec.len();
1424 let cap = self.preallocation_size();
1425 if len < cap {
1426 vec.reserve(cap - len);
1427 }
1428 }
1429}
1430
1431impl Default for Preallocator {
1432 fn default() -> Self {
1433 Self::new(0)
1434 }
1435}
1436
1437pub fn scale_factors<Src, Dst>(
1446 mat: &Transform3D<f32, Src, Dst>
1447) -> (f32, f32) {
1448 let m11 = mat.m11 as f64;
1449 let m12 = mat.m12 as f64;
1450 let det = m11 * mat.m22 as f64 - m12 * mat.m21 as f64;
1452 if det == 0.0 {
1453 return (0.0, 0.0);
1454 }
1455
1456 let det = det.abs();
1458
1459 let major = (m11 * m11 + m12 * m12).sqrt();
1460 let minor = if major != 0.0 { det / major } else { 0.0 };
1461
1462 (major as f32, minor as f32)
1463}
1464
1465#[test]
1466fn scale_factors_large() {
1467 let mat = Transform3D::<f32, (), ()>::new(
1469 1.6534229920333123e27, 3.673100922561787e27, 0.0, 0.0,
1470 -3.673100922561787e27, 1.6534229920333123e27, 0.0, 0.0,
1471 0.0, 0.0, 1.0, 0.0,
1472 -828140552192.0, -1771307401216.0, 0.0, 1.0,
1473 );
1474 let (major, minor) = scale_factors(&mat);
1475 assert!(major.is_normal() && minor.is_normal());
1476}
1477
1478pub fn clamp_to_scale_factor(val: f32, round_down: bool) -> f32 {
1485 const SCALE_RESOLUTION: f32 = 2.0;
1489
1490 let val = val.abs();
1493
1494 let (val, inverse) = if val < 1.0 {
1495 (1.0 / val, true)
1496 } else {
1497 (val, false)
1498 };
1499
1500 let power = val.log2() / SCALE_RESOLUTION.log2();
1501
1502 let power = if (power - power.round()).abs() < 1e-5 {
1506 power.round()
1507 } else if inverse != round_down {
1508 power.floor()
1511 } else {
1512 power.ceil()
1515 };
1516
1517 let scale = SCALE_RESOLUTION.powf(power);
1518
1519 if inverse {
1520 1.0 / scale
1521 } else {
1522 scale
1523 }
1524}
1525
1526pub fn round_up_to_multiple(val: usize, mul: NonZeroUsize) -> usize {
1528 match val % mul.get() {
1529 0 => val,
1530 rem => val - rem + mul.get(),
1531 }
1532}
1533
1534
1535#[macro_export]
1536macro_rules! c_str {
1537 ($lit:expr) => {
1538 unsafe {
1539 std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr()
1540 as *const std::os::raw::c_char)
1541 }
1542 }
1543}
1544
1545pub struct WeakTable {
1548 inner: Vec<std::sync::Weak<Vec<u8>>>
1549}
1550
1551impl WeakTable {
1552 pub fn new() -> WeakTable {
1553 WeakTable { inner: Vec::new() }
1554 }
1555 pub fn insert(&mut self, x: std::sync::Weak<Vec<u8>>) {
1556 if self.inner.len() == self.inner.capacity() {
1557 self.remove_expired();
1558
1559 if self.inner.len() * 3 < self.inner.capacity() {
1563 self.inner.shrink_to_fit();
1567 } else {
1568 self.inner.reserve(self.inner.len())
1570 }
1571 }
1572 self.inner.push(x);
1573 }
1574
1575 fn remove_expired(&mut self) {
1576 self.inner.retain(|x| x.strong_count() > 0)
1577 }
1578
1579 pub fn iter(&self) -> impl Iterator<Item = Arc<Vec<u8>>> + '_ {
1580 self.inner.iter().filter_map(|x| x.upgrade())
1581 }
1582}
1583
1584#[test]
1585fn weak_table() {
1586 let mut tbl = WeakTable::new();
1587 let mut things = Vec::new();
1588 let target_count = 50;
1589 for _ in 0..target_count {
1590 things.push(Arc::new(vec![4]));
1591 }
1592 for i in &things {
1593 tbl.insert(Arc::downgrade(i))
1594 }
1595 assert_eq!(tbl.inner.len(), target_count);
1596 drop(things);
1597 assert_eq!(tbl.iter().count(), 0);
1598
1599 for _ in 0..target_count*2 {
1602 tbl.insert(Arc::downgrade(&Arc::new(vec![5])))
1603 }
1604 assert!(tbl.inner.capacity() <= 4);
1605}
1606
1607#[test]
1608fn scale_offset_pre_post() {
1609 let a = ScaleOffset::new(1.0, 2.0, 3.0, 4.0);
1610 let b = ScaleOffset::new(5.0, 6.0, 7.0, 8.0);
1611
1612 assert_eq!(a.then(&b), b.pre_transform(&a));
1613 assert_eq!(a.then_scale(10.0), a.then(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0))));
1614 assert_eq!(a.pre_scale(10.0), a.pre_transform(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0))));
1615}