diff options
| author | Micha White <botahamec@outlook.com> | 2024-08-15 20:14:15 -0400 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2024-08-15 20:14:15 -0400 |
| commit | f8a80039c74332e2101a177ef3fde31ef2077224 (patch) | |
| tree | f887c96bf9879a28b7ce914ad96161f63ee83190 /tvg/src/colors.rs | |
| parent | 488c7ed94b0662222fa0d825ab81b60b0b1e5d6c (diff) | |
Lots a changes
Diffstat (limited to 'tvg/src/colors.rs')
| -rw-r--r-- | tvg/src/colors.rs | 171 |
1 files changed, 168 insertions, 3 deletions
diff --git a/tvg/src/colors.rs b/tvg/src/colors.rs index 494e6aa..0dd8831 100644 --- a/tvg/src/colors.rs +++ b/tvg/src/colors.rs @@ -5,13 +5,16 @@ use num_enum::TryFromPrimitive; use crate::TvgError; +const GAMMA: f32 = 2.2; +const INVERT_GAMMA: f32 = 1.0 / GAMMA; + /// The color table encodes the palette for this file. /// /// It’s binary content is defined by the `color_encoding` field in the header. /// For the three defined color encodings, each will yield a list of /// `color_count` RGBA tuples. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct ColorTable<C: Color> { +pub struct ColorTable<C: Color> { colors: Box<[C]>, } @@ -59,12 +62,17 @@ impl<C: Color> ColorTable<C> { } /// Returns the number of colors in the table. - fn len(&self) -> usize { + pub fn len(&self) -> usize { self.colors.len() } + /// Returns `true` if the color table has no colors in it + pub fn is_empty(&self) -> bool { + self.colors.is_empty() + } + /// Returns a reference to a color, or `None` if out-of-bounds. - fn get(&self, index: usize) -> Option<&C> { + pub fn get(&self, index: usize) -> Option<&C> { self.colors.get(index) } @@ -128,10 +136,68 @@ pub trait Color: Sized { /// Convert from the RGBA16 format to this format. This may be lossy. fn from_rgba16_lossy(red: u16, green: u16, blue: u16, alpha: u16) -> Self; + /// Convert from the RGBAF32 format to this format. This may be lossy. + fn from_rgbaf32_lossy(red: f32, green: f32, blue: f32, alpha: f32) -> Self; + fn red_u16(&self) -> u16; fn blue_u16(&self) -> u16; fn green_u16(&self) -> u16; fn alpha_u16(&self) -> u16; + + fn red_f32(&self) -> f32; + fn green_f32(&self) -> f32; + fn blue_f32(&self) -> f32; + fn alpha_f32(&self) -> f32; +} + +fn to_color_space(val: f32) -> f32 { + val.powf(INVERT_GAMMA) +} + +fn to_linear(val: f32) -> f32 { + val.powf(GAMMA) +} + +pub(crate) fn blend<C: Color + Clone>(dest: &mut C, src: &C) { + fn lerp_color(src: f32, dst: f32, src_alpha: f32, dst_alpha: f32, alpha: f32) -> f32 { + let src = to_linear(src); + let dst = to_linear(dst); + + let val = (1.0 / alpha) * (src_alpha * src + (1.0 - src_alpha) * dst_alpha * dst); + + to_color_space(val) + } + + if src.alpha_f32() == 1.0 || dest.alpha_u16() == 0 { + *dest = src.clone(); + return; + } else if src.alpha_u16() == 0 { + return; + } + + let src_a = src.alpha_f32(); + let dst_a = dest.alpha_f32(); + let alpha = src_a + (1.0 - src_a) * dst_a; + let red = lerp_color(src.red_f32(), dest.red_f32(), src_a, dst_a, alpha); + let green = lerp_color(src.green_f32(), dest.green_f32(), src_a, dst_a, alpha); + let blue = lerp_color(src.blue_f32(), dest.blue_f32(), src_a, dst_a, alpha); + + *dest = C::from_rgbaf32_lossy(red, green, blue, alpha); +} + +pub(crate) fn lerp<C: Color>(first: &C, second: &C, f: f64) -> C { + fn lerp_color(a: f32, b: f32, f: f64) -> f32 { + a + (b - a) * f as f32 + } + + let f = f.clamp(0.0, 1.0); + + let red = to_color_space(lerp_color(first.red_f32(), second.red_f32(), f)); + let green = to_color_space(lerp_color(first.green_f32(), second.green_f32(), f)); + let blue = to_color_space(lerp_color(first.blue_f32(), second.blue_f32(), f)); + let alpha = lerp_color(first.alpha_f32(), second.alpha_f32(), f); + + C::from_rgbaf32_lossy(red, green, blue, alpha) } /// Each color value is encoded as a sequence of four bytes. @@ -172,6 +238,15 @@ impl Color for Rgba8888 { } } + fn from_rgbaf32_lossy(red: f32, green: f32, blue: f32, alpha: f32) -> Self { + Self { + red: (red * u8::MAX as f32) as u8, + green: (green * u8::MAX as f32) as u8, + blue: (blue * u8::MAX as f32) as u8, + alpha: (alpha * u8::MAX as f32) as u8, + } + } + fn red_u16(&self) -> u16 { (self.red as u16) << 8 } @@ -187,6 +262,22 @@ impl Color for Rgba8888 { fn alpha_u16(&self) -> u16 { (self.alpha as u16) << 8 } + + fn red_f32(&self) -> f32 { + self.red as f32 / u8::MAX as f32 + } + + fn green_f32(&self) -> f32 { + self.green as f32 / u8::MAX as f32 + } + + fn blue_f32(&self) -> f32 { + self.blue as f32 / u8::MAX as f32 + } + + fn alpha_f32(&self) -> f32 { + self.alpha as f32 / u8::MAX as f32 + } } /// Each color value is encoded as a sequence of 2 bytes. @@ -224,6 +315,14 @@ impl Color for Rgb565 { } } + fn from_rgbaf32_lossy(red: f32, green: f32, blue: f32, alpha: f32) -> Self { + Self { + red: (red * (0b011111) as f32) as u8, + green: (green * (0b111111) as f32) as u8, + blue: (blue * (0b011111) as f32) as u8, + } + } + fn red_u16(&self) -> u16 { (self.red as u16) << 11 } @@ -239,6 +338,22 @@ impl Color for Rgb565 { fn alpha_u16(&self) -> u16 { 0 } + + fn red_f32(&self) -> f32 { + self.red as f32 / (0b011111) as f32 + } + + fn green_f32(&self) -> f32 { + self.green as f32 / (0b111111) as f32 + } + + fn blue_f32(&self) -> f32 { + self.blue as f32 / (0b011111) as f32 + } + + fn alpha_f32(&self) -> f32 { + 0.0 + } } /// Each color value is encoded as a sequence of 16 bytes. @@ -279,6 +394,15 @@ impl Color for RgbaF32 { } } + fn from_rgbaf32_lossy(red: f32, green: f32, blue: f32, alpha: f32) -> Self { + Self { + red, + green, + blue, + alpha, + } + } + fn red_u16(&self) -> u16 { (self.red * (u16::MAX as f32)) as u16 } @@ -294,6 +418,22 @@ impl Color for RgbaF32 { fn alpha_u16(&self) -> u16 { (self.alpha * (u16::MAX as f32)) as u16 } + + fn red_f32(&self) -> f32 { + self.red + } + + fn green_f32(&self) -> f32 { + self.green + } + + fn blue_f32(&self) -> f32 { + self.blue + } + + fn alpha_f32(&self) -> f32 { + self.alpha + } } /// Each color value is encoded as a sequence of 8 bytes. @@ -334,6 +474,15 @@ impl Color for Rgba16 { } } + fn from_rgbaf32_lossy(red: f32, green: f32, blue: f32, alpha: f32) -> Self { + Self { + red: (red * u16::MAX as f32) as u16, + green: (green * u16::MAX as f32) as u16, + blue: (blue * u16::MAX as f32) as u16, + alpha: (alpha * u16::MAX as f32) as u16, + } + } + fn red_u16(&self) -> u16 { self.red } @@ -349,4 +498,20 @@ impl Color for Rgba16 { fn alpha_u16(&self) -> u16 { self.alpha } + + fn red_f32(&self) -> f32 { + self.red as f32 / u16::MAX as f32 + } + + fn green_f32(&self) -> f32 { + self.green as f32 / u16::MAX as f32 + } + + fn blue_f32(&self) -> f32 { + self.blue as f32 / u16::MAX as f32 + } + + fn alpha_f32(&self) -> f32 { + self.alpha as f32 / u16::MAX as f32 + } } |
