summaryrefslogblamecommitdiffstats
path: root/glow/src/triangle/gradient.rs
blob: 547871e273d083b9b7059980710235e8cee79da5 (plain) (tree)
1
2
3
4
5
6
7
8
                            
                                                              
                                               
                                    
                                      


                                  


























                                                                                


                      
                                                                











                                                                
                          


                            
                                   
       


                                                                                                  
 
                                                    





































































                                                                         
                                                          

         
 



                                                                                                        
 

                                                     























                                                                           
                                                                  






                                                                 
                                                                 



                                                   



                                                                      
                             
                                               



                                          
                                                  




                                                         
                                   



              
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: <Context as HasContext>::Program,
    pub uniform_data: GradientUniformData,
}

#[derive(Debug)]
pub struct GradientUniformData {
    gradient: Gradient,
    transform: Transformation,
    uniform_locations: GradientUniformLocations,
}

#[derive(Debug)]
struct GradientUniformLocations {
    gradient_start_location: <Context as HasContext>::UniformLocation,
    gradient_end_location: <Context as HasContext>::UniformLocation,
    color_stops_size_location: <Context as HasContext>::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: <Context as HasContext>::UniformLocation,
}

#[derive(Copy, Debug, Clone)]
struct ColorStopLocation {
    color: <Context as HasContext>::UniformLocation,
    offset: <Context as HasContext>::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,
            },
        }
    }
}