notecrumbs

a nostr opengraph server build on nostrdb and egui
git clone git://jb55.com/notecrumbs
Log | Files | Refs | README | LICENSE

gradient.rs (3377B)


      1 use egui::{lerp, Color32, Pos2, Rgba};
      2 
      3 #[derive(Clone, Hash, PartialEq, Eq)]
      4 pub struct Gradient(pub Vec<Color32>);
      5 
      6 impl Gradient {
      7     pub fn linear(left: Color32, right: Color32) -> Self {
      8         let left = Rgba::from(left);
      9         let right = Rgba::from(right);
     10 
     11         let n = 255;
     12         Self(
     13             (0..=n)
     14                 .map(|i| {
     15                     let t = i as f32 / n as f32;
     16                     Color32::from(lerp(left..=right, t))
     17                 })
     18                 .collect(),
     19         )
     20     }
     21 
     22     pub fn linear_many(colors: Vec<Color32>) -> Self {
     23         if colors.is_empty() {
     24             return Self(Vec::new());
     25         }
     26         if colors.len() == 1 {
     27             return Self(vec![colors[0]; 256]);
     28         }
     29 
     30         let n = 255;
     31         let mut result = Vec::new();
     32         let segments = colors.len() - 1;
     33 
     34         for i in 0..segments {
     35             let left = Rgba::from(colors[i]);
     36             let right = Rgba::from(colors[i + 1]);
     37 
     38             for j in 0..=n {
     39                 let t = j as f32 / n as f32;
     40                 result.push(Color32::from(lerp(left..=right, t)));
     41             }
     42         }
     43 
     44         Self(result)
     45     }
     46 
     47     pub fn radial_alpha_gradient(
     48         center: Pos2,
     49         radius: f32,
     50         start_color: Color32,
     51         end_color: Color32,
     52     ) -> Self {
     53         let start_color = Rgba::from(start_color);
     54         let end_color = Rgba::from(end_color);
     55 
     56         let diameter = (2.0 * radius) as i32;
     57         let mut pixels = Vec::new();
     58 
     59         for x in 0..diameter {
     60             for y in 0..diameter {
     61                 let dx = x as f32 - center.x;
     62                 let dy = y as f32 - center.y;
     63                 let distance = (dx * dx + dy * dy).sqrt();
     64 
     65                 if distance <= radius {
     66                     let t = (distance / radius).clamp(0.0, 1.0);
     67                     let tl = (x as f32) / (diameter as f32);
     68                     let interpolated_color = Color32::from(lerp(start_color..=end_color, tl));
     69                     let alpha = (1.0 - t).clamp(0.0, 1.0);
     70 
     71                     pixels.push(Color32::from_rgba_premultiplied(
     72                         interpolated_color.r(),
     73                         interpolated_color.g(),
     74                         interpolated_color.b(),
     75                         (alpha * 255.0) as u8,
     76                     ));
     77                 } else {
     78                     // Handle pixels outside the circle
     79                     pixels.push(Color32::DEBUG_COLOR);
     80                 }
     81             }
     82         }
     83 
     84         Self(pixels)
     85     }
     86 
     87     /// Do premultiplied alpha-aware blending of the gradient on top of the fill color
     88     /// in gamma-space.
     89     pub fn with_bg_fill(self, bg: Color32) -> Self {
     90         Self(
     91             self.0
     92                 .into_iter()
     93                 .map(|fg| {
     94                     let a = fg.a() as f32 / 255.0;
     95                     Color32::from_rgba_premultiplied(
     96                         (bg[0] as f32 * (1.0 - a) + fg[0] as f32).round() as u8,
     97                         (bg[1] as f32 * (1.0 - a) + fg[1] as f32).round() as u8,
     98                         (bg[2] as f32 * (1.0 - a) + fg[2] as f32).round() as u8,
     99                         (bg[3] as f32 * (1.0 - a) + fg[3] as f32).round() as u8,
    100                     )
    101                 })
    102                 .collect(),
    103         )
    104     }
    105 
    106     pub fn to_pixel_row(&self) -> Vec<Color32> {
    107         self.0.clone()
    108     }
    109 }