summaryrefslogtreecommitdiff
path: root/tvg/src/colors.rs
diff options
context:
space:
mode:
authorMicha White <botahamec@outlook.com>2024-08-15 20:14:15 -0400
committerMicha White <botahamec@outlook.com>2024-08-15 20:14:15 -0400
commitf8a80039c74332e2101a177ef3fde31ef2077224 (patch)
treef887c96bf9879a28b7ce914ad96161f63ee83190 /tvg/src/colors.rs
parent488c7ed94b0662222fa0d825ab81b60b0b1e5d6c (diff)
Lots a changes
Diffstat (limited to 'tvg/src/colors.rs')
-rw-r--r--tvg/src/colors.rs171
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
+ }
}