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