1use crate::*;
3use anyhow::{Result, ensure};
4use std::sync::Arc;
5
6pub const MAX_BACKDROPS: usize = 4;
8pub const MAX_BACKDROP_SIDE: u32 = 1024;
10pub const BACKDROP_DOWNSAMPLE: u32 = 4;
12
13#[derive(Clone, Debug)]
15pub struct BackdropDraw {
16 pub order: DrawOrder,
18 pub bounds: Bounds<ScaledPixels>,
20 pub content_mask: ContentMask<ScaledPixels>,
22 pub surface_bounds: Bounds<ScaledPixels>,
24 pub clip: ContentMask<ScaledPixels>,
26 pub corner_radii: Corners<ScaledPixels>,
28 pub radius: f32,
30 pub opacity: f32,
32 pub program: Arc<SceneShaderProgram>,
34}
35#[derive(Clone, Debug)]
37pub struct BackdropPlan {
38 pub origin: [u32; 2],
40 pub size: [u32; 2],
42 pub reduced: [u32; 2],
44 pub parameters: [[[f32; 4]; 6]; 3],
46}
47impl BackdropDraw {
48 pub fn plan(&self, viewport: Size<DevicePixels>) -> Result<BackdropPlan> {
51 ensure!(
52 viewport.width.0 > 0 && viewport.height.0 > 0,
53 "invalid backdrop viewport"
54 );
55 let b = self.surface_bounds;
56 let clip = self.clip.bounds;
57 let values = [
58 b.origin.x.0,
59 b.origin.y.0,
60 b.size.width.0,
61 b.size.height.0,
62 clip.origin.x.0,
63 clip.origin.y.0,
64 clip.size.width.0,
65 clip.size.height.0,
66 self.radius,
67 self.opacity,
68 self.corner_radii.top_left.0,
69 self.corner_radii.top_right.0,
70 self.corner_radii.bottom_right.0,
71 self.corner_radii.bottom_left.0,
72 ];
73 ensure!(
74 values.iter().all(|v| v.is_finite()),
75 "non-finite backdrop geometry"
76 );
77 ensure!(
78 self.radius > 0. && self.radius <= 128.,
79 "backdrop radius must be in (0,128] device pixels"
80 );
81 ensure!(
82 (0. ..=1.).contains(&self.opacity),
83 "invalid backdrop opacity"
84 );
85 ensure!(
86 b.size.width.0 > 0. && b.size.height.0 > 0.,
87 "empty backdrop surface"
88 );
89 ensure!(
90 values[10..].iter().all(|v| *v >= 0.),
91 "invalid backdrop corner radii"
92 );
93 let halo = self.radius + BACKDROP_DOWNSAMPLE as f32;
95 let x = (b.origin.x.0 - halo)
96 .floor()
97 .max(0.)
98 .min(viewport.width.0 as f32) as u32;
99 let y = (b.origin.y.0 - halo)
100 .floor()
101 .max(0.)
102 .min(viewport.height.0 as f32) as u32;
103 let right = (b.origin.x.0 + b.size.width.0 + halo)
104 .ceil()
105 .max(0.)
106 .min(viewport.width.0 as f32) as u32;
107 let bottom = (b.origin.y.0 + b.size.height.0 + halo)
108 .ceil()
109 .max(0.)
110 .min(viewport.height.0 as f32) as u32;
111 let size = [right.saturating_sub(x), bottom.saturating_sub(y)];
112 ensure!(
113 size.iter().all(|v| *v > 0 && *v <= MAX_BACKDROP_SIDE),
114 "backdrop capture including halo must fit within 1024x1024 device pixels"
115 );
116 let reduced = [
117 size[0].div_ceil(BACKDROP_DOWNSAMPLE),
118 size[1].div_ceil(BACKDROP_DOWNSAMPLE),
119 ];
120 let mut parameters = [[[0.; 4]; 6]; 3];
121 for pass in 0..2 {
122 parameters[pass][0] = [reduced[0] as f32, reduced[1] as f32, pass as f32, 0.];
123 parameters[pass][1] = [0., 0., reduced[0] as f32, reduced[1] as f32];
124 parameters[pass][2] = [0., 0., 1., 1.];
125 }
126 parameters[0][4][0] = self.radius / 4.;
127 parameters[1][4][1] = self.radius / 4. * reduced[1] as f32 / size[1] as f32;
128 parameters[2][0] = [viewport.width.0 as f32, viewport.height.0 as f32, 2., 0.];
129 parameters[2][1] = [b.origin.x.0, b.origin.y.0, b.size.width.0, b.size.height.0];
130 parameters[2][2] = [
131 (b.origin.x.0 - x as f32) / size[0] as f32,
132 (b.origin.y.0 - y as f32) / size[1] as f32,
133 b.size.width.0 / size[0] as f32,
134 b.size.height.0 / size[1] as f32,
135 ];
136 parameters[2][3] = [
137 clip.origin.x.0,
138 clip.origin.y.0,
139 clip.origin.x.0 + clip.size.width.0,
140 clip.origin.y.0 + clip.size.height.0,
141 ];
142 parameters[2][4][2] = self.opacity;
143 parameters[2][5] = [
144 self.corner_radii.top_left.0,
145 self.corner_radii.top_right.0,
146 self.corner_radii.bottom_right.0,
147 self.corner_radii.bottom_left.0,
148 ];
149 Ok(BackdropPlan {
150 origin: [x, y],
151 size,
152 reduced,
153 parameters,
154 })
155 }
156}