Skip to main content

webrender/
util.rs

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