Skip to main content

webrender/prim_store/gradient/
mod.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::{ColorF, ColorU, GradientStop};
6use api::units::{LayoutRect, LayoutSize, LayoutVector2D};
7use std::hash;
8
9mod linear;
10mod radial;
11mod conic;
12
13pub use linear::*;
14pub use radial::*;
15pub use conic::*;
16
17/// A hashable gradient stop that can be used in primitive keys.
18#[cfg_attr(feature = "capture", derive(Serialize))]
19#[cfg_attr(feature = "replay", derive(Deserialize))]
20#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
21pub struct GradientStopKey {
22    pub offset: f32,
23    pub color: ColorU,
24}
25
26impl GradientStopKey {
27    pub fn empty() -> Self {
28        GradientStopKey {
29            offset: 0.0,
30            color: ColorU::new(0, 0, 0, 0),
31        }
32    }
33}
34
35impl Into<GradientStopKey> for GradientStop {
36    fn into(self) -> GradientStopKey {
37        GradientStopKey {
38            offset: self.offset,
39            color: self.color.into(),
40        }
41    }
42}
43
44// Convert `stop_keys` into a vector of `GradientStop`s, which is a more
45// convenient representation for the current gradient builder. Compute the
46// minimum stop alpha along the way.
47fn stops_and_min_alpha(stop_keys: &[GradientStopKey]) -> (Vec<GradientStop>, f32) {
48    let mut min_alpha: f32 = 1.0;
49    let stops = stop_keys.iter().map(|stop_key| {
50        let color: ColorF = stop_key.color.into();
51        min_alpha = min_alpha.min(color.a);
52
53        GradientStop {
54            offset: stop_key.offset,
55            color,
56        }
57    }).collect();
58
59    (stops, min_alpha)
60}
61
62impl Eq for GradientStopKey {}
63
64impl hash::Hash for GradientStopKey {
65    fn hash<H: hash::Hasher>(&self, state: &mut H) {
66        self.offset.to_bits().hash(state);
67        self.color.hash(state);
68    }
69}
70
71// If the gradient is not tiled we know that any content outside of the clip will not
72// be shown. Applying the clip early reduces how much of the gradient we
73// render and cache. We do this optimization separately on each axis.
74// Returns the offset between the new and old primitive rect origin, to apply to the
75// gradient parameters that are relative to the primitive origin.
76pub fn apply_gradient_local_clip(
77    prim_rect: &mut LayoutRect,
78    stretch_size: &LayoutSize,
79    tile_spacing: &LayoutSize,
80    clip_rect: &LayoutRect,
81) -> LayoutVector2D {
82    let w = prim_rect.max.x.min(clip_rect.max.x) - prim_rect.min.x;
83    let h = prim_rect.max.y.min(clip_rect.max.y) - prim_rect.min.y;
84    let is_tiled_x = w > stretch_size.width + tile_spacing.width;
85    let is_tiled_y = h > stretch_size.height + tile_spacing.height;
86
87    let mut offset = LayoutVector2D::new(0.0, 0.0);
88
89    if !is_tiled_x {
90        let diff = (clip_rect.min.x - prim_rect.min.x).min(prim_rect.width());
91        if diff > 0.0 {
92            prim_rect.min.x += diff;
93            offset.x = -diff;
94        }
95
96        let diff = prim_rect.max.x - clip_rect.max.x;
97        if diff > 0.0 {
98            prim_rect.max.x -= diff;
99        }
100    }
101
102    if !is_tiled_y {
103        let diff = (clip_rect.min.y - prim_rect.min.y).min(prim_rect.height());
104        if diff > 0.0 {
105            prim_rect.min.y += diff;
106            offset.y = -diff;
107        }
108
109        let diff = prim_rect.max.y - clip_rect.max.y;
110        if diff > 0.0 {
111            prim_rect.max.y -= diff;
112        }
113    }
114
115    offset
116}
117
118#[test]
119#[cfg(target_pointer_width = "64")]
120fn test_struct_sizes() {
121    use std::mem;
122    // The sizes of these structures are critical for performance on a number of
123    // talos stress tests. If you get a failure here on CI, there's two possibilities:
124    // (a) You made a structure smaller than it currently is. Great work! Update the
125    //     test expectations and move on.
126    // (b) You made a structure larger. This is not necessarily a problem, but should only
127    //     be done with care, and after checking if talos performance regresses badly.
128    assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed");
129    assert_eq!(mem::size_of::<LinearGradientTemplate>(), 80, "LinearGradientTemplate size changed");
130    assert_eq!(mem::size_of::<LinearGradientKey>(), 72, "LinearGradientKey size changed");
131
132    assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed");
133    assert_eq!(mem::size_of::<RadialGradientTemplate>(), 80, "RadialGradientTemplate size changed");
134    assert_eq!(mem::size_of::<RadialGradientKey>(), 72, "RadialGradientKey size changed");
135
136    assert_eq!(mem::size_of::<ConicGradient>(), 72, "ConicGradient size changed");
137    assert_eq!(mem::size_of::<ConicGradientTemplate>(), 80, "ConicGradientTemplate size changed");
138    assert_eq!(mem::size_of::<ConicGradientKey>(), 72, "ConicGradientKey size changed");
139}