Skip to main content

webrender/
util.rs

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
5use 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
18// Matches the definition of SK_ScalarNearlyZero in Skia.
19const 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.
23pub struct Allocation<'a, T: 'a> {
24    vec: &'a mut Vec<T>,
25    index: usize,
26}
27
28impl<'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`.
43pub 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    /// 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
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// 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))]
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    // 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        let x0 = rect.min.x * self.scale.x + self.offset.x;
268        let y0 = rect.min.y * self.scale.y + self.offset.y;
269        // TODO: If the supplied rect is invalid (has size < 0) we must ensure that the
270        // returned rect has size zero else some tests fail. Using the max() of the min
271        // and max points ensures that is the case. In future we could catch / assert /
272        // fix these invalid rects earlier, and assert here instead.
273        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        // TODO: If the supplied rect is invalid (has size < 0) we must ensure that the
286        // returned rect has size zero else some tests fail. Using the max() of the min
287        // and max points ensures that is the case. In future we could catch / assert /
288        // fix these invalid rects earlier, and assert here instead.
289        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
370// TODO: Implement these in euclid!
371pub trait MatrixHelpers<Src, Dst> {
372    /// A port of the preserves2dAxisAlignment function in Skia.
373    /// Defined in the SkMatrix44 class.
374    fn preserves_2d_axis_alignment(&self) -> bool;
375    fn has_perspective_component(&self) -> bool;
376    /// Scale factors for content that is coplanar in the source space (z=0),
377    /// which is the case for the contents of a picture surface. Returns `None`
378    /// if the perspective divide varies across that plane, where no single
379    /// scale factor is correct.
380    ///
381    /// Much weaker than `is_2d_on_z_plane`: this only asks how the plane is
382    /// mapped, not that mapping it back through `inverse()` agrees, so a
383    /// constant `w != 1` is fine (it divides out) and the `z` terms don't
384    /// matter at all.
385    fn coplanar_scale_factors(&self) -> Option<(f32, f32)>;
386    fn has_2d_inverse(&self) -> bool;
387    /// Check if the matrix post-scaling on either the X or Y axes could cause geometry
388    /// transformed by this matrix to have scaling exceeding the supplied limit.
389    fn exceeds_2d_scale(&self, limit: f64) -> bool;
390    fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>>;
391    fn inverse_rect_footprint(&self, rect: &Box2D<f32, Dst>) -> Option<Box2D<f32, Src>>;
392    fn is_simple_translation(&self) -> bool;
393    fn is_simple_2d_translation(&self) -> bool;
394    fn is_2d_scale_translation(&self) -> bool;
395    /// Return the determinant of the 2D part of the matrix.
396    fn determinant_2d(&self) -> f32;
397    /// Turn Z transformation into identity. This is useful when crossing "flat"
398    /// transform styled stacking contexts upon traversing the coordinate systems.
399    fn flatten_z_output(&mut self);
400
401    fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst>;
402}
403
404impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> {
405    fn preserves_2d_axis_alignment(&self) -> bool {
406        if self.m14 != 0.0 || self.m24 != 0.0 {
407            return false;
408        }
409
410        let mut col0 = 0;
411        let mut col1 = 0;
412        let mut row0 = 0;
413        let mut row1 = 0;
414
415        if self.m11.abs() > NEARLY_ZERO {
416            col0 += 1;
417            row0 += 1;
418        }
419        if self.m12.abs() > NEARLY_ZERO {
420            col1 += 1;
421            row0 += 1;
422        }
423        if self.m21.abs() > NEARLY_ZERO {
424            col0 += 1;
425            row1 += 1;
426        }
427        if self.m22.abs() > NEARLY_ZERO {
428            col1 += 1;
429            row1 += 1;
430        }
431
432        col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
433    }
434
435    fn has_perspective_component(&self) -> bool {
436         self.m14.abs() > NEARLY_ZERO ||
437         self.m24.abs() > NEARLY_ZERO ||
438         self.m34.abs() > NEARLY_ZERO ||
439         (self.m44 - 1.0).abs() > NEARLY_ZERO
440    }
441
442    fn coplanar_scale_factors(&self) -> Option<(f32, f32)> {
443        // `w` varies across the z=0 plane: a true keystone, with no single scale.
444        if self.m14.abs() > NEARLY_ZERO ||
445           self.m24.abs() > NEARLY_ZERO {
446            return None;
447        }
448
449        // `w` is constant over that plane, so the mapping is affine with its
450        // 2d part divided through by `m44`. A non-positive `w` means the plane
451        // is degenerate or behind the eye.
452        if self.m44 < NEARLY_ZERO {
453            return None;
454        }
455
456        let (major, minor) = scale_factors(self);
457
458        Some((major / self.m44, minor / self.m44))
459    }
460
461    fn has_2d_inverse(&self) -> bool {
462        self.determinant_2d() != 0.0
463    }
464
465    fn exceeds_2d_scale(&self, limit: f64) -> bool {
466        let limit2 = (limit * limit) as f32;
467        self.m11 * self.m11 + self.m12 * self.m12 > limit2 ||
468        self.m21 * self.m21 + self.m22 * self.m22 > limit2
469    }
470
471    /// Find out a point in `Src` that would be projected into the `target`.
472    fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>> {
473        // form the linear equation for the hyperplane intersection
474        let m = Transform2D::<f32, Src, Dst>::new(
475            self.m11 - target.x * self.m14, self.m12 - target.y * self.m14,
476            self.m21 - target.x * self.m24, self.m22 - target.y * self.m24,
477            self.m41 - target.x * self.m44, self.m42 - target.y * self.m44,
478        );
479        let inv = m.inverse()?;
480        // we found the point, now check if it maps to the positive hemisphere
481        if inv.m31 * self.m14 + inv.m32 * self.m24 + self.m44 > 0.0 {
482            Some(Point2D::new(inv.m31, inv.m32))
483        } else {
484            None
485        }
486    }
487
488    fn inverse_rect_footprint(&self, rect: &Box2D<f32, Dst>) -> Option<Box2D<f32, Src>> {
489        Some(Box2D::from_points(&[
490            self.inverse_project(&rect.top_left())?,
491            self.inverse_project(&rect.top_right())?,
492            self.inverse_project(&rect.bottom_left())?,
493            self.inverse_project(&rect.bottom_right())?,
494        ]))
495    }
496
497    fn is_simple_translation(&self) -> bool {
498        if (self.m11 - 1.0).abs() > NEARLY_ZERO ||
499            (self.m22 - 1.0).abs() > NEARLY_ZERO ||
500            (self.m33 - 1.0).abs() > NEARLY_ZERO ||
501            (self.m44 - 1.0).abs() > NEARLY_ZERO {
502            return false;
503        }
504
505        self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO &&
506            self.m14.abs() < NEARLY_ZERO && self.m21.abs() < NEARLY_ZERO &&
507            self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO &&
508            self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO &&
509            self.m34.abs() < NEARLY_ZERO
510    }
511
512    fn is_simple_2d_translation(&self) -> bool {
513        if !self.is_simple_translation() {
514            return false;
515        }
516
517        self.m43.abs() < NEARLY_ZERO
518    }
519
520    /*  is this...
521     *  X  0  0  0
522     *  0  Y  0  0
523     *  0  0  1  0
524     *  a  b  0  1
525     */
526    fn is_2d_scale_translation(&self) -> bool {
527        (self.m33 - 1.0).abs() < NEARLY_ZERO &&
528            (self.m44 - 1.0).abs() < NEARLY_ZERO &&
529            self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO && self.m14.abs() < NEARLY_ZERO &&
530            self.m21.abs() < NEARLY_ZERO && self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO &&
531            self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO && self.m34.abs() < NEARLY_ZERO &&
532            self.m43.abs() < NEARLY_ZERO
533    }
534
535    fn determinant_2d(&self) -> f32 {
536        self.m11 * self.m22 - self.m12 * self.m21
537    }
538
539    fn flatten_z_output(&mut self) {
540        self.m13 = 0.0;
541        self.m23 = 0.0;
542        self.m33 = 1.0;
543        self.m43 = 0.0;
544        //Note: we used to zero out m3? as well, see "reftests/flatten-all-flat.yaml" test
545    }
546
547    fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst> {
548        Transform3D::new(
549            self.m11, self.m12, self.m13, self.m14,
550            self.m21, self.m22, self.m23, self.m24,
551            self.m31, self.m32, self.m33, self.m34,
552            self.m41, self.m42, self.m43, self.m44,
553        )
554    }
555}
556
557pub trait PointHelpers<U>
558where
559    Self: Sized,
560{
561    fn snap(&self) -> Self;
562}
563
564impl<U> PointHelpers<U> for Point2D<f32, U> {
565    fn snap(&self) -> Self {
566        Point2D::new(
567            self.x.round(),
568            self.y.round(),
569        )
570    }
571}
572
573pub trait VectorHelpers<U>
574where
575    Self: Sized,
576{
577    fn snap(&self) -> Self;
578}
579
580impl<U> VectorHelpers<U> for Vector2D<f32, U> {
581    fn snap(&self) -> Self {
582        Vector2D::new(
583            self.x.round(),
584            self.y.round(),
585        )
586    }
587}
588
589pub trait RectHelpers<U>
590where
591    Self: Sized,
592{
593    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
594    fn snap(&self) -> Self;
595}
596
597impl<U> RectHelpers<U> for Rect<f32, U> {
598    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
599        Rect::new(
600            Point2D::new(x0, y0),
601            Size2D::new(x1 - x0, y1 - y0),
602        )
603    }
604
605    fn snap(&self) -> Self {
606        let origin = Point2D::new(
607            (self.origin.x + 0.5).floor(),
608            (self.origin.y + 0.5).floor(),
609        );
610        Rect::new(
611            origin,
612            Size2D::new(
613                (self.origin.x + self.size.width + 0.5).floor() - origin.x,
614                (self.origin.y + self.size.height + 0.5).floor() - origin.y,
615            ),
616        )
617    }
618}
619
620impl<U> RectHelpers<U> for Box2D<f32, U> {
621    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
622        Box2D {
623            min: Point2D::new(x0, y0),
624            max: Point2D::new(x1, y1),
625        }
626    }
627
628    fn snap(&self) -> Self {
629        self.round()
630    }
631}
632
633pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
634    (b - a) * t + a
635}
636
637#[repr(u32)]
638#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
639#[cfg_attr(feature = "capture", derive(Serialize))]
640#[cfg_attr(feature = "replay", derive(Deserialize))]
641pub enum TransformedRectKind {
642    AxisAligned = 0,
643    Complex = 1,
644}
645
646#[inline(always)]
647pub fn pack_as_float(value: u32) -> f32 {
648    value as f32 + 0.5
649}
650
651#[inline]
652fn extract_inner_rect_impl<U>(
653    rect: &Box2D<f32, U>,
654    radii: &BorderRadius,
655    k: f32,
656) -> Option<Box2D<f32, U>> {
657    // `k` defines how much border is taken into account
658    // We enforce the offsets to be rounded to pixel boundaries
659    // by `ceil`-ing and `floor`-ing them
660
661    let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil();
662    let xr = (rect.width() - k * radii.top_right.width.max(radii.bottom_right.width)).floor();
663    let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil();
664    let yb =
665        (rect.height() - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor();
666
667    if xl <= xr && yt <= yb {
668        Some(Box2D::from_origin_and_size(
669            Point2D::new(rect.min.x + xl, rect.min.y + yt),
670            Size2D::new(xr - xl, yb - yt),
671        ))
672    } else {
673        None
674    }
675}
676
677/// Return an aligned rectangle that is inside the clip region and doesn't intersect
678/// any of the bounding rectangles of the rounded corners.
679pub fn extract_inner_rect_safe<U>(
680    rect: &Box2D<f32, U>,
681    radii: &BorderRadius,
682) -> Option<Box2D<f32, U>> {
683    // value of `k==1.0` is used for extraction of the corner rectangles
684    // see `SEGMENT_CORNER_*` in `clip_shared.glsl`
685    extract_inner_rect_impl(rect, radii, 1.0)
686}
687
688/// Return an aligned rectangle that is inside the clip region and doesn't intersect
689/// any of the bounding rectangles of the rounded corners, with a specific k factor
690/// to control how much of the rounded corner is included.
691pub fn extract_inner_rect_k<U>(
692    rect: &Box2D<f32, U>,
693    radii: &BorderRadius,
694    k: f32,
695) -> Option<Box2D<f32, U>> {
696    extract_inner_rect_impl(rect, radii, k)
697}
698
699#[cfg(test)]
700use euclid::vec3;
701
702#[cfg(test)]
703pub mod test {
704    use super::*;
705    use euclid::default::{Box2D, Point2D, Size2D, Transform3D};
706    use euclid::{Angle, approxeq::ApproxEq};
707    use std::f32::consts::PI;
708    use crate::clip::{is_left_of_line, polygon_contains_point};
709    use crate::prim_store::PolygonKey;
710    use api::FillRule;
711
712    #[test]
713    fn inverse_project() {
714        let m0 = Transform3D::identity();
715        let p0 = Point2D::new(1.0, 2.0);
716        // an identical transform doesn't need any inverse projection
717        assert_eq!(m0.inverse_project(&p0), Some(p0));
718        let m1 = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-PI / 3.0));
719        // rotation by 60 degrees would imply scaling of X component by a factor of 2
720        assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
721    }
722
723    #[test]
724    fn inverse_project_footprint() {
725        let m = Transform3D::new(
726            0.477499992, 0.135000005, -1.0, 0.000624999986,
727            -0.642787635, 0.766044438, 0.0, 0.0,
728            0.766044438, 0.642787635, 0.0, 0.0,
729            1137.10986, 113.71286, 402.0, 0.748749971,
730        );
731        let r = Box2D::from_size(Size2D::new(804.0, 804.0));
732        {
733            let points = &[
734                r.top_left(),
735                r.top_right(),
736                r.bottom_left(),
737                r.bottom_right(),
738            ];
739            let mi = m.inverse().unwrap();
740            // In this section, we do the forward and backward transformation
741            // to confirm that its bijective.
742            // We also do the inverse projection path, and confirm it functions the same way.
743            info!("Points:");
744            for p in points {
745                let pp = m.transform_point2d_homogeneous(*p);
746                let p3 = pp.to_point3d().unwrap();
747                let pi = mi.transform_point3d_homogeneous(p3);
748                let px = pi.to_point2d().unwrap();
749                let py = m.inverse_project(&pp.to_point2d().unwrap()).unwrap();
750                info!("\t{:?} -> {:?} -> {:?} -> ({:?} -> {:?}, {:?})", p, pp, p3, pi, px, py);
751                assert!(px.approx_eq_eps(p, &Point2D::new(0.001, 0.001)));
752                assert!(py.approx_eq_eps(p, &Point2D::new(0.001, 0.001)));
753            }
754        }
755        // project
756        let rp = project_rect(&m, &r, &Box2D::from_size(Size2D::new(1000.0, 1000.0))).unwrap();
757        info!("Projected {:?}", rp);
758        // one of the points ends up in the negative hemisphere
759        assert_eq!(m.inverse_project(&rp.min), None);
760        // inverse
761        if let Some(ri) = m.inverse_rect_footprint(&rp) {
762            // inverse footprint should be larger, since it doesn't know the original Z
763            assert!(ri.contains_box(&r), "Inverse {:?}", ri);
764        }
765    }
766
767    fn validate_convert(xref: &LayoutTransform) {
768        let so = ScaleOffset::from_transform(xref).unwrap();
769        let xf = so.to_transform();
770        assert!(xref.approx_eq(&xf));
771    }
772
773    #[test]
774    fn negative_scale_map_unmap() {
775        let xref = LayoutTransform::scale(1.0, -1.0, 1.0)
776                        .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
777        let so = ScaleOffset::from_transform(&xref).unwrap();
778        let local_rect = LayoutRect {
779            min: LayoutPoint::new(50.0, -100.0),
780            max: LayoutPoint::new(250.0, 300.0),
781        };
782
783        let mapped_rect = so.map_rect::<LayoutPixel, DevicePixel>(&local_rect);
784        let xf_rect = project_rect(
785            &xref,
786            &local_rect,
787            &LayoutRect::max_rect(),
788        ).unwrap();
789
790        assert!(mapped_rect.min.x.approx_eq(&xf_rect.min.x));
791        assert!(mapped_rect.min.y.approx_eq(&xf_rect.min.y));
792        assert!(mapped_rect.max.x.approx_eq(&xf_rect.max.x));
793        assert!(mapped_rect.max.y.approx_eq(&xf_rect.max.y));
794
795        let unmapped_rect = so.unmap_rect::<DevicePixel, LayoutPixel>(&mapped_rect);
796        assert!(unmapped_rect.min.x.approx_eq(&local_rect.min.x));
797        assert!(unmapped_rect.min.y.approx_eq(&local_rect.min.y));
798        assert!(unmapped_rect.max.x.approx_eq(&local_rect.max.x));
799        assert!(unmapped_rect.max.y.approx_eq(&local_rect.max.y));
800    }
801
802    #[test]
803    fn scale_offset_convert() {
804        let xref = LayoutTransform::translation(130.0, 200.0, 0.0);
805        validate_convert(&xref);
806
807        let xref = LayoutTransform::scale(13.0, 8.0, 1.0);
808        validate_convert(&xref);
809
810        let xref = LayoutTransform::scale(0.5, 0.5, 1.0)
811                        .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
812        validate_convert(&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_convert(&xref);
817    }
818
819    fn validate_inverse(xref: &LayoutTransform) {
820        let s0 = ScaleOffset::from_transform(xref).unwrap();
821        let s1 = s0.inverse().pre_transform(&s0);
822        assert!((s1.scale.x - 1.0).abs() < NEARLY_ZERO &&
823                (s1.scale.y - 1.0).abs() < NEARLY_ZERO &&
824                s1.offset.x.abs() < NEARLY_ZERO &&
825                s1.offset.y.abs() < NEARLY_ZERO,
826                "{:?}",
827                s1);
828    }
829
830    #[test]
831    fn scale_offset_inverse() {
832        let xref = LayoutTransform::translation(130.0, 200.0, 0.0);
833        validate_inverse(&xref);
834
835        let xref = LayoutTransform::scale(13.0, 8.0, 1.0);
836        validate_inverse(&xref);
837
838        let xref = LayoutTransform::translation(124.0, 38.0, 0.0).
839            then_scale(0.5, 0.5, 1.0);
840
841        validate_inverse(&xref);
842
843        let xref = LayoutTransform::scale(30.0, 11.0, 1.0)
844            .then_translate(vec3(50.0, 240.0, 0.0));
845        validate_inverse(&xref);
846    }
847
848    fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) {
849        let x = x1.then(&x0);
850
851        let s0 = ScaleOffset::from_transform(x0).unwrap();
852        let s1 = ScaleOffset::from_transform(x1).unwrap();
853
854        let s = s0.pre_transform(&s1).to_transform();
855
856        assert!(x.approx_eq(&s), "{:?}\n{:?}", x, s);
857    }
858
859    #[test]
860    fn scale_offset_accumulate() {
861        let x0 = LayoutTransform::translation(130.0, 200.0, 0.0);
862        let x1 = LayoutTransform::scale(7.0, 3.0, 1.0);
863
864        validate_accumulate(&x0, &x1);
865    }
866
867    #[test]
868    fn scale_offset_invalid_scale() {
869        let s0 = ScaleOffset::new(0.0, 1.0, 10.0, 20.0);
870        let i0 = s0.inverse();
871        assert_eq!(i0, ScaleOffset::new(0.0, 0.0, 0.0, 0.0));
872
873        let s1 = ScaleOffset::new(1.0, 0.0, 10.0, 20.0);
874        let i1 = s1.inverse();
875        assert_eq!(i1, ScaleOffset::new(0.0, 0.0, 0.0, 0.0));
876    }
877
878    #[test]
879    fn polygon_clip_is_left_of_point() {
880        // Define points of a line through (1, -3) and (-2, 6) to test against.
881        // If the triplet consisting of these two points and the test point
882        // form a counter-clockwise triangle, then the test point is on the
883        // left. The easiest way to visualize this is with an "ascending"
884        // line from low-Y to high-Y.
885        let p0_x = 1.0;
886        let p0_y = -3.0;
887        let p1_x = -2.0;
888        let p1_y = 6.0;
889
890        // Test some points to the left of the line.
891        assert!(is_left_of_line(-9.0, 0.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
892        assert!(is_left_of_line(-1.0, 1.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
893        assert!(is_left_of_line(1.0, -4.0, p0_x, p0_y, p1_x, p1_y) > 0.0);
894
895        // Test some points on the line.
896        assert!(is_left_of_line(-3.0, 9.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
897        assert!(is_left_of_line(0.0, 0.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
898        assert!(is_left_of_line(100.0, -300.0, p0_x, p0_y, p1_x, p1_y) == 0.0);
899
900        // Test some points to the right of the line.
901        assert!(is_left_of_line(0.0, 1.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
902        assert!(is_left_of_line(-4.0, 13.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
903        assert!(is_left_of_line(5.0, -12.0, p0_x, p0_y, p1_x, p1_y) < 0.0);
904    }
905
906    #[test]
907    fn polygon_clip_contains_point() {
908        // We define the points of a self-overlapping polygon, which we will
909        // use to create polygons with different windings and fill rules.
910        let p0 = LayoutPoint::new(4.0, 4.0);
911        let p1 = LayoutPoint::new(6.0, 4.0);
912        let p2 = LayoutPoint::new(4.0, 7.0);
913        let p3 = LayoutPoint::new(2.0, 1.0);
914        let p4 = LayoutPoint::new(8.0, 1.0);
915        let p5 = LayoutPoint::new(6.0, 7.0);
916
917        let poly_clockwise_nonzero = PolygonKey::new(
918            &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Nonzero
919        );
920        let poly_clockwise_evenodd = PolygonKey::new(
921            &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Evenodd
922        );
923        let poly_counter_clockwise_nonzero = PolygonKey::new(
924            &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Nonzero
925        );
926        let poly_counter_clockwise_evenodd = PolygonKey::new(
927            &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Evenodd
928        );
929
930        // We define a rect that provides a bounding clip area of
931        // the polygon.
932        let rect = LayoutRect::from_size(LayoutSize::new(10.0, 10.0));
933
934        // And we'll test three points of interest.
935        let p_inside_once = LayoutPoint::new(5.0, 3.0);
936        let p_inside_twice = LayoutPoint::new(5.0, 5.0);
937        let p_outside = LayoutPoint::new(9.0, 9.0);
938
939        // We should get the same results for both clockwise and
940        // counter-clockwise polygons.
941        // For nonzero polygons, the inside twice point is considered inside.
942        for poly_nonzero in vec![poly_clockwise_nonzero, poly_counter_clockwise_nonzero].iter() {
943            assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_nonzero), true);
944            assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_nonzero), true);
945            assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_nonzero), false);
946        }
947        // For evenodd polygons, the inside twice point is considered outside.
948        for poly_evenodd in vec![poly_clockwise_evenodd, poly_counter_clockwise_evenodd].iter() {
949            assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_evenodd), true);
950            assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_evenodd), false);
951            assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_evenodd), false);
952        }
953    }
954
955    // Ensures that mapping or unmapping an input rect with negative size returns a rect
956    // with size 0, and the origin transformed as expected.
957    #[test]
958    fn map_unmap_negative_size() {
959        let scale_offset = ScaleOffset::new(2.0, 2.0, 1.0, 1.0);
960        let rect = Box2D::new(Point2D::new(5.0, 5.0), Point2D::new(0.0, 0.0));
961        let mapped_rect: Box2D<f32> = scale_offset.map_rect(&rect);
962        assert_eq!(mapped_rect, Box2D::new(Point2D::new(11.0, 11.0), Point2D::new(11.0, 11.0)));
963
964        let unmapped_rect: Box2D<f32> = scale_offset.unmap_rect(&rect);
965        assert_eq!(unmapped_rect, Box2D::new(Point2D::new(2.0, 2.0), Point2D::new(2.0, 2.0)));
966    }
967
968    // Ensures that mapping or unmapping two adjoining input rects returns two rects that
969    // are still adjoining.
970    #[test]
971    fn map_unmap_adjoining_rects() {
972        let so = ScaleOffset::new(0.3, 0.3, 0.0, 0.0);
973        let p1 = Point2D::new(15.0, 15.0);
974        let p2 = Point2D::new(45.0, 45.0);
975        let p3 = Point2D::new(75.0, 75.0);
976
977        let rect_1 = Box2D::new(p1, p2);
978        let rect_2 = Box2D::new(p2, p3);
979
980        let mapped_rect_1: Box2D<f32> = so.map_rect(&rect_1);
981        let mapped_rect_2: Box2D<f32> = so.map_rect(&rect_2);
982        assert_eq!(mapped_rect_1.max, mapped_rect_2.min);
983
984        let unmapped_rect_1: Box2D<f32> = so.unmap_rect(&rect_1);
985        let unmapped_rect_2: Box2D<f32> = so.unmap_rect(&rect_2);
986        assert_eq!(unmapped_rect_1.max, unmapped_rect_2.min);
987    }
988}
989
990pub trait MaxRect {
991    fn max_rect() -> Self;
992}
993
994impl MaxRect for DeviceIntRect {
995    fn max_rect() -> Self {
996        DeviceIntRect::from_origin_and_size(
997            DeviceIntPoint::new(i32::MIN / 2, i32::MIN / 2),
998            DeviceIntSize::new(i32::MAX, i32::MAX),
999        )
1000    }
1001}
1002
1003impl<U> MaxRect for Rect<f32, U> {
1004    fn max_rect() -> Self {
1005        // Having an unlimited bounding box is fine up until we try
1006        // to cast it to `i32`, where we get `-2147483648` for any
1007        // values larger than or equal to 2^31.
1008        //
1009        // Note: clamping to i32::MIN and i32::MAX is not a solution,
1010        // with explanation left as an exercise for the reader.
1011        const MAX_COORD: f32 = 1.0e9;
1012
1013        Rect::new(
1014            Point2D::new(-MAX_COORD, -MAX_COORD),
1015            Size2D::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
1016        )
1017    }
1018}
1019
1020impl<U> MaxRect for Box2D<f32, U> {
1021    fn max_rect() -> Self {
1022        // Having an unlimited bounding box is fine up until we try
1023        // to cast it to `i32`, where we get `-2147483648` for any
1024        // values larger than or equal to 2^31.
1025        //
1026        // Note: clamping to i32::MIN and i32::MAX is not a solution,
1027        // with explanation left as an exercise for the reader.
1028        const MAX_COORD: f32 = 1.0e9;
1029
1030        Box2D::new(
1031            Point2D::new(-MAX_COORD, -MAX_COORD),
1032            Point2D::new(MAX_COORD, MAX_COORD),
1033        )
1034    }
1035}
1036
1037/// An enum that tries to avoid expensive transformation matrix calculations
1038/// when possible when dealing with non-perspective axis-aligned transformations.
1039#[derive(Debug, MallocSizeOf)]
1040#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1041pub enum FastTransform<Src, Dst> {
1042    /// A simple offset, which can be used without doing any matrix math.
1043    Offset(Vector2D<f32, Src>),
1044
1045    /// A 2D transformation with an inverse.
1046    Transform {
1047        transform: Transform3D<f32, Src, Dst>,
1048        inverse: Option<Transform3D<f32, Dst, Src>>,
1049        is_2d: bool,
1050    },
1051}
1052
1053impl<Src, Dst> Clone for FastTransform<Src, Dst> {
1054    fn clone(&self) -> Self {
1055        *self
1056    }
1057}
1058
1059impl<Src, Dst> Copy for FastTransform<Src, Dst> { }
1060
1061impl<Src, Dst> Default for FastTransform<Src, Dst> {
1062    fn default() -> Self {
1063        Self::identity()
1064    }
1065}
1066
1067impl<Src, Dst> FastTransform<Src, Dst> {
1068    pub fn identity() -> Self {
1069        FastTransform::Offset(Vector2D::zero())
1070    }
1071
1072    pub fn with_vector(offset: Vector2D<f32, Src>) -> Self {
1073        FastTransform::Offset(offset)
1074    }
1075
1076    pub fn with_scale_offset(scale_offset: ScaleOffset) -> Self {
1077        if scale_offset.scale == Vector2D::new(1.0, 1.0) {
1078            FastTransform::Offset(Vector2D::from_untyped(scale_offset.offset))
1079        } else {
1080            FastTransform::Transform {
1081                transform: scale_offset.to_transform(),
1082                inverse: Some(scale_offset.inverse().to_transform()),
1083                is_2d: true,
1084            }
1085        }
1086    }
1087
1088    #[inline(always)]
1089    pub fn with_transform(transform: Transform3D<f32, Src, Dst>) -> Self {
1090        if transform.is_simple_2d_translation() {
1091            return FastTransform::Offset(Vector2D::new(transform.m41, transform.m42));
1092        }
1093        let inverse = transform.inverse();
1094        let is_2d = transform.is_2d();
1095        FastTransform::Transform { transform, inverse, is_2d}
1096    }
1097
1098    pub fn to_transform(&self) -> Cow<Transform3D<f32, Src, Dst>> {
1099        match *self {
1100            FastTransform::Offset(offset) => Cow::Owned(
1101                Transform3D::translation(offset.x, offset.y, 0.0)
1102            ),
1103            FastTransform::Transform { ref transform, .. } => Cow::Borrowed(transform),
1104        }
1105    }
1106
1107    /// Return true if this is an identity transform
1108    #[allow(unused)]
1109    pub fn is_identity(&self)-> bool {
1110        match *self {
1111            FastTransform::Offset(offset) => {
1112                offset == Vector2D::zero()
1113            }
1114            FastTransform::Transform { ref transform, .. } => {
1115                *transform == Transform3D::identity()
1116            }
1117        }
1118    }
1119
1120    pub fn then<NewDst>(&self, other: &FastTransform<Dst, NewDst>) -> FastTransform<Src, NewDst> {
1121        match *self {
1122            FastTransform::Offset(offset) => match *other {
1123                FastTransform::Offset(other_offset) => {
1124                    FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0))
1125                }
1126                FastTransform::Transform { transform: ref other_transform, .. } => {
1127                    FastTransform::with_transform(
1128                        other_transform
1129                            .with_source::<Src>()
1130                            .pre_translate(offset.to_3d())
1131                    )
1132                }
1133            }
1134            FastTransform::Transform { ref transform, ref inverse, is_2d } => match *other {
1135                FastTransform::Offset(other_offset) => {
1136                    FastTransform::with_transform(
1137                        transform
1138                            .then_translate(other_offset.to_3d())
1139                            .with_destination::<NewDst>()
1140                    )
1141                }
1142                FastTransform::Transform { transform: ref other_transform, inverse: ref other_inverse, is_2d: other_is_2d } => {
1143                    FastTransform::Transform {
1144                        transform: transform.then(other_transform),
1145                        inverse: inverse.as_ref().and_then(|self_inv|
1146                            other_inverse.as_ref().map(|other_inv| other_inv.then(self_inv))
1147                        ),
1148                        is_2d: is_2d & other_is_2d,
1149                    }
1150                }
1151            }
1152        }
1153    }
1154
1155    pub fn pre_transform<NewSrc>(
1156        &self,
1157        other: &FastTransform<NewSrc, Src>
1158    ) -> FastTransform<NewSrc, Dst> {
1159        other.then(self)
1160    }
1161
1162    pub fn pre_translate(&self, other_offset: Vector2D<f32, Src>) -> Self {
1163        match *self {
1164            FastTransform::Offset(offset) =>
1165                FastTransform::Offset(offset + other_offset),
1166            FastTransform::Transform { transform, .. } =>
1167                FastTransform::with_transform(transform.pre_translate(other_offset.to_3d()))
1168        }
1169    }
1170
1171    pub fn then_translate(&self, other_offset: Vector2D<f32, Dst>) -> Self {
1172        match *self {
1173            FastTransform::Offset(offset) => {
1174                FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0))
1175            }
1176            FastTransform::Transform { ref transform, .. } => {
1177                let transform = transform.then_translate(other_offset.to_3d());
1178                FastTransform::with_transform(transform)
1179            }
1180        }
1181    }
1182
1183    #[inline(always)]
1184    pub fn is_backface_visible(&self) -> bool {
1185        match *self {
1186            FastTransform::Offset(..) => false,
1187            FastTransform::Transform { inverse: None, .. } => false,
1188            //TODO: fix this properly by taking "det|M33| * det|M34| > 0"
1189            // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23014
1190            FastTransform::Transform { inverse: Some(ref inverse), .. } => inverse.m33 < 0.0,
1191        }
1192    }
1193
1194    #[inline(always)]
1195    pub fn transform_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> {
1196        match *self {
1197            FastTransform::Offset(offset) => {
1198                let new_point = point + offset;
1199                Some(Point2D::from_untyped(new_point.to_untyped()))
1200            }
1201            FastTransform::Transform { ref transform, .. } => transform.transform_point2d(point),
1202        }
1203    }
1204
1205    #[inline(always)]
1206    pub fn project_point2d(&self, point: Point2D<f32, Src>) -> Option<Point2D<f32, Dst>> {
1207        match* self {
1208            FastTransform::Offset(..) => self.transform_point2d(point),
1209            FastTransform::Transform{ref transform, ..} => {
1210                // Find a value for z that will transform to 0.
1211
1212                // The transformed value of z is computed as:
1213                // z' = point.x * self.m13 + point.y * self.m23 + z * self.m33 + self.m43
1214
1215                // Solving for z when z' = 0 gives us:
1216                let z = -(point.x * transform.m13 + point.y * transform.m23 + transform.m43) / transform.m33;
1217
1218                transform.transform_point3d(point3(point.x, point.y, z)).map(| p3 | point2(p3.x, p3.y))
1219            }
1220        }
1221    }
1222
1223    #[inline(always)]
1224    pub fn inverse(&self) -> Option<FastTransform<Dst, Src>> {
1225        match *self {
1226            FastTransform::Offset(offset) =>
1227                Some(FastTransform::Offset(Vector2D::new(-offset.x, -offset.y))),
1228            FastTransform::Transform { transform, inverse: Some(inverse), is_2d, } =>
1229                Some(FastTransform::Transform {
1230                    transform: inverse,
1231                    inverse: Some(transform),
1232                    is_2d
1233                }),
1234            FastTransform::Transform { inverse: None, .. } => None,
1235
1236        }
1237    }
1238}
1239
1240impl<Src, Dst> From<Transform3D<f32, Src, Dst>> for FastTransform<Src, Dst> {
1241    fn from(transform: Transform3D<f32, Src, Dst>) -> Self {
1242        FastTransform::with_transform(transform)
1243    }
1244}
1245
1246impl<Src, Dst> From<Vector2D<f32, Src>> for FastTransform<Src, Dst> {
1247    fn from(vector: Vector2D<f32, Src>) -> Self {
1248        FastTransform::with_vector(vector)
1249    }
1250}
1251
1252pub type LayoutFastTransform = FastTransform<LayoutPixel, LayoutPixel>;
1253pub type LayoutToWorldFastTransform = FastTransform<LayoutPixel, WorldPixel>;
1254
1255pub fn project_rect<F, T>(
1256    transform: &Transform3D<f32, F, T>,
1257    rect: &Box2D<f32, F>,
1258    bounds: &Box2D<f32, T>,
1259) -> Option<Box2D<f32, T>>
1260 where F: fmt::Debug
1261{
1262    let homogens = [
1263        transform.transform_point2d_homogeneous(rect.top_left()),
1264        transform.transform_point2d_homogeneous(rect.top_right()),
1265        transform.transform_point2d_homogeneous(rect.bottom_left()),
1266        transform.transform_point2d_homogeneous(rect.bottom_right()),
1267    ];
1268
1269    // Note: we only do the full frustum collision when the polygon approaches the camera plane.
1270    // Otherwise, it will be clamped to the screen bounds anyway.
1271    if homogens.iter().any(|h| h.w <= 0.0 || h.w.is_nan()) {
1272        let mut clipper = Clipper::new();
1273        let polygon = Polygon::from_rect(rect.to_rect().cast().cast_unit(), 1);
1274
1275        let planes = match Clipper::<usize>::frustum_planes(
1276            &transform.cast_unit().cast(),
1277            Some(bounds.to_rect().cast_unit().to_f64()),
1278        ) {
1279            Ok(planes) => planes,
1280            Err(..) => return None,
1281        };
1282
1283        for plane in planes {
1284            clipper.add(plane);
1285        }
1286
1287        let results = clipper.clip(polygon);
1288        if results.is_empty() {
1289            return None
1290        }
1291
1292        Some(Box2D::from_points(results
1293            .into_iter()
1294            // filter out parts behind the view plane
1295            .flat_map(|poly| &poly.points)
1296            .map(|p| {
1297                let mut homo = transform.transform_point2d_homogeneous(p.to_2d().to_f32().cast_unit());
1298                homo.w = homo.w.max(0.00000001); // avoid infinite values
1299                homo.to_point2d().unwrap()
1300            })
1301        ))
1302    } else {
1303        // we just checked for all the points to be in positive hemisphere, so `unwrap` is valid
1304        Some(Box2D::from_points(&[
1305            homogens[0].to_point2d().unwrap(),
1306            homogens[1].to_point2d().unwrap(),
1307            homogens[2].to_point2d().unwrap(),
1308            homogens[3].to_point2d().unwrap(),
1309        ]))
1310    }
1311}
1312
1313/// Run the first callback over all elements in the array. If the callback returns true,
1314/// the element is removed from the array and moved to a second callback.
1315///
1316/// This is a simple implementation waiting for Vec::drain_filter to be stable.
1317/// When that happens, code like:
1318///
1319/// let filter = |op| {
1320///     match *op {
1321///         Enum::Foo | Enum::Bar => true,
1322///         Enum::Baz => false,
1323///     }
1324/// };
1325/// drain_filter(
1326///     &mut ops,
1327///     filter,
1328///     |op| {
1329///         match op {
1330///             Enum::Foo => { foo(); }
1331///             Enum::Bar => { bar(); }
1332///             Enum::Baz => { unreachable!(); }
1333///         }
1334///     },
1335/// );
1336///
1337/// Can be rewritten as:
1338///
1339/// let filter = |op| {
1340///     match *op {
1341///         Enum::Foo | Enum::Bar => true,
1342///         Enum::Baz => false,
1343///     }
1344/// };
1345/// for op in ops.drain_filter(filter) {
1346///     match op {
1347///         Enum::Foo => { foo(); }
1348///         Enum::Bar => { bar(); }
1349///         Enum::Baz => { unreachable!(); }
1350///     }
1351/// }
1352///
1353/// See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter
1354pub fn drain_filter<T, Filter, Action>(
1355    vec: &mut Vec<T>,
1356    mut filter: Filter,
1357    mut action: Action,
1358)
1359where
1360    Filter: FnMut(&mut T) -> bool,
1361    Action: FnMut(T)
1362{
1363    let mut i = 0;
1364    while i != vec.len() {
1365        if filter(&mut vec[i]) {
1366            action(vec.remove(i));
1367        } else {
1368            i += 1;
1369        }
1370    }
1371}
1372
1373
1374#[derive(Debug)]
1375pub struct Recycler {
1376    pub num_allocations: usize,
1377}
1378
1379impl Recycler {
1380    /// Maximum extra capacity that a recycled vector is allowed to have. If the actual capacity
1381    /// is larger, we re-allocate the vector storage with lower capacity.
1382    const MAX_EXTRA_CAPACITY_PERCENT: usize = 200;
1383    /// Minimum extra capacity to keep when re-allocating the vector storage.
1384    const MIN_EXTRA_CAPACITY_PERCENT: usize = 20;
1385    /// Minimum sensible vector length to consider for re-allocation.
1386    const MIN_VECTOR_LENGTH: usize = 16;
1387
1388    pub fn new() -> Self {
1389        Recycler {
1390            num_allocations: 0,
1391        }
1392    }
1393
1394    /// Clear a vector for re-use, while retaining the backing memory buffer. May shrink the buffer
1395    /// if it's currently much larger than was actually used.
1396    pub fn recycle_vec<T>(&mut self, vec: &mut Vec<T>) {
1397        let extra_capacity = (vec.capacity() - vec.len()) * 100 / vec.len().max(Self::MIN_VECTOR_LENGTH);
1398
1399        if extra_capacity > Self::MAX_EXTRA_CAPACITY_PERCENT {
1400            // Reduce capacity of the buffer if it is a lot larger than it needs to be. This prevents
1401            // a frame with exceptionally large allocations to cause subsequent frames to retain
1402            // more memory than they need.
1403            //TODO: use `shrink_to` when it's stable
1404            *vec = Vec::with_capacity(vec.len() + vec.len() * Self::MIN_EXTRA_CAPACITY_PERCENT / 100);
1405            self.num_allocations += 1;
1406        } else {
1407            vec.clear();
1408        }
1409    }
1410}
1411
1412/// Record the size of a data structure to preallocate a similar size
1413/// at the next frame and avoid growing it too many time.
1414#[derive(Copy, Clone, Debug)]
1415pub struct Preallocator {
1416    size: usize,
1417}
1418
1419impl Preallocator {
1420    pub fn new(initial_size: usize) -> Self {
1421        Preallocator {
1422            size: initial_size,
1423        }
1424    }
1425
1426    /// Record the size of a vector to preallocate it the next frame.
1427    pub fn record_vec<T>(&mut self, vec: &[T]) {
1428        let len = vec.len();
1429        if len > self.size {
1430            self.size = len;
1431        } else {
1432            self.size = (self.size + len) / 2;
1433        }
1434    }
1435
1436    /// The size that we'll preallocate the vector with.
1437    pub fn preallocation_size(&self) -> usize {
1438        // Round up to multiple of 16 to avoid small tiny
1439        // variations causing reallocations.
1440        (self.size + 15) & !15
1441    }
1442
1443    /// Preallocate vector storage.
1444    ///
1445    /// The preallocated amount depends on the length recorded in the last
1446    /// record_vec call.
1447    pub fn preallocate_vec<T>(&self, vec: &mut Vec<T>) {
1448        let len = vec.len();
1449        let cap = self.preallocation_size();
1450        if len < cap {
1451            vec.reserve(cap - len);
1452        }
1453    }
1454
1455    /// Preallocate vector storage.
1456    ///
1457    /// The preallocated amount depends on the length recorded in the last
1458    /// record_vec call.
1459    pub fn preallocate_framevec<T>(&self, vec: &mut FrameVec<T>) {
1460        let len = vec.len();
1461        let cap = self.preallocation_size();
1462        if len < cap {
1463            vec.reserve(cap - len);
1464        }
1465    }
1466}
1467
1468impl Default for Preallocator {
1469    fn default() -> Self {
1470        Self::new(0)
1471    }
1472}
1473
1474/// Computes the scale factors of this matrix; that is,
1475/// the amounts each basis vector is scaled by.
1476///
1477/// This code comes from gecko gfx/2d/Matrix.h with the following
1478/// modifications:
1479///
1480/// * Removed `xMajor` parameter.
1481/// * All arithmetics is done with double precision.
1482pub fn scale_factors<Src, Dst>(
1483    mat: &Transform3D<f32, Src, Dst>
1484) -> (f32, f32) {
1485    let m11 = mat.m11 as f64;
1486    let m12 = mat.m12 as f64;
1487    // Determinant is just of the 2D component.
1488    let det = m11 * mat.m22 as f64 - m12 * mat.m21 as f64;
1489    if det == 0.0 {
1490        return (0.0, 0.0);
1491    }
1492
1493    // ignore mirroring
1494    let det = det.abs();
1495
1496    let major = (m11 * m11 + m12 * m12).sqrt();
1497    let minor = if major != 0.0 { det / major } else { 0.0 };
1498
1499    (major as f32, minor as f32)
1500}
1501
1502#[test]
1503fn scale_factors_large() {
1504    // https://bugzilla.mozilla.org/show_bug.cgi?id=1748499
1505    let mat = Transform3D::<f32, (), ()>::new(
1506        1.6534229920333123e27, 3.673100922561787e27, 0.0, 0.0,
1507        -3.673100922561787e27, 1.6534229920333123e27, 0.0, 0.0,
1508        0.0, 0.0, 1.0, 0.0,
1509        -828140552192.0, -1771307401216.0, 0.0, 1.0,
1510    );
1511    let (major, minor) = scale_factors(&mat);
1512    assert!(major.is_normal() && minor.is_normal());
1513}
1514
1515/// Clamp scaling factor to a power of two.
1516///
1517/// This code comes from gecko gfx/thebes/gfxUtils.cpp with the following
1518/// modification:
1519///
1520/// * logs are taken in base 2 instead of base e.
1521pub fn clamp_to_scale_factor(val: f32, round_down: bool) -> f32 {
1522    // Arbitary scale factor limitation. We can increase this
1523    // for better scaling performance at the cost of worse
1524    // quality.
1525    const SCALE_RESOLUTION: f32 = 2.0;
1526
1527    // Negative scaling is just a flip and irrelevant to
1528    // our resolution calculation.
1529    let val = val.abs();
1530
1531    let (val, inverse) = if val < 1.0 {
1532        (1.0 / val, true)
1533    } else {
1534        (val, false)
1535    };
1536
1537    let power = val.log2() / SCALE_RESOLUTION.log2();
1538
1539    // If power is within 1e-5 of an integer, round to nearest to
1540    // prevent floating point errors, otherwise round up to the
1541    // next integer value.
1542    let power = if (power - power.round()).abs() < 1e-5 {
1543        power.round()
1544    } else if inverse != round_down {
1545        // Use floor when we are either inverted or rounding down, but
1546        // not both.
1547        power.floor()
1548    } else {
1549        // Otherwise, ceil when we are not inverted and not rounding
1550        // down, or we are inverted and rounding down.
1551        power.ceil()
1552    };
1553
1554    let scale = SCALE_RESOLUTION.powf(power);
1555
1556    if inverse {
1557        1.0 / scale
1558    } else {
1559        scale
1560    }
1561}
1562
1563/// Rounds a value up to the nearest multiple of mul
1564pub fn round_up_to_multiple(val: usize, mul: NonZeroUsize) -> usize {
1565    match val % mul.get() {
1566        0 => val,
1567        rem => val - rem + mul.get(),
1568    }
1569}
1570
1571
1572#[macro_export]
1573macro_rules! c_str {
1574    ($lit:expr) => {
1575        unsafe {
1576            std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr()
1577                                     as *const std::os::raw::c_char)
1578        }
1579    }
1580}
1581
1582/// This is inspired by the `weak-table` crate.
1583/// It holds a Vec of weak pointers that are garbage collected as the Vec
1584pub struct WeakTable {
1585    inner: Vec<std::sync::Weak<Vec<u8>>>
1586}
1587
1588impl WeakTable {
1589    pub fn new() -> WeakTable {
1590        WeakTable { inner: Vec::new() }
1591    }
1592    pub fn insert(&mut self, x: std::sync::Weak<Vec<u8>>) {
1593        if self.inner.len() == self.inner.capacity() {
1594            self.remove_expired();
1595
1596            // We want to make sure that we change capacity()
1597            // even if remove_expired() removes some entries
1598            // so that we don't repeatedly hit remove_expired()
1599            if self.inner.len() * 3 < self.inner.capacity() {
1600                // We use a different multiple for shrinking then
1601                // expanding so that we we don't accidentally
1602                // oscilate.
1603                self.inner.shrink_to_fit();
1604            } else {
1605                // Otherwise double our size
1606                self.inner.reserve(self.inner.len())
1607            }
1608        }
1609        self.inner.push(x);
1610    }
1611
1612    fn remove_expired(&mut self) {
1613        self.inner.retain(|x| x.strong_count() > 0)
1614    }
1615
1616    pub fn iter(&self) -> impl Iterator<Item = Arc<Vec<u8>>> + '_ {
1617        self.inner.iter().filter_map(|x| x.upgrade())
1618    }
1619}
1620
1621#[test]
1622fn weak_table() {
1623    let mut tbl = WeakTable::new();
1624    let mut things = Vec::new();
1625    let target_count = 50;
1626    for _ in 0..target_count {
1627        things.push(Arc::new(vec![4]));
1628    }
1629    for i in &things {
1630        tbl.insert(Arc::downgrade(i))
1631    }
1632    assert_eq!(tbl.inner.len(), target_count);
1633    drop(things);
1634    assert_eq!(tbl.iter().count(), 0);
1635
1636    // make sure that we shrink the table if it gets too big
1637    // by adding a bunch of dead items
1638    for _ in 0..target_count*2 {
1639        tbl.insert(Arc::downgrade(&Arc::new(vec![5])))
1640    }
1641    assert!(tbl.inner.capacity() <= 4);
1642}
1643
1644#[test]
1645fn scale_offset_pre_post() {
1646    let a = ScaleOffset::new(1.0, 2.0, 3.0, 4.0);
1647    let b = ScaleOffset::new(5.0, 6.0, 7.0, 8.0);
1648
1649    assert_eq!(a.then(&b), b.pre_transform(&a));
1650    assert_eq!(a.then_scale(10.0), a.then(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0))));
1651    assert_eq!(a.pre_scale(10.0), a.pre_transform(&ScaleOffset::from_scale(Vector2D::new(10.0, 10.0))));
1652}