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