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 }