use crate::program::Version; use crate::triangle::{simple_triangle_program, set_transform}; use glow::{Context, HasContext, NativeProgram}; use iced_graphics::gradient::Linear; use iced_graphics::gradient::Gradient; use iced_graphics::Transformation; #[derive(Debug)] pub struct GradientProgram { pub program: ::Program, pub uniform_data: GradientUniformData, } #[derive(Debug)] pub struct GradientUniformData { gradient: Gradient, transform: Transformation, uniform_locations: GradientUniformLocations, } #[derive(Debug)] struct GradientUniformLocations { gradient_start_location: ::UniformLocation, gradient_end_location: ::UniformLocation, color_stops_size_location: ::UniformLocation, //currently the maximum number of stops is 64 due to needing to allocate the //memory for the array of stops with a const value in GLSL color_stops_locations: [ColorStopLocation; 64], transform_location: ::UniformLocation, } #[derive(Copy, Debug, Clone)] struct ColorStopLocation { color: ::UniformLocation, offset: ::UniformLocation, } impl GradientProgram { pub fn new(gl: &Context, shader_version: &Version) -> Self { let program = simple_triangle_program( gl, shader_version, include_str!("../shader/common/gradient.frag"), ); Self { program, uniform_data: GradientUniformData::new(gl, program), } } pub fn write_uniforms( &mut self, gl: &Context, gradient: &Gradient, transform: &Transformation, ) { if transform != &self.uniform_data.transform { set_transform(gl, self.uniform_data.uniform_locations.transform_location, *transform); } if &self.uniform_data.gradient != gradient { match gradient { Gradient::Linear(linear) => { let gradient_start: [f32; 2] = (linear.start).into(); let gradient_end: [f32; 2] = (linear.end).into(); unsafe { gl.uniform_2_f32( Some( &self .uniform_data .uniform_locations .gradient_start_location, ), gradient_start[0], gradient_start[1], ); gl.uniform_2_f32( Some( &self .uniform_data .uniform_locations .gradient_end_location, ), gradient_end[0], gradient_end[1], ); gl.uniform_1_u32( Some( &self .uniform_data .uniform_locations .color_stops_size_location, ), linear.color_stops.len() as u32, ); for (index, stop) in linear.color_stops.iter().enumerate() { gl.uniform_1_f32( Some( &self .uniform_data .uniform_locations .color_stops_locations[index] .offset, ), stop.offset, ); gl.uniform_4_f32( Some( &self .uniform_data .uniform_locations .color_stops_locations[index] .color, ), stop.color.r, stop.color.g, stop.color.b, stop.color.a, ); } } } } self.uniform_data.gradient = gradient.clone(); } } pub fn use_program(&mut self, gl: &glow::Context, gradient: &Gradient, transform: &Transformation) { unsafe { gl.use_program(Some(self.program)) } self.write_uniforms(gl, gradient, transform); } } impl GradientUniformData { fn new(gl: &Context, program: NativeProgram) -> Self { let gradient_start_location = unsafe { gl.get_uniform_location(program, "gradient_start") } .expect("Gradient - Get gradient_start."); let gradient_end_location = unsafe { gl.get_uniform_location(program, "gradient_end") } .expect("Gradient - Get gradient_end."); let color_stops_size_location = unsafe { gl.get_uniform_location(program, "color_stops_size") } .expect("Gradient - Get color_stops_size."); let color_stops_locations: [ColorStopLocation; 64] = core::array::from_fn(|index| { let offset = unsafe { gl.get_uniform_location( program, &format!("color_stop_offsets[{}]", index), ) } .expect("Gradient - Color stop offset location."); let color = unsafe { gl.get_uniform_location( program, &format!("color_stop_colors[{}]", index), ) } .expect("Gradient - Color stop color location."); ColorStopLocation { color, offset } }); let transform_location = unsafe { gl.get_uniform_location(program, "u_Transform") } .expect("Get transform location."); GradientUniformData { gradient: Gradient::Linear(Linear { start: Default::default(), end: Default::default(), color_stops: vec![], }), transform: Transformation::identity(), uniform_locations: GradientUniformLocations { gradient_start_location, gradient_end_location, color_stops_size_location, color_stops_locations, transform_location, }, } } }