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 x0 = rect.min.x * self.scale.x + self.offset.x;
268 let y0 = rect.min.y * self.scale.y + self.offset.y;
269 let x1 = rect.min.x.max(rect.max.x) * self.scale.x + self.offset.x;
274 let y1 = rect.min.y.max(rect.max.y) * self.scale.y + self.offset.y;
275
276 Box2D::new(
277 Point2D::new(x0.min(x1), y0.min(y1)),
278 Point2D::new(x0.max(x1), y0.max(y1)),
279 )
280 }
281
282 pub fn unmap_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> {
283 let x0 = (rect.min.x - self.offset.x) / self.scale.x;
284 let y0 = (rect.min.y - self.offset.y) / self.scale.y;
285 let x1 = (rect.min.x.max(rect.max.x) - self.offset.x) / self.scale.x;
290 let y1 = (rect.min.y.max(rect.max.y) - self.offset.y) / self.scale.y;
291
292 Box2D::new(
293 Point2D::new(x0.min(x1), y0.min(y1)),
294 Point2D::new(x0.max(x1), y0.max(y1)),
295 )
296 }
297
298 pub fn map_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> {
299 Vector2D::new(
300 vector.x * self.scale.x,
301 vector.y * self.scale.y,
302 )
303 }
304
305 pub fn map_size<F, T>(&self, size: &Size2D<f32, F>) -> Size2D<f32, T> {
306 Size2D::new(
307 size.width * self.scale.x,
308 size.height * self.scale.y,
309 )
310 }
311
312 pub fn unmap_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> {
313 Vector2D::new(
314 vector.x / self.scale.x,
315 vector.y / self.scale.y,
316 )
317 }
318
319 pub fn map_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> {
320 Point2D::new(
321 point.x * self.scale.x + self.offset.x,
322 point.y * self.scale.y + self.offset.y,
323 )
324 }
325
326 pub fn unmap_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> {
327 Point2D::new(
328 (point.x - self.offset.x) / self.scale.x,
329 (point.y - self.offset.y) / self.scale.y,
330 )
331 }
332
333 pub fn to_transform<F, T>(&self) -> Transform3D<f32, F, T> {
334 Transform3D::new(
335 self.scale.x,
336 0.0,
337 0.0,
338 0.0,
339
340 0.0,
341 self.scale.y,
342 0.0,
343 0.0,
344
345 0.0,
346 0.0,
347 1.0,
348 0.0,
349
350 self.offset.x,
351 self.offset.y,
352 0.0,
353 1.0,
354 )
355 }
356
357 pub fn is_identity(&self) -> bool {
358 self.scale.x == 1.0 &&
359 self.scale.y == 1.0 &&
360 self.offset.x == 0.0 &&
361 self.offset.y == 0.0
362 }
363
364 pub fn is_reflection(&self) -> bool {
365 self.scale.x < 0.0 ||
366 self.scale.y < 0.0
367 }
368}
369
370pub trait MatrixHelpers<Src, Dst> {
372 fn preserves_2d_axis_alignment(&self) -> bool;
375 fn has_perspective_component(&self) -> bool;
376 fn has_2d_inverse(&self) -> bool;
377 fn exceeds_2d_scale(&self, limit: f64) -> bool;
380 fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>>;
381 fn inverse_rect_footprint(&self, rect: &Box2D<f32, Dst>) -> Option<Box2D<f32, Src>>;
382 fn is_simple_translation(&self) -> bool;
383 fn is_simple_2d_translation(&self) -> bool;
384 fn is_2d_scale_translation(&self) -> bool;
385 fn determinant_2d(&self) -> f32;
387 fn flatten_z_output(&mut self);
390
391 fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst>;
392}
393
394impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> {
395 fn preserves_2d_axis_alignment(&self) -> bool {
396 if self.m14 != 0.0 || self.m24 != 0.0 {
397 return false;
398 }
399
400 let mut col0 = 0;
401 let mut col1 = 0;
402 let mut row0 = 0;
403 let mut row1 = 0;
404
405 if self.m11.abs() > NEARLY_ZERO {
406 col0 += 1;
407 row0 += 1;
408 }
409 if self.m12.abs() > NEARLY_ZERO {
410 col1 += 1;
411 row0 += 1;
412 }
413 if self.m21.abs() > NEARLY_ZERO {
414 col0 += 1;
415 row1 += 1;
416 }
417 if self.m22.abs() > NEARLY_ZERO {
418 col1 += 1;
419 row1 += 1;
420 }
421
422 col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
423 }
424
425 fn has_perspective_component(&self) -> bool {
426 self.m14.abs() > NEARLY_ZERO ||
427 self.m24.abs() > NEARLY_ZERO ||
428 self.m34.abs() > NEARLY_ZERO ||
429 (self.m44 - 1.0).abs() > NEARLY_ZERO
430 }
431
432 fn has_2d_inverse(&self) -> bool {
433 self.determinant_2d() != 0.0
434 }
435
436 fn exceeds_2d_scale(&self, limit: f64) -> bool {
437 let limit2 = (limit * limit) as f32;
438 self.m11 * self.m11 + self.m12 * self.m12 > limit2 ||
439 self.m21 * self.m21 + self.m22 * self.m22 > limit2
440 }
441
442 fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>> {
444 let m = Transform2D::<f32, Src, Dst>::new(
446 self.m11 - target.x * self.m14, self.m12 - target.y * self.m14,
447 self.m21 - target.x * self.m24, self.m22 - target.y * self.m24,
448 self.m41 - target.x * self.m44, self.m42 - target.y * self.m44,
449 );
450 let inv = m.inverse()?;
451 if inv.m31 * self.m14 + inv.m32 * self.m24 + self.m44 > 0.0 {
453 Some(Point2D::new(inv.m31, inv.m32))
454 } else {
455 None
456 }
457 }
458
459 fn inverse_rect_footprint(&self, rect: &Box2D<f32, Dst>) -> Option<Box2D<f32, Src>> {
460 Some(Box2D::from_points(&[
461 self.inverse_project(&rect.top_left())?,
462 self.inverse_project(&rect.top_right())?,
463 self.inverse_project(&rect.bottom_left())?,
464 self.inverse_project(&rect.bottom_right())?,
465 ]))
466 }
467
468 fn is_simple_translation(&self) -> bool {
469 if (self.m11 - 1.0).abs() > NEARLY_ZERO ||
470 (self.m22 - 1.0).abs() > NEARLY_ZERO ||
471 (self.m33 - 1.0).abs() > NEARLY_ZERO ||
472 (self.m44 - 1.0).abs() > NEARLY_ZERO {
473 return false;
474 }
475
476 self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO &&
477 self.m14.abs() < NEARLY_ZERO && self.m21.abs() < NEARLY_ZERO &&
478 self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO &&
479 self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO &&
480 self.m34.abs() < NEARLY_ZERO
481 }
482
483 fn is_simple_2d_translation(&self) -> bool {
484 if !self.is_simple_translation() {
485 return false;
486 }
487
488 self.m43.abs() < NEARLY_ZERO
489 }
490
491 fn is_2d_scale_translation(&self) -> bool {
498 (self.m33 - 1.0).abs() < NEARLY_ZERO &&
499 (self.m44 - 1.0).abs() < NEARLY_ZERO &&
500 self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO && self.m14.abs() < NEARLY_ZERO &&
501 self.m21.abs() < NEARLY_ZERO && self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO &&
502 self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO && self.m34.abs() < NEARLY_ZERO &&
503 self.m43.abs() < NEARLY_ZERO
504 }
505
506 fn determinant_2d(&self) -> f32 {
507 self.m11 * self.m22 - self.m12 * self.m21
508 }
509
510 fn flatten_z_output(&mut self) {
511 self.m13 = 0.0;
512 self.m23 = 0.0;
513 self.m33 = 1.0;
514 self.m43 = 0.0;
515 }
517
518 fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst> {
519 Transform3D::new(
520 self.m11, self.m12, self.m13, self.m14,
521 self.m21, self.m22, self.m23, self.m24,
522 self.m31, self.m32, self.m33, self.m34,
523 self.m41, self.m42, self.m43, self.m44,
524 )
525 }
526}
527
528pub trait PointHelpers<U>
529where
530 Self: Sized,
531{
532 fn snap(&self) -> Self;
533}
534
535impl<U> PointHelpers<U> for Point2D<f32, U> {
536 fn snap(&self) -> Self {
537 Point2D::new(
538 self.x.round(),
539 self.y.round(),
540 )
541 }
542}
543
544pub trait VectorHelpers<U>
545where
546 Self: Sized,
547{
548 fn snap(&self) -> Self;
549}
550
551impl<U> VectorHelpers<U> for Vector2D<f32, U> {
552 fn snap(&self) -> Self {
553 Vector2D::new(
554 self.x.round(),
555 self.y.round(),
556 )
557 }
558}
559
560pub trait RectHelpers<U>
561where
562 Self: Sized,
563{
564 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
565 fn snap(&self) -> Self;
566}
567
568impl<U> RectHelpers<U> for Rect<f32, U> {
569 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
570 Rect::new(
571 Point2D::new(x0, y0),
572 Size2D::new(x1 - x0, y1 - y0),
573 )
574 }
575
576 fn snap(&self) -> Self {
577 let origin = Point2D::new(
578 (self.origin.x + 0.5).floor(),
579 (self.origin.y + 0.5).floor(),
580 );
581 Rect::new(
582 origin,
583 Size2D::new(
584 (self.origin.x + self.size.width + 0.5).floor() - origin.x,
585 (self.origin.y + self.size.height + 0.5).floor() - origin.y,
586 ),
587 )
588 }
589}
590
591impl<U> RectHelpers<U> for Box2D<f32, U> {
592 fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
593 Box2D {
594 min: Point2D::new(x0, y0),
595 max: Point2D::new(x1, y1),
596 }
597 }
598
599 fn snap(&self) -> Self {
600 self.round()
601 }
602}
603
604pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
605 (b - a) * t + a
606}
607
608#[repr(u32)]
609#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
610#[cfg_attr(feature = "capture", derive(Serialize))]
611#[cfg_attr(feature = "replay", derive(Deserialize))]
612pub enum TransformedRectKind {
613 AxisAligned = 0,
614 Complex = 1,
615}
616
617#[inline(always)]
618pub fn pack_as_float(value: u32) -> f32 {
619 value as f32 + 0.5
620}
621
622#[inline]
623fn extract_inner_rect_impl<U>(
624 rect: &Box2D<f32, U>,
625 radii: &BorderRadius,
626 k: f32,
627) -> Option<Box2D<f32, U>> {
628 let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil();
633 let xr = (rect.width() - k * radii.top_right.width.max(radii.bottom_right.width)).floor();
634 let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil();
635 let yb =
636 (rect.height() - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor();
637
638 if xl <= xr && yt <= yb {
639 Some(Box2D::from_origin_and_size(
640 Point2D::new(rect.min.x + xl, rect.min.y + yt),
641 Size2D::new(xr - xl, yb - yt),
642 ))
643 } else {
644 None
645 }
646}
647
648pub fn extract_inner_rect_safe<U>(
651 rect: &Box2D<f32, U>,
652 radii: &BorderRadius,
653) -> Option<Box2D<f32, U>> {
654 extract_inner_rect_impl(rect, radii, 1.0)
657}
658
659pub fn extract_inner_rect_k<U>(
663 rect: &Box2D<f32, U>,
664 radii: &BorderRadius,
665 k: f32,
666) -> Option<Box2D<f32, U>> {
667 extract_inner_rect_impl(rect, radii, k)
668}
669
670#[cfg(test)]
671use euclid::vec3;
672
673#[cfg(test)]
674pub mod test {
675 use super::*;
676 use euclid::default::{Box2D, Point2D, Size2D, Transform3D};
677 use euclid::{Angle, approxeq::ApproxEq};
678 use std::f32::consts::PI;
679 use crate::clip::{is_left_of_line, polygon_contains_point};
680 use crate::prim_store::PolygonKey;
681 use api::FillRule;
682
683 #[test]
684 fn inverse_project() {
685 let m0 = Transform3D::identity();
686 let p0 = Point2D::new(1.0, 2.0);
687 assert_eq!(m0.inverse_project(&p0), Some(p0));
689 let m1 = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-PI / 3.0));
690 assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
692 }
693
694 #[test]
695 fn inverse_project_footprint() {
696 let m = Transform3D::new(
697 0.477499992, 0.135000005, -1.0, 0.000624999986,
698 -0.642787635, 0.766044438, 0.0, 0.0,
699 0.766044438, 0.642787635, 0.0, 0.0,
700 1137.10986, 113.71286, 402.0, 0.748749971,
701 );
702 let r = Box2D::from_size(Size2D::new(804.0, 804.0));
703 {
704 let points = &[
705 r.top_left(),
706 r.top_right(),
707 r.bottom_left(),
708 r.bottom_right(),
709 ];
710 let mi = m.inverse().unwrap();
711 info!("Points:");
715 for p in points {
716 let pp = m.transform_point2d_homogeneous(*p);
717 let p3 = pp.to_point3d().unwrap();
718 let pi = mi.transform_point3d_homogeneous(p3);
719 let px = pi.to_point2d().unwrap();
720 let py = m.inverse_project(&pp.to_point2d().unwrap()).unwrap();
721 info!("\t{:?} -> {:?} -> {:?} -> ({:?} -> {:?}, {:?})", p, pp, p3, pi, px, py);
722 assert!(px.approx_eq_eps(p, &Point2D::new(0.001, 0.001)));
723 assert!(py.approx_eq_eps(p, &Point2D::new(0.001, 0.001)));
724 }
725 }
726 let rp = project_rect(&m, &r, &Box2D::from_size(Size2D::new(1000.0, 1000.0))).unwrap();
728 info!("Projected {:?}", rp);
729 assert_eq!(m.inverse_project(&rp.min), None);
731 if let Some(ri) = m.inverse_rect_footprint(&rp) {
733 assert!(ri.contains_box(&r), "Inverse {:?}", ri);
735 }
736 }
737
738 fn validate_convert(xref: &LayoutTransform) {
739 let so = ScaleOffset::from_transform(xref).unwrap();
740 let xf = so.to_transform();
741 assert!(xref.approx_eq(&xf));
742 }
743
744 #[test]
745 fn negative_scale_map_unmap() {
746 let xref = LayoutTransform::scale(1.0, -1.0, 1.0)
747 .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
748 let so = ScaleOffset::from_transform(&xref).unwrap();
749 let local_rect = LayoutRect {
750 min: LayoutPoint::new(50.0, -100.0),
751 max: LayoutPoint::new(250.0, 300.0),
752 };
753
754 let mapped_rect = so.map_rect::<LayoutPixel, DevicePixel>(&local_rect);
755 let xf_rect = project_rect(
756 &xref,
757 &local_rect,
758 &LayoutRect::max_rect(),
759 ).unwrap();
760
761 assert!(mapped_rect.min.x.approx_eq(&xf_rect.min.x));
762 assert!(mapped_rect.min.y.approx_eq(&xf_rect.min.y));
763 assert!(mapped_rect.max.x.approx_eq(&xf_rect.max.x));
764 assert!(mapped_rect.max.y.approx_eq(&xf_rect.max.y));
765
766 let unmapped_rect = so.unmap_rect::<DevicePixel, LayoutPixel>(&mapped_rect);
767 assert!(unmapped_rect.min.x.approx_eq(&local_rect.min.x));
768 assert!(unmapped_rect.min.y.approx_eq(&local_rect.min.y));
769 assert!(unmapped_rect.max.x.approx_eq(&local_rect.max.x));
770 assert!(unmapped_rect.max.y.approx_eq(&local_rect.max.y));
771 }
772
773 #[test]
774 fn scale_offset_convert() {
775 let xref = LayoutTransform::translation(130.0, 200.0, 0.0);
776 validate_convert(&xref);
777
778 let xref = LayoutTransform::scale(13.0, 8.0, 1.0);
779 validate_convert(&xref);
780
781 let xref = LayoutTransform::scale(0.5, 0.5, 1.0)
782 .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
783 validate_convert(&xref);
784
785 let xref = LayoutTransform::scale(30.0, 11.0, 1.0)
786 .then_translate(vec3(50.0, 240.0, 0.0));
787 validate_convert(&xref);
788 }
789
790 fn validate_inverse(xref: &LayoutTransform) {
791 let s0 = ScaleOffset::from_transform(xref).unwrap();
792 let s1 = s0.inverse().pre_transform(&s0);
793 assert!((s1.scale.x - 1.0).abs() < NEARLY_ZERO &&
794 (s1.scale.y - 1.0).abs() < NEARLY_ZERO &&
795 s1.offset.x.abs() < NEARLY_ZERO &&
796 s1.offset.y.abs() < NEARLY_ZERO,
797 "{:?}",
798 s1);
799 }
800
801 #[test]
802 fn scale_offset_inverse() {
803 let xref = LayoutTransform::translation(130.0, 200.0, 0.0);
804 validate_inverse(&xref);
805
806 let xref = LayoutTransform::scale(13.0, 8.0, 1.0);
807 validate_inverse(&xref);
808
809 let xref = LayoutTransform::translation(124.0, 38.0, 0.0).
810 then_scale(0.5, 0.5, 1.0);
811
812 validate_inverse(&xref);
813
814 let xref = LayoutTransform::scale(30.0, 11.0, 1.0)
815 .then_translate(vec3(50.0, 240.0, 0.0));
816 validate_inverse(&xref);
817 }
818
819 fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) {
820 let x = x1.then(&x0);
821
822 let s0 = ScaleOffset::from_transform(x0).unwrap();
823 let s1 = ScaleOffset::from_transform(x1).unwrap();
824
825 let s = s0.pre_transform(&s1).to_transform();
826
827 assert!(x.approx_eq(&s), "{:?}\n{:?}", x, s);
828 }
829
830 #[test]
831 fn scale_offset_accumulate() {
832 let x0 = LayoutTransform::translation(130.0, 200.0, 0.0);
833 let x1 = LayoutTransform::scale(7.0, 3.0, 1.0);
834
835 validate_accumulate(&x0, &x1);
836 }
837
838 #[test]
839 fn scale_offset_invalid_scale() {
840 let s0 = ScaleOffset::new(0.0, 1.0, 10.0, 20.0);
841 let i0 = s0.inverse();
842 assert_eq!(i0, ScaleOffset::new(0.0, 0.0, 0.0, 0.0));
843
844 let s1 = ScaleOffset::new(1.0, 0.0, 10.0, 20.0);
845 let i1 = s1.inverse();
846 assert_eq!(i1, ScaleOffset::new(0.0, 0.0, 0.0, 0.0));
847 }
848
849 #[test]
850 fn polygon_clip_is_left_of_point() {
851 let p0_x = 1.0;
857 let p0_y = -3.0;
858 let p1_x = -2.0;
859 let p1_y = 6.0;
860
861 assert!(is_left_of_line(-9.0, 0.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
863 assert!(is_left_of_line(-1.0, 1.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
864 assert!(is_left_of_line(1.0, -4.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
865
866 assert!(is_left_of_line(-3.0, 9.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
868 assert!(is_left_of_line(0.0, 0.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
869 assert!(is_left_of_line(100.0, -300.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
870
871 assert!(is_left_of_line(0.0, 1.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
873 assert!(is_left_of_line(-4.0, 13.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
874 assert!(is_left_of_line(5.0, -12.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
875 }
876
877 #[test]
878 fn polygon_clip_contains_point() {
879 let p0 = LayoutPoint::new(4.0, 4.0);
882 let p1 = LayoutPoint::new(6.0, 4.0);
883 let p2 = LayoutPoint::new(4.0, 7.0);
884 let p3 = LayoutPoint::new(2.0, 1.0);
885 let p4 = LayoutPoint::new(8.0, 1.0);
886 let p5 = LayoutPoint::new(6.0, 7.0);
887
888 let poly_clockwise_nonzero = PolygonKey::new(
889 &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Nonzero
890 );
891 let poly_clockwise_evenodd = PolygonKey::new(
892 &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Evenodd
893 );
894 let poly_counter_clockwise_nonzero = PolygonKey::new(
895 &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Nonzero
896 );
897 let poly_counter_clockwise_evenodd = PolygonKey::new(
898 &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Evenodd
899 );
900
901 let rect = LayoutRect::from_size(LayoutSize::new(10.0, 10.0));
904
905 let p_inside_once = LayoutPoint::new(5.0, 3.0);
907 let p_inside_twice = LayoutPoint::new(5.0, 5.0);
908 let p_outside = LayoutPoint::new(9.0, 9.0);
909
910 for poly_nonzero in vec![poly_clockwise_nonzero, poly_counter_clockwise_nonzero].iter() {
914 assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_nonzero), true);
915 assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_nonzero), true);
916 assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_nonzero), false);
917 }
918 for poly_evenodd in vec![poly_clockwise_evenodd, poly_counter_clockwise_evenodd].iter() {
920 assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_evenodd), true);
921 assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_evenodd), false);
922 assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_evenodd), false);
923 }
924 }
925
926 #[test]
929 fn map_unmap_negative_size() {
930 let scale_offset = ScaleOffset::new(2.0, 2.0, 1.0, 1.0);
931 let rect = Box2D::new(Point2D::new(5.0, 5.0), Point2D::new(0.0, 0.0));
932 let mapped_rect: Box2D<f32> = scale_offset.map_rect(&rect);
933 assert_eq!(mapped_rect, Box2D::new(Point2D::new(11.0, 11.0), Point2D::new(11.0, 11.0)));
934
935 let unmapped_rect: Box2D<f32> = scale_offset.unmap_rect(&rect);
936 assert_eq!(unmapped_rect, Box2D::new(Point2D::new(2.0, 2.0), Point2D::new(2.0, 2.0)));
937 }
938
939 #[test]
942 fn map_unmap_adjoining_rects() {
943 let so = ScaleOffset::new(0.3, 0.3, 0.0, 0.0);
944 let p1 = Point2D::new(15.0, 15.0);
945 let p2 = Point2D::new(45.0, 45.0);
946 let p3 = Point2D::new(75.0, 75.0);
947
948 let rect_1 = Box2D::new(p1, p2);
949 let rect_2 = Box2D::new(p2, p3);
950
951 let mapped_rect_1: Box2D<f32> = so.map_rect(&rect_1);
952 let mapped_rect_2: Box2D<f32> = so.map_rect(&rect_2);
953 assert_eq!(mapped_rect_1.max, mapped_rect_2.min);
954
955 let unmapped_rect_1: Box2D<f32> = so.unmap_rect(&rect_1);
956 let unmapped_rect_2: Box2D<f32> = so.unmap_rect(&rect_2);
957 assert_eq!(unmapped_rect_1.max, unmapped_rect_2.min);
958 }
959}
960
961pub trait MaxRect {
962 fn max_rect() -> Self;
963}
964
965impl MaxRect for DeviceIntRect {
966 fn max_rect() -> Self {
967 DeviceIntRect::from_origin_and_size(
968 DeviceIntPoint::new(i32::MIN / 2, i32::MIN / 2),
969 DeviceIntSize::new(i32::MAX, i32::MAX),
970 )
971 }
972}
973
974impl<U> MaxRect for Rect<f32, U> {
975 fn max_rect() -> Self {
976 const MAX_COORD: f32 = 1.0e9;
983
984 Rect::new(
985 Point2D::new(-MAX_COORD, -MAX_COORD),
986 Size2D::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
987 )
988 }
989}
990
991impl<U> MaxRect for Box2D<f32, U> {
992 fn max_rect() -> Self {
993 const MAX_COORD: f32 = 1.0e9;
1000
1001 Box2D::new(
1002 Point2D::new(-MAX_COORD, -MAX_COORD),
1003 Point2D::new(MAX_COORD, MAX_COORD),
1004 )
1005 }
1006}
1007
1008#[derive(Debug, MallocSizeOf)]
1011#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1012pub enum FastTransform<Src, Dst> {
1013 Offset(Vector2D<f32, Src>),
1015
1016 Transform {
1018 transform: Transform3D<f32, Src, Dst>,
1019 inverse: Option<Transform3D<f32, Dst, Src>>,
1020 is_2d: bool,
1021 },
1022}
1023
1024impl<Src, Dst> Clone for FastTransform<Src, Dst> {
1025 fn clone(&self) -> Self {
1026 *self
1027 }
1028}
1029
1030impl<Src, Dst> Copy for FastTransform<Src, Dst> { }
1031
1032impl<Src, Dst> Default for FastTransform<Src, Dst> {
1033 fn default() -> Self {
1034 Self::identity()
1035 }
1036}
1037
1038impl<Src, Dst> FastTransform<Src, Dst> {
1039 pub fn identity() -> Self {
1040 FastTransform::Offset(Vector2D::zero())
1041 }
1042
1043 pub fn with_vector(offset: Vector2D<f32, Src>) -> Self {
1044 FastTransform::Offset(offset)
1045 }
1046
1047 pub fn with_scale_offset(scale_offset: ScaleOffset) -> Self {
1048 if scale_offset.scale == Vector2D::new(1.0, 1.0) {
1049 FastTransform::Offset(Vector2D::from_untyped(scale_offset.offset))
1050 } else {
1051 FastTransform::Transform {
1052 transform: scale_offset.to_transform(),
1053 inverse: Some(scale_offset.inverse().to_transform()),
1054 is_2d: true,
1055 }
1056 }
1057 }
1058
1059 #[inline(always)]
1060 pub fn with_transform(transform: Transform3D<f32, Src, Dst>) -> Self {
1061 if transform.is_simple_2d_translation() {
1062 return FastTransform::Offset(Vector2D::new(transform.m41, transform.m42));
1063 }
1064 let inverse = transform.inverse();
1065 let is_2d = transform.is_2d();
1066 FastTransform::Transform { transform, inverse, is_2d}
1067 }
1068
1069 pub fn to_transform(&self) -> Cow<Transform3D<f32, Src, Dst>> {
1070 match *self {
1071 FastTransform::Offset(offset) => Cow::Owned(
1072 Transform3D::translation(offset.x, offset.y, 0.0)
1073 ),
1074 FastTransform::Transform { ref transform, .. } => Cow::Borrowed(transform),
1075 }
1076 }
1077
1078 #[allow(unused)]
1080 pub fn is_identity(&self)-> bool {
1081 match *self {
1082 FastTransform::Offset(offset) => {
1083 offset == Vector2D::zero()
1084 }
1085 FastTransform::Transform { ref transform, .. } => {
1086 *transform == Transform3D::identity()
1087 }
1088 }
1089 }
1090
1091 pub fn then<NewDst>(&self, other: &FastTransform<Dst, NewDst>) -> FastTransform<Src, NewDst> {
1092 match *self {
1093 FastTransform::Offset(offset) => match *other {
1094 FastTransform::Offset(other_offset) => {
1095 FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0))
1096 }
1097 FastTransform::Transform { transform: ref other_transform, .. } => {
1098 FastTransform::with_transform(
1099 other_transform
1100 .with_source::<Src>()
1101 .pre_translate(offset.to_3d())
1102 )
1103 }
1104 }
1105 FastTransform::Transform { ref transform, ref inverse, is_2d } => match *other {
1106 FastTransform::Offset(other_offset) => {
1107 FastTransform::with_transform(
1108 transform
1109 .then_translate(other_offset.to_3d())
1110 .with_destination::<NewDst>()
1111 )
1112 }
1113 FastTransform::Transform { transform: ref other_transform, inverse: ref other_inverse, is_2d: other_is_2d } => {
1114 FastTransform::Transform {
1115 transform: transform.then(other_transform),
1116 inverse: inverse.as_ref().and_then(|self_inv|
1117 other_inverse.as_ref().map(|other_inv| other_inv.then(self_inv))
1118 ),
1119 is_2d: is_2d & other_is_2d,
1120 }
1121 }
1122 }
1123 }
1124 }
1125
1126 pub fn pre_transform<NewSrc>(
1127 &self,
1128 other: &FastTransform<NewSrc, Src>
1129 ) -> FastTransform<NewSrc, Dst> {
1130 other.then(self)
1131 }
1132
1133 pub fn pre_translate(&self, other_offset: Vector2D<f32, Src>) -> Self {
1134 match *self {
1135 FastTransform::Offset(offset) =>
1136 FastTransform::Offset(offset + other_offset),
1137 FastTransform::Transform { transform, .. } =>
1138 FastTransform::with_transform(transform.pre_translate(other_offset.to_3d()))
1139 }
1140 }
1141
1142 pub fn then_translate(&self, other_offset: Vector2D<f32, Dst>) -> Self {
1143 match *self {
1144 FastTransform::Offset(offset) => {
1145 FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0))
1146 }
1147 FastTransform::Transform { ref transform, .. } => {
1148 let transform = transform.then_translate(other_offset.to_3d());
1149 FastTransform::with_transform(transform)
1150 }
1151 }
1152 }
1153
1154 #[inline(always)]
1155 pub fn is_backface_visible(&self) -> bool {
1156 match *self {
1157 FastTransform::Offset(..) => false,
1158 FastTransform::Transform { inverse: None, .. } => false,
1159 FastTransform::Transform { inverse: Some(ref inverse), .. } => inverse.m33 < 0.0,
1162 }
1163 }
1164
1165 #[inline(always)]
1166 pub fn transform_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> {
1167 match *self {
1168 FastTransform::Offset(offset) => {
1169 let new_point = point + offset;
1170 Some(Point2D::from_untyped(new_point.to_untyped()))
1171 }
1172 FastTransform::Transform { ref transform, .. } => transform.transform_point2d(point),
1173 }
1174 }
1175
1176 #[inline(always)]
1177 pub fn project_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> {
1178 match* self {
1179 FastTransform::Offset(..) => self.transform_point2d(point),
1180 FastTransform::Transform{ref transform, ..} => {
1181 let z = -(point.x * transform.m13 + point.y * transform.m23 + transform.m43) / transform.m33;
1188
1189 transform.transform_point3d(point3(point.x, point.y, z)).map(| p3 | point2(p3.x, p3.y))
1190 }
1191 }
1192 }
1193
1194 #[inline(always)]
1195 pub fn inverse(&self) -> Option<FastTransform<Dst, Src>> {
1196 match *self {
1197 FastTransform::Offset(offset) =>
1198 Some(FastTransform::Offset(Vector2D::new(-offset.x, -offset.y))),
1199 FastTransform::Transform { transform, inverse: Some(inverse), is_2d, } =>
1200 Some(FastTransform::Transform {
1201 transform: inverse,
1202 inverse: Some(transform),
1203 is_2d
1204 }),
1205 FastTransform::Transform { inverse: None, .. } => None,
1206
1207 }
1208 }
1209}
1210
1211impl<Src, Dst> From<Transform3D<f32, Src, Dst>> for FastTransform<Src, Dst> {
1212 fn from(transform: Transform3D<f32, Src, Dst>) -> Self {
1213 FastTransform::with_transform(transform)
1214 }
1215}
1216
1217impl<Src, Dst> From<Vector2D<f32, Src>> for FastTransform<Src, Dst> {
1218 fn from(vector: Vector2D<f32, Src>) -> Self {
1219 FastTransform::with_vector(vector)
1220 }
1221}
1222
1223pub type LayoutFastTransform = FastTransform<LayoutPixel, LayoutPixel>;
1224pub type LayoutToWorldFastTransform = FastTransform<LayoutPixel, WorldPixel>;
1225
1226pub fn project_rect<F, T>(
1227 transform: &Transform3D<f32, F, T>,
1228 rect: &Box2D<f32, F>,
1229 bounds: &Box2D<f32, T>,
1230) -> Option<Box2D<f32, T>>
1231 where F: fmt::Debug
1232{
1233 let homogens = [
1234 transform.transform_point2d_homogeneous(rect.top_left()),
1235 transform.transform_point2d_homogeneous(rect.top_right()),
1236 transform.transform_point2d_homogeneous(rect.bottom_left()),
1237 transform.transform_point2d_homogeneous(rect.bottom_right()),
1238 ];
1239
1240 if homogens.iter().any(|h| h.w <= 0.0 || h.w.is_nan()) {
1243 let mut clipper = Clipper::new();
1244 let polygon = Polygon::from_rect(rect.to_rect().cast().cast_unit(), 1);
1245
1246 let planes = match Clipper::<usize>::frustum_planes(
1247 &transform.cast_unit().cast(),
1248 Some(bounds.to_rect().cast_unit().to_f64()),
1249 ) {
1250 Ok(planes) => planes,
1251 Err(..) => return None,
1252 };
1253
1254 for plane in planes {
1255 clipper.add(plane);
1256 }
1257
1258 let results = clipper.clip(polygon);
1259 if results.is_empty() {
1260 return None
1261 }
1262
1263 Some(Box2D::from_points(results
1264 .into_iter()
1265 .flat_map(|poly| &poly.points)
1267 .map(|p| {
1268 let mut homo = transform.transform_point2d_homogeneous(p.to_2d().to_f32().cast_unit());
1269 homo.w = homo.w.max(0.00000001); homo.to_point2d().unwrap()
1271 })
1272 ))
1273 } else {
1274 Some(Box2D::from_points(&[
1276 homogens[0].to_point2d().unwrap(),
1277 homogens[1].to_point2d().unwrap(),
1278 homogens[2].to_point2d().unwrap(),
1279 homogens[3].to_point2d().unwrap(),
1280 ]))
1281 }
1282}
1283
1284pub fn drain_filter<T, Filter, Action>(
1326 vec: &mut Vec<T>,
1327 mut filter: Filter,
1328 mut action: Action,
1329)
1330where
1331 Filter: FnMut(&mut T) -> bool,
1332 Action: FnMut(T)
1333{
1334 let mut i = 0;
1335 while i != vec.len() {
1336 if filter(&mut vec[i]) {
1337 action(vec.remove(i));
1338 } else {
1339 i += 1;
1340 }
1341 }
1342}
1343
1344
1345#[derive(Debug)]
1346pub struct Recycler {
1347 pub num_allocations: usize,
1348}
1349
1350impl Recycler {
1351 const MAX_EXTRA_CAPACITY_PERCENT: usize = 200;
1354 const MIN_EXTRA_CAPACITY_PERCENT: usize = 20;
1356 const MIN_VECTOR_LENGTH: usize = 16;
1358
1359 pub fn new() -> Self {
1360 Recycler {
1361 num_allocations: 0,
1362 }
1363 }
1364
1365 pub fn recycle_vec<T>(&mut self, vec: &mut Vec<T>) {
1368 let extra_capacity = (vec.capacity() - vec.len()) * 100 / vec.len().max(Self::MIN_VECTOR_LENGTH);
1369
1370 if extra_capacity > Self::MAX_EXTRA_CAPACITY_PERCENT {
1371 *vec = Vec::with_capacity(vec.len() + vec.len() * Self::MIN_EXTRA_CAPACITY_PERCENT / 100);
1376 self.num_allocations += 1;
1377 } else {
1378 vec.clear();
1379 }
1380 }
1381}
1382
1383#[derive(Copy, Clone, Debug)]
1386pub struct Preallocator {
1387 size: usize,
1388}
1389
1390impl Preallocator {
1391 pub fn new(initial_size: usize) -> Self {
1392 Preallocator {
1393 size: initial_size,
1394 }
1395 }
1396
1397 pub fn record_vec<T>(&mut self, vec: &[T]) {
1399 let len = vec.len();
1400 if len > self.size {
1401 self.size = len;
1402 } else {
1403 self.size = (self.size + len) / 2;
1404 }
1405 }
1406
1407 pub fn preallocation_size(&self) -> usize {
1409 (self.size + 15) & !15
1412 }
1413
1414 pub fn preallocate_vec<T>(&self, vec: &mut Vec<T>) {
1419 let len = vec.len();
1420 let cap = self.preallocation_size();
1421 if len < cap {
1422 vec.reserve(cap - len);
1423 }
1424 }
1425
1426 pub fn preallocate_framevec<T>(&self, vec: &mut FrameVec<T>) {
1431 let len = vec.len();
1432 let cap = self.preallocation_size();
1433 if len < cap {
1434 vec.reserve(cap - len);
1435 }
1436 }
1437}
1438
1439impl Default for Preallocator {
1440 fn default() -> Self {
1441 Self::new(0)
1442 }
1443}
1444
1445pub fn scale_factors<Src, Dst>(
1454 mat: &Transform3D<f32, Src, Dst>
1455) -> (f32, f32) {
1456 let m11 = mat.m11 as f64;
1457 let m12 = mat.m12 as f64;
1458 let det = m11 * mat.m22 as f64 - m12 * mat.m21 as f64;
1460 if det == 0.0 {
1461 return (0.0, 0.0);
1462 }
1463
1464 let det = det.abs();
1466
1467 let major = (m11 * m11 + m12 * m12).sqrt();
1468 let minor = if major != 0.0 { det / major } else { 0.0 };
1469
1470 (major as f32, minor as f32)
1471}
1472
1473#[test]
1474fn scale_factors_large() {
1475 let mat = Transform3D::<f32, (), ()>::new(
1477 1.6534229920333123e27, 3.673100922561787e27, 0.0, 0.0,
1478 -3.673100922561787e27, 1.6534229920333123e27, 0.0, 0.0,
1479 0.0, 0.0, 1.0, 0.0,
1480 -828140552192.0, -1771307401216.0, 0.0, 1.0,
1481 );
1482 let (major, minor) = scale_factors(&mat);
1483 assert!(major.is_normal() && minor.is_normal());
1484}
1485
1486pub fn clamp_to_scale_factor(val: f32, round_down: bool) -> f32 {
1493 const SCALE_RESOLUTION: f32 = 2.0;
1497
1498 let val = val.abs();
1501
1502 let (val, inverse) = if val < 1.0 {
1503 (1.0 / val, true)
1504 } else {
1505 (val, false)
1506 };
1507
1508 let power = val.log2() / SCALE_RESOLUTION.log2();
1509
1510 let power = if (power - power.round()).abs() < 1e-5 {
1514 power.round()
1515 } else if inverse != round_down {
1516 power.floor()
1519 } else {
1520 power.ceil()
1523 };
1524
1525 let scale = SCALE_RESOLUTION.powf(power);
1526
1527 if inverse {
1528 1.0 / scale
1529 } else {
1530 scale
1531 }
1532}
1533
1534pub fn round_up_to_multiple(val: usize, mul: NonZeroUsize) -> usize {
1536 match val % mul.get() {
1537 0 => val,
1538 rem => val - rem + mul.get(),
1539 }
1540}
1541
1542
1543#[macro_export]
1544macro_rules! c_str {
1545 ($lit:expr) => {
1546 unsafe {
1547 std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr()
1548 as *const std::os::raw::c_char)
1549 }
1550 }
1551}
1552
1553pub struct WeakTable {
1556 inner: Vec<std::sync::Weak<Vec<u8>>>
1557}
1558
1559impl WeakTable {
1560 pub fn new() -> WeakTable {
1561 WeakTable { inner: Vec::new() }
1562 }
1563 pub fn insert(&mut self, x: std::sync::Weak<Vec<u8>>) {
1564 if self.inner.len() == self.inner.capacity() {
1565 self.remove_expired();
1566
1567 if self.inner.len() * 3 < self.inner.capacity() {
1571 self.inner.shrink_to_fit();
1575 } else {
1576 self.inner.reserve(self.inner.len())
1578 }
1579 }
1580 self.inner.push(x);
1581 }
1582
1583 fn remove_expired(&mut self) {
1584 self.inner.retain(|x| x.strong_count() > 0)
1585 }
1586
1587 pub fn iter(&self) -> impl Iterator<Item = Arc<Vec<u8>>> + '_ {
1588 self.inner.iter().filter_map(|x| x.upgrade())
1589 }
1590}
1591
1592#[test]
1593fn weak_table() {
1594 let mut tbl = WeakTable::new();
1595 let mut things = Vec::new();
1596 let target_count = 50;
1597 for _ in 0..target_count {
1598 things.push(Arc::new(vec![4]));
1599 }
1600 for i in &things {
1601 tbl.insert(Arc::downgrade(i))
1602 }
1603 assert_eq!(tbl.inner.len(), target_count);
1604 drop(things);
1605 assert_eq!(tbl.iter().count(), 0);
1606
1607 for _ in 0..target_count*2 {
1610 tbl.insert(Arc::downgrade(&Arc::new(vec![5])))
1611 }
1612 assert!(tbl.inner.capacity() <= 4);
1613}
1614
1615#[test]
1616fn scale_offset_pre_post() {
1617 let a = ScaleOffset::new(1.0, 2.0, 3.0, 4.0);
1618 let b = ScaleOffset::new(5.0, 6.0, 7.0, 8.0);
1619
1620 assert_eq!(a.then(&b), b.pre_transform(&a));
1621 assert_eq!(a.then_scale(10.0), a.then(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0))));
1622 assert_eq!(a.pre_scale(10.0), a.pre_transform(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0))));
1623}