#![allow(unused)]
use futures::executor::block_on;
use grafo::{BorderRadii, Shape};
use grafo::{Color, Stroke};
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::window::{Window, WindowId};
const BLUR_EFFECT: u64 = 1;
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[allow(dead_code)]
struct BlurParams {
radius: f32,
_pad: f32,
tex_size: [f32; 2],
}
const HORIZONTAL_BLUR_WGSL: &str = r#"
const DIRECTION: vec2<f32> = vec2<f32>(1.0, 0.0);
struct Params {
radius: f32,
_pad: f32,
tex_size: vec2<f32>,
}
@group(1) @binding(0) var<uniform> params: Params;
@fragment
fn effect_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let pixel = DIRECTION / params.tex_size;
let sigma = max(params.radius / 3.0, 0.001);
var color = vec4<f32>(0.0);
var total_weight = 0.0;
let r = i32(ceil(params.radius));
for (var i = -r; i <= r; i++) {
let offset = f32(i);
let weight = exp(-(offset * offset) / (2.0 * sigma * sigma));
color += textureSample(t_input, s_input, uv + pixel * offset) * weight;
total_weight += weight;
}
return color / total_weight;
}
"#;
const VERTICAL_BLUR_WGSL: &str = r#"
const DIRECTION: vec2<f32> = vec2<f32>(0.0, 1.0);
struct Params {
radius: f32,
_pad: f32,
tex_size: vec2<f32>,
}
@group(1) @binding(0) var<uniform> params: Params;
@fragment
fn effect_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let pixel = DIRECTION / params.tex_size;
let sigma = max(params.radius / 3.0, 0.001);
var color = vec4<f32>(0.0);
var total_weight = 0.0;
let r = i32(ceil(params.radius));
for (var i = -r; i <= r; i++) {
let offset = f32(i);
let weight = exp(-(offset * offset) / (2.0 * sigma * sigma));
color += textureSample(t_input, s_input, uv + pixel * offset) * weight;
total_weight += weight;
}
return color / total_weight;
}
"#;
#[derive(Default)]
struct App<'a> {
window: Option<Arc<Window>>,
renderer: Option<grafo::Renderer<'a>>,
}
impl<'a> ApplicationHandler for App<'a> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = Arc::new(
event_loop
.create_window(
Window::default_attributes()
.with_title("Grafo – Backdrop Blur (Frosted Glass)"),
)
.unwrap(),
);
let window_size = window.inner_size();
let scale_factor = window.scale_factor();
let physical_size = (window_size.width, window_size.height);
let mut renderer = block_on(grafo::Renderer::new(
window.clone(),
physical_size,
scale_factor,
true,
false,
1,
));
renderer
.load_effect(BLUR_EFFECT, &[HORIZONTAL_BLUR_WGSL, VERTICAL_BLUR_WGSL])
.expect("Failed to compile blur effect");
self.window = Some(window);
self.renderer = Some(renderer);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let Some(window) = &self.window else { return };
let Some(renderer) = &mut self.renderer else {
return;
};
if window_id != window.id() {
return;
}
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(physical_size) => {
let new_size = (physical_size.width, physical_size.height);
renderer.resize(new_size);
window.request_redraw();
}
WindowEvent::RedrawRequested => {
let (pw, ph) = renderer.size();
let bg = Shape::rect(
[(20.0, 20.0), (780.0, 580.0)],
Stroke::new(2.0, Color::BLACK),
);
let bg_id = renderer.add_shape(bg, None, None);
renderer.set_shape_color(bg_id, Some(Color::rgb(245, 245, 250)));
let r1 = Shape::rect(
[(60.0, 80.0), (260.0, 260.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),
);
let r1_id = renderer.add_shape(r1, Some(bg_id), None);
renderer.set_shape_color(r1_id, Some(Color::rgb(220, 50, 50)));
let r2 = Shape::rect(
[(180.0, 160.0), (420.0, 380.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),
);
let r2_id = renderer.add_shape(r2, Some(bg_id), None);
renderer.set_shape_color(r2_id, Some(Color::rgb(50, 160, 50)));
let r3 = Shape::rect(
[(340.0, 100.0), (560.0, 300.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),
);
let r3_id = renderer.add_shape(r3, Some(bg_id), None);
renderer.set_shape_color(r3_id, Some(Color::rgb(50, 80, 220)));
let r4 = Shape::rect(
[(100.0, 350.0), (700.0, 540.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),
);
let r4_id = renderer.add_shape(r4, Some(bg_id), None);
renderer.set_shape_color(r4_id, Some(Color::rgb(200, 180, 50)));
let panel = Shape::rect(
[(120.0, 120.0), (520.0, 460.0)],
Stroke::new(2.0, Color::rgb(100, 100, 100)),
);
let panel_id = renderer.add_shape(panel, None, None);
renderer.set_shape_color(panel_id, Some(Color::rgba(255, 255, 255, 100)));
let panel_content = Shape::rounded_rect(
[(240.0, 240.0), (600.0, 540.0)],
BorderRadii::new(100.0),
Stroke::new(1.0, Color::rgb(80, 80, 80)),
);
let content_id = renderer.add_shape(panel_content, Some(panel_id), None);
renderer.set_shape_color(content_id, Some(Color::rgba(200, 220, 255, 120)));
let blur_params = BlurParams {
radius: 12.0,
_pad: 0.0,
tex_size: [pw as f32, ph as f32],
};
renderer
.set_shape_backdrop_effect(
panel_id,
BLUR_EFFECT,
bytemuck::bytes_of(&blur_params),
)
.expect("Failed to set backdrop effect");
let panel2 = Shape::rect(
[(560.0, 200.0), (740.0, 400.0)],
Stroke::new(2.0, Color::rgb(80, 80, 80)),
);
let panel2_id = renderer.add_shape(panel2, None, None);
renderer.set_shape_color(panel2_id, Some(Color::rgba(200, 220, 255, 120)));
let blur_params2 = BlurParams {
radius: 20.0,
_pad: 0.0,
tex_size: [pw as f32, ph as f32],
};
renderer
.set_shape_backdrop_effect(
panel2_id,
BLUR_EFFECT,
bytemuck::bytes_of(&blur_params2),
)
.expect("Failed to set backdrop effect");
match renderer.render() {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(wgpu::SurfaceError::Lost) => renderer.resize(renderer.size()),
Err(wgpu::SurfaceError::OutOfMemory) => event_loop.exit(),
Err(e) => eprintln!("{e:?}"),
}
}
_ => {}
}
}
}
pub fn main() {
env_logger::init();
let event_loop = EventLoop::new().expect("To create the event loop");
let mut app = App::default();
let _ = event_loop.run_app(&mut app);
}