use vyre::ir::Program;
use super::blur::{gaussian_blur_2pass, GaussianBlurStages};
use super::downsample::downsample_2x;
use super::filter_chain::filter_chain;
use super::upsample::upsample_2x;
const OP_ID: &str = "vyre-libs::visual::glass";
#[derive(Clone, Debug)]
pub struct GlassParams {
pub width: u32,
pub height: u32,
pub blur_radius: u32,
pub blur_sigma: f32,
pub tint_rgba: u32,
pub brightness: f32,
pub saturation: f32,
}
#[must_use]
pub fn glass_blur_stage(
input: &str,
output: &str,
scratch: &str,
params: &GlassParams,
) -> GaussianBlurStages {
gaussian_blur_2pass(
input,
output,
scratch,
params.width,
params.height,
params.blur_radius,
params.blur_sigma,
)
}
#[must_use]
pub fn glass_filter_stage(pixels: &str, params: &GlassParams) -> Program {
let count = params.width * params.height;
filter_chain(
pixels,
count,
params.brightness,
1.0,
params.saturation,
0.0,
)
}
#[must_use]
pub fn glass_stages(
input: &str,
output: &str,
scratch: &str,
params: &GlassParams,
) -> (GaussianBlurStages, Program) {
(
glass_blur_stage(input, output, scratch, params),
glass_filter_stage(output, params),
)
}
#[must_use]
pub fn glass_stages_half_res(
input: &str,
output: &str,
scratch: &str,
half: &str,
half_scratch: &str,
params: &GlassParams,
) -> GlassHalfResPipeline {
if params.blur_radius <= 8 || params.width < 4 || params.height < 4 {
let (blur, filter) = glass_stages(input, output, scratch, params);
return GlassHalfResPipeline::FullRes { blur, filter };
}
let half_w = params.width / 2;
let half_h = params.height / 2;
let downsample = downsample_2x(input, half, params.width, params.height);
let half_radius = (params.blur_radius / 2).max(1);
let half_sigma = params.blur_sigma / 2.0;
let blur = gaussian_blur_2pass(
half,
half_scratch,
half,
half_w,
half_h,
half_radius,
half_sigma,
);
let upsample = upsample_2x(half_scratch, output, params.width, params.height);
let filter = glass_filter_stage(output, params);
GlassHalfResPipeline::HalfRes {
downsample,
blur,
upsample,
filter,
}
}
#[derive(Debug)]
pub enum GlassHalfResPipeline {
FullRes {
blur: GaussianBlurStages,
filter: Program,
},
HalfRes {
downsample: Program,
blur: GaussianBlurStages,
upsample: Program,
filter: Program,
},
}
impl GlassHalfResPipeline {
#[must_use]
pub fn stage_count(&self) -> usize {
match self {
Self::FullRes { blur, .. } => blur.stage_count() + 1,
Self::HalfRes { blur, .. } => blur.stage_count() + 3,
}
}
#[must_use]
pub fn programs(&self) -> Vec<&Program> {
match self {
Self::FullRes { blur, filter } => {
let mut programs = Vec::with_capacity(3);
programs.extend(blur.programs());
programs.push(filter);
programs
}
Self::HalfRes {
downsample,
blur,
upsample,
filter,
} => {
let mut programs = Vec::with_capacity(5);
programs.push(downsample);
programs.extend(blur.programs());
programs.push(upsample);
programs.push(filter);
programs
}
}
}
}
inventory::submit! {
crate::harness::OpEntry {
id: OP_ID,
build: || crate::region::tag_program(OP_ID, glass_blur_stage("scene", "output", "scratch", &GlassParams {
width: 4,
height: 4,
blur_radius: 1,
blur_sigma: 0.8,
tint_rgba: 0x0D_FFFFFF,
brightness: 1.0,
saturation: 0.75,
}).horizontal),
test_inputs: Some(|| {
let pixels = vec![0xFFFF_FFFFu32; 16];
vec![vec![
crate::visual::byte_helpers::u32_words_to_le_bytes(&pixels),
vec![0u8; 64],
]]
}),
expected_output: Some(|| {
let pixels = vec![0xFFFF_FFFFu32; 16];
vec![vec![crate::visual::byte_helpers::u32_words_to_le_bytes(&pixels)]]
}),
category: Some("visual"),
}
}