diff --git a/egui/src/paint/color.rs b/egui/src/paint/color.rs index 4580aa59..ac0433fd 100644 --- a/egui/src/paint/color.rs +++ b/egui/src/paint/color.rs @@ -1,23 +1,149 @@ use crate::math::clamp; /// 0-255 gamma space `sRGBA` color with premultiplied alpha. +/// Alpha channel is in linear space. +/// This format is used for space-efficient color representation. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Srgba { - pub r: u8, - pub g: u8, - pub b: u8, - /// Alpha is in linear space (not subject to sRGBA gamma conversion) - pub a: u8, +pub struct Srgba(pub [u8; 4]); + +impl std::ops::Index for Srgba { + type Output = u8; + fn index(&self, index: usize) -> &u8 { + &self.0[index] + } } + +impl std::ops::IndexMut for Srgba { + fn index_mut(&mut self, index: usize) -> &mut u8 { + &mut self.0[index] + } +} + +pub const fn srgba(r: u8, g: u8, b: u8, a: u8) -> Srgba { + Srgba([r, g, b, a]) +} + +impl Srgba { + pub const fn gray(l: u8) -> Self { + Self([l, l, l, 255]) + } + + pub const fn black_alpha(a: u8) -> Self { + Self([0, 0, 0, a]) + } + + pub const fn additive_luminance(l: u8) -> Self { + Self([l, l, l, 0]) + } +} + +// ---------------------------------------------------------------------------- + +pub const TRANSPARENT: Srgba = srgba(0, 0, 0, 0); +pub const BLACK: Srgba = srgba(0, 0, 0, 255); +pub const LIGHT_GRAY: Srgba = srgba(220, 220, 220, 255); +pub const GRAY: Srgba = srgba(160, 160, 160, 255); +pub const WHITE: Srgba = srgba(255, 255, 255, 255); +pub const RED: Srgba = srgba(255, 0, 0, 255); +pub const GREEN: Srgba = srgba(0, 255, 0, 255); +pub const BLUE: Srgba = srgba(0, 0, 255, 255); +pub const YELLOW: Srgba = srgba(255, 255, 0, 255); +pub const LIGHT_BLUE: Srgba = srgba(140, 160, 255, 255); + +// ---------------------------------------------------------------------------- + /// 0-1 linear space `RGBA` color with premultiplied alpha. #[derive(Clone, Copy, Debug, Default, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Rgba { - pub r: f32, - pub g: f32, - pub b: f32, - pub a: f32, +pub struct Rgba(pub [f32; 4]); + +impl std::ops::Index for Rgba { + type Output = f32; + fn index(&self, index: usize) -> &f32 { + &self.0[index] + } +} + +impl std::ops::IndexMut for Rgba { + fn index_mut(&mut self, index: usize) -> &mut f32 { + &mut self.0[index] + } +} + +impl Rgba { + pub const TRANSPARENT: Rgba = Rgba::new(0.0, 0.0, 0.0, 0.0); + pub const BLACK: Rgba = Rgba::new(0.0, 0.0, 0.0, 1.0); + pub const WHITE: Rgba = Rgba::new(1.0, 1.0, 1.0, 1.0); + pub const RED: Rgba = Rgba::new(1.0, 0.0, 0.0, 1.0); + pub const GREEN: Rgba = Rgba::new(0.0, 1.0, 0.0, 1.0); + pub const BLUE: Rgba = Rgba::new(0.0, 0.0, 1.0, 1.0); + + pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { + Self([r, g, b, a]) + } + + pub const fn gray(l: f32) -> Self { + Self([l, l, l, 1.0]) + } + + pub fn luminance_alpha(l: f32, a: f32) -> Self { + debug_assert!(0.0 <= l && l <= 1.0); + debug_assert!(0.0 <= a && a <= 1.0); + Self([l * a, l * a, l * a, a]) + } + + /// Transparent white + pub fn white_alpha(a: f32) -> Self { + debug_assert!(0.0 <= a && a <= 1.0); + Self([a, a, a, a]) + } + + /// Multiply with e.g. 0.5 to make us half transparent + pub fn multiply(self, alpha: f32) -> Self { + Self([ + alpha * self[0], + alpha * self[1], + alpha * self[2], + alpha * self[3], + ]) + } +} + +impl std::ops::Add for Rgba { + type Output = Rgba; + fn add(self, rhs: Rgba) -> Rgba { + Rgba([ + self[0] + rhs[0], + self[1] + rhs[1], + self[2] + rhs[2], + self[3] + rhs[3], + ]) + } +} + +impl std::ops::Mul for Rgba { + type Output = Rgba; + fn mul(self, factor: f32) -> Rgba { + Rgba([ + self[0] * factor, + self[1] * factor, + self[2] * factor, + self[3] * factor, + ]) + } +} + +impl std::ops::Mul for f32 { + type Output = Rgba; + fn mul(self, rgba: Rgba) -> Rgba { + Rgba([ + self * rgba[0], + self * rgba[1], + self * rgba[2], + self * rgba[3], + ]) + } } // ---------------------------------------------------------------------------- @@ -25,23 +151,23 @@ pub struct Rgba { impl From for Rgba { fn from(srgba: Srgba) -> Rgba { - Rgba { - r: linear_from_srgb_byte(srgba.r), - g: linear_from_srgb_byte(srgba.g), - b: linear_from_srgb_byte(srgba.b), - a: srgba.a as f32 / 255.0, - } + Rgba([ + linear_from_srgb_byte(srgba[0]), + linear_from_srgb_byte(srgba[1]), + linear_from_srgb_byte(srgba[2]), + srgba[3] as f32 / 255.0, + ]) } } impl From for Srgba { fn from(rgba: Rgba) -> Srgba { - Srgba { - r: srgb_byte_from_linear(rgba.r), - g: srgb_byte_from_linear(rgba.g), - b: srgb_byte_from_linear(rgba.b), - a: clamp(rgba.a * 255.0, 0.0..=255.0).round() as u8, - } + Srgba([ + srgb_byte_from_linear(rgba[0]), + srgb_byte_from_linear(rgba[1]), + srgb_byte_from_linear(rgba[2]), + clamp(rgba[3] * 255.0, 0.0..=255.0).round() as u8, + ]) } } @@ -74,143 +200,3 @@ fn test_srgba_conversion() { assert_eq!(srgb_byte_from_linear(l), b); } } - -// ---------------------------------------------------------------------------- - -pub const fn srgba(r: u8, g: u8, b: u8, a: u8) -> Srgba { - Srgba { r, g, b, a } -} - -impl Srgba { - pub const fn gray(l: u8) -> Self { - Self { - r: l, - g: l, - b: l, - a: 255, - } - } - - pub const fn black_alpha(a: u8) -> Self { - Self { - r: 0, - g: 0, - b: 0, - a, - } - } - - pub const fn additive_luminance(l: u8) -> Self { - Self { - r: l, - g: l, - b: l, - a: 0, - } - } -} - -// ---------------------------------------------------------------------------- - -pub const TRANSPARENT: Srgba = srgba(0, 0, 0, 0); -pub const BLACK: Srgba = srgba(0, 0, 0, 255); -pub const LIGHT_GRAY: Srgba = srgba(220, 220, 220, 255); -pub const GRAY: Srgba = srgba(160, 160, 160, 255); -pub const WHITE: Srgba = srgba(255, 255, 255, 255); -pub const RED: Srgba = srgba(255, 0, 0, 255); -pub const GREEN: Srgba = srgba(0, 255, 0, 255); -pub const BLUE: Srgba = srgba(0, 0, 255, 255); -pub const YELLOW: Srgba = srgba(255, 255, 0, 255); -pub const LIGHT_BLUE: Srgba = srgba(140, 160, 255, 255); - -// ---------------------------------------------------------------------------- - -impl Rgba { - pub const TRANSPARENT: Rgba = Rgba::new(0.0, 0.0, 0.0, 0.0); - pub const BLACK: Rgba = Rgba::new(0.0, 0.0, 0.0, 1.0); - pub const WHITE: Rgba = Rgba::new(1.0, 1.0, 1.0, 1.0); - pub const RED: Rgba = Rgba::new(1.0, 0.0, 0.0, 1.0); - pub const GREEN: Rgba = Rgba::new(0.0, 1.0, 0.0, 1.0); - pub const BLUE: Rgba = Rgba::new(0.0, 0.0, 1.0, 1.0); - - pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { - Self { r, g, b, a } - } - - pub const fn gray(l: f32) -> Self { - Self { - r: l, - g: l, - b: l, - a: 1.0, - } - } - - pub fn luminance_alpha(l: f32, a: f32) -> Self { - debug_assert!(0.0 <= l && l <= 1.0); - debug_assert!(0.0 <= a && a <= 1.0); - Self { - r: l * a, - g: l * a, - b: l * a, - a, - } - } - - /// Transparent white - pub fn white_alpha(a: f32) -> Self { - debug_assert!(0.0 <= a && a <= 1.0); - Self { - r: a, - g: a, - b: a, - a, - } - } - - /// Multiply with e.g. 0.5 to make us half transparent - pub fn multiply(self, alpha: f32) -> Self { - Self { - r: alpha * self.r, - g: alpha * self.g, - b: alpha * self.b, - a: alpha * self.a, - } - } -} - -impl std::ops::Add for Rgba { - type Output = Rgba; - fn add(self, rhs: Rgba) -> Rgba { - Rgba { - r: self.r + rhs.r, - g: self.g + rhs.g, - b: self.b + rhs.b, - a: self.a + rhs.a, - } - } -} - -impl std::ops::Mul for Rgba { - type Output = Rgba; - fn mul(self, factor: f32) -> Rgba { - Rgba { - r: self.r * factor, - g: self.g * factor, - b: self.b * factor, - a: self.a * factor, - } - } -} - -impl std::ops::Mul for f32 { - type Output = Rgba; - fn mul(self, rgba: Rgba) -> Rgba { - Rgba { - r: self * rgba.r, - g: self * rgba.g, - b: self * rgba.b, - a: self * rgba.a, - } - } -} diff --git a/egui/src/style.rs b/egui/src/style.rs index 1fff990c..1df7e0e9 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -430,9 +430,9 @@ fn ui_slider_vec2(ui: &mut Ui, value: &mut Vec2, range: std::ops::RangeInclusive fn ui_color(ui: &mut Ui, srgba: &mut Srgba, text: &str) { ui.horizontal_centered(|ui| { ui.label(format!("{} sRGBA: ", text)); - ui.add(DragValue::u8(&mut srgba.r)).tooltip_text("r"); - ui.add(DragValue::u8(&mut srgba.g)).tooltip_text("g"); - ui.add(DragValue::u8(&mut srgba.b)).tooltip_text("b"); - ui.add(DragValue::u8(&mut srgba.a)).tooltip_text("a"); + ui.add(DragValue::u8(&mut srgba[0])).tooltip_text("r"); + ui.add(DragValue::u8(&mut srgba[1])).tooltip_text("g"); + ui.add(DragValue::u8(&mut srgba[2])).tooltip_text("b"); + ui.add(DragValue::u8(&mut srgba[3])).tooltip_text("a"); }); } diff --git a/egui_glium/src/painter.rs b/egui_glium/src/painter.rs index 24f9d476..77b47583 100644 --- a/egui_glium/src/painter.rs +++ b/egui_glium/src/painter.rs @@ -234,7 +234,7 @@ impl Painter { .iter() .map(|v| Vertex { a_pos: [v.pos.x, v.pos.y], - a_srgba: [v.color.r, v.color.g, v.color.b, v.color.a], + a_srgba: v.color.0, a_tc: [v.uv.0, v.uv.1], }) .collect(); diff --git a/egui_web/src/webgl.rs b/egui_web/src/webgl.rs index d09a7230..aa5056d5 100644 --- a/egui_web/src/webgl.rs +++ b/egui_web/src/webgl.rs @@ -226,10 +226,10 @@ impl Painter { ); // TODO: sRGBA ? gl.clear_color( - bg_color.r as f32 / 255.0, - bg_color.g as f32 / 255.0, - bg_color.b as f32 / 255.0, - bg_color.a as f32 / 255.0, + bg_color[0] as f32 / 255.0, + bg_color[1] as f32 / 255.0, + bg_color[2] as f32 / 255.0, + bg_color[3] as f32 / 255.0, ); gl.clear(Gl::COLOR_BUFFER_BIT); @@ -277,10 +277,10 @@ impl Painter { let mut colors: Vec = Vec::with_capacity(4 * triangles.vertices.len()); for v in &triangles.vertices { - colors.push(v.color.r); - colors.push(v.color.g); - colors.push(v.color.b); - colors.push(v.color.a); + colors.push(v.color[0]); + colors.push(v.color[1]); + colors.push(v.color[2]); + colors.push(v.color[3]); } // --------------------------------------------------------------------