[refactor] represent colors with arrays

This commit is contained in:
Emil Ernerfeldt 2020-09-02 22:04:10 +02:00
parent 9823e4d63c
commit dc40a5d31d
4 changed files with 162 additions and 176 deletions

View file

@ -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<usize> for Srgba {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
&self.0[index]
}
}
impl std::ops::IndexMut<usize> 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<usize> for Rgba {
type Output = f32;
fn index(&self, index: usize) -> &f32 {
&self.0[index]
}
}
impl std::ops::IndexMut<usize> 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<f32> 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<Rgba> 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<Srgba> 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<Rgba> 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<f32> 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<Rgba> 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,
}
}
}

View file

@ -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");
});
}

View file

@ -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();

View file

@ -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<u8> = 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]);
}
// --------------------------------------------------------------------