use crate::*;
use anyhow::{Result, ensure};
use std::sync::Arc;
pub const MAX_BACKDROPS: usize = 4;
pub const MAX_BACKDROP_SIDE: u32 = 1024;
pub const BACKDROP_DOWNSAMPLE: u32 = 4;
#[derive(Clone, Debug)]
pub struct BackdropDraw {
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub surface_bounds: Bounds<ScaledPixels>,
pub clip: ContentMask<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
pub radius: f32,
pub opacity: f32,
pub program: Arc<SceneShaderProgram>,
}
#[derive(Clone, Debug)]
pub struct BackdropPlan {
pub origin: [u32; 2],
pub size: [u32; 2],
pub reduced: [u32; 2],
pub parameters: [[[f32; 4]; 6]; 3],
}
impl BackdropDraw {
pub fn plan(&self, viewport: Size<DevicePixels>) -> Result<BackdropPlan> {
ensure!(
viewport.width.0 > 0 && viewport.height.0 > 0,
"invalid backdrop viewport"
);
let b = self.surface_bounds;
let clip = self.clip.bounds;
let values = [
b.origin.x.0,
b.origin.y.0,
b.size.width.0,
b.size.height.0,
clip.origin.x.0,
clip.origin.y.0,
clip.size.width.0,
clip.size.height.0,
self.radius,
self.opacity,
self.corner_radii.top_left.0,
self.corner_radii.top_right.0,
self.corner_radii.bottom_right.0,
self.corner_radii.bottom_left.0,
];
ensure!(
values.iter().all(|v| v.is_finite()),
"non-finite backdrop geometry"
);
ensure!(
self.radius > 0. && self.radius <= 128.,
"backdrop radius must be in (0,128] device pixels"
);
ensure!(
(0. ..=1.).contains(&self.opacity),
"invalid backdrop opacity"
);
ensure!(
b.size.width.0 > 0. && b.size.height.0 > 0.,
"empty backdrop surface"
);
ensure!(
values[10..].iter().all(|v| *v >= 0.),
"invalid backdrop corner radii"
);
let halo = self.radius + BACKDROP_DOWNSAMPLE as f32;
let x = (b.origin.x.0 - halo)
.floor()
.max(0.)
.min(viewport.width.0 as f32) as u32;
let y = (b.origin.y.0 - halo)
.floor()
.max(0.)
.min(viewport.height.0 as f32) as u32;
let right = (b.origin.x.0 + b.size.width.0 + halo)
.ceil()
.max(0.)
.min(viewport.width.0 as f32) as u32;
let bottom = (b.origin.y.0 + b.size.height.0 + halo)
.ceil()
.max(0.)
.min(viewport.height.0 as f32) as u32;
let size = [right.saturating_sub(x), bottom.saturating_sub(y)];
ensure!(
size.iter().all(|v| *v > 0 && *v <= MAX_BACKDROP_SIDE),
"backdrop capture including halo must fit within 1024x1024 device pixels"
);
let reduced = [
size[0].div_ceil(BACKDROP_DOWNSAMPLE),
size[1].div_ceil(BACKDROP_DOWNSAMPLE),
];
let mut parameters = [[[0.; 4]; 6]; 3];
for pass in 0..2 {
parameters[pass][0] = [reduced[0] as f32, reduced[1] as f32, pass as f32, 0.];
parameters[pass][1] = [0., 0., reduced[0] as f32, reduced[1] as f32];
parameters[pass][2] = [0., 0., 1., 1.];
}
parameters[0][4][0] = self.radius / 4.;
parameters[1][4][1] = self.radius / 4. * reduced[1] as f32 / size[1] as f32;
parameters[2][0] = [viewport.width.0 as f32, viewport.height.0 as f32, 2., 0.];
parameters[2][1] = [b.origin.x.0, b.origin.y.0, b.size.width.0, b.size.height.0];
parameters[2][2] = [
(b.origin.x.0 - x as f32) / size[0] as f32,
(b.origin.y.0 - y as f32) / size[1] as f32,
b.size.width.0 / size[0] as f32,
b.size.height.0 / size[1] as f32,
];
parameters[2][3] = [
clip.origin.x.0,
clip.origin.y.0,
clip.origin.x.0 + clip.size.width.0,
clip.origin.y.0 + clip.size.height.0,
];
parameters[2][4][2] = self.opacity;
parameters[2][5] = [
self.corner_radii.top_left.0,
self.corner_radii.top_right.0,
self.corner_radii.bottom_right.0,
self.corner_radii.bottom_left.0,
];
Ok(BackdropPlan {
origin: [x, y],
size,
reduced,
parameters,
})
}
}