From 861b467b95be55db3a42182b77dba944869bf49f Mon Sep 17 00:00:00 2001 From: Micha White Date: Mon, 13 Feb 2023 00:24:07 -0500 Subject: Rename the subdirectories --- tvg/src/colors.rs | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 tvg/src/colors.rs (limited to 'tvg/src/colors.rs') diff --git a/tvg/src/colors.rs b/tvg/src/colors.rs new file mode 100644 index 0000000..10bc41c --- /dev/null +++ b/tvg/src/colors.rs @@ -0,0 +1,333 @@ +use std::io::{self, Read}; + +use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; +use num_enum::TryFromPrimitive; + +/// 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 struct ColorTable { + colors: Box<[C]>, +} + +impl ColorTable { + /// Read in one encoding, and convert it to another + pub fn read_from_encoding( + reader: &mut impl Read, + color_count: u32, + encoding: ColorEncoding, + ) -> io::Result { + Ok(match encoding { + ColorEncoding::Rgba8888 => (&ColorTable::::read(reader, color_count)?).into(), + ColorEncoding::Rgb565 => (&ColorTable::::read(reader, color_count)?).into(), + ColorEncoding::RgbaF32 => (&ColorTable::::read(reader, color_count)?).into(), + ColorEncoding::Custom => (&ColorTable::::read(reader, color_count)?).into(), + }) + } + + /// Parse a color table. + fn read(reader: &mut impl Read, color_count: u32) -> io::Result { + let mut colors = Vec::with_capacity(color_count as usize); + for _ in 0..color_count { + colors.push(C::parse_bytes(reader)?); + } + + let colors = colors.into_boxed_slice(); + Ok(Self { colors }) + } + + /// Returns the number of colors in the table. + fn len(&self) -> usize { + self.colors.len() + } + + /// Returns a reference to a color, or `None` if out-of-bounds. + fn get(&self, index: usize) -> Option<&C> { + self.colors.get(index) + } + + fn iter(&self) -> impl Iterator { + self.colors.iter() + } +} + +impl ColorTable {} + +impl From<&ColorTable> for ColorTable { + fn from(value: &ColorTable) -> Self { + let mut colors = Vec::with_capacity(value.len()); + + for color in value.iter() { + let r = color.red_u16(); + let g = color.green_u16(); + let b = color.blue_u16(); + let a = color.alpha_u16(); + colors.push(New::from_rgba16_lossy(r, g, b, a)); + } + + let colors = colors.into_boxed_slice(); + Self { colors } + } +} + +/// The color encoding defines which format the colors in the color table will +/// have. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)] +#[repr(u8)] +pub enum ColorEncoding { + /// Each color is a 4-tuple (red, green, blue, alpha) of bytes with the + /// color channels encoded in sRGB and the alpha as linear alpha. + Rgba8888 = 0, + /// Each color is encoded as a 3-tuple (red, green, blue) with 16 bits per + /// color. + /// + /// While red and blue both use 5 bits, the green channel uses 6 bits. Red + /// uses bit range 0...4, green bits 5...10 and blue bits 11...15. This + /// color also uses the sRGB color space. + Rgb565 = 1, + /// Each color is a 4-tuple (red, green, blue, alpha) of binary32 IEEE 754 + /// floating point value with the color channels encoded in scRGB and the + /// alpha as linear alpha. A color value of 1.0 is full intensity, while a + /// value of 0.0 is zero intensity. + RgbaF32 = 2, + /// The custom color encoding is defined *undefined*. The information how + /// these colors are encoded must be implemented via external means. + Custom = 3, +} + +pub trait Color: Sized { + /// The size of the color's representation in bits + const SIZE: usize; + + /// Attempt to read the color. Returns `Err` if an error occurred while + /// attempting to read [`SIZE`] bytes from `bytes`. + fn parse_bytes(reader: &mut impl Read) -> io::Result; + + /// 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; + + fn red_u16(&self) -> u16; + fn blue_u16(&self) -> u16; + fn green_u16(&self) -> u16; + fn alpha_u16(&self) -> u16; +} + +/// Each color value is encoded as a sequence of four bytes. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct Rgba8888 { + /// Red color channel between 0 and 100% intensity, mapped to byte values 0 + /// to 255. + red: u8, + /// Green color channel between 0 and 100% intensity, mapped to byte values + /// 0 to 255. + green: u8, + /// Blue color channel between 0 and 100% intensity, mapped to byte values + /// 0 to 255. + blue: u8, + /// Transparency channel between 0 and 100% transparency, mapped to byte + /// values 0 to 255. + alpha: u8, +} + +impl Color for Rgba8888 { + const SIZE: usize = 4; + + fn parse_bytes(reader: &mut impl Read) -> io::Result { + Ok(Self { + red: reader.read_u8()?, + green: reader.read_u8()?, + blue: reader.read_u8()?, + alpha: reader.read_u8()?, + }) + } + + fn from_rgba16_lossy(red: u16, green: u16, blue: u16, alpha: u16) -> Self { + Self { + red: (red >> 8) as u8, + green: (green >> 8) as u8, + blue: (blue >> 8) as u8, + alpha: (alpha >> 8) as u8, + } + } + + fn red_u16(&self) -> u16 { + (self.red as u16) << 8 + } + + fn green_u16(&self) -> u16 { + (self.green as u16) << 8 + } + + fn blue_u16(&self) -> u16 { + (self.blue as u16) << 8 + } + + fn alpha_u16(&self) -> u16 { + (self.alpha as u16) << 8 + } +} + +/// Each color value is encoded as a sequence of 2 bytes. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct Rgb565 { + /// Red color channel between 0 and 100% intensity, mapped to integer + /// values 0 to 31. + red: u8, + /// Green color channel between 0 and 100% intensity, mapped to integer + /// values 0 to 63. + green: u8, + /// Blue color channel between 0 and 100% intensity, mapped to integer + /// values 0 to 31. + blue: u8, +} + +impl Color for Rgb565 { + const SIZE: usize = 2; + + fn parse_bytes(reader: &mut impl Read) -> io::Result { + let color = reader.read_u16::()?; + + let red = ((color & 0x001F) << 3) as u8; + let green = ((color & 0x07E0) >> 3) as u8; + let blue = ((color & 0xF800) >> 8) as u8; + + Ok(Self { red, blue, green }) + } + + fn from_rgba16_lossy(red: u16, green: u16, blue: u16, _a: u16) -> Self { + Self { + red: (red >> 11) as u8, + green: (green >> 10) as u8, + blue: (blue >> 11) as u8, + } + } + + fn red_u16(&self) -> u16 { + (self.red as u16) << 11 + } + + fn green_u16(&self) -> u16 { + (self.green as u16) << 11 + } + + fn blue_u16(&self) -> u16 { + (self.blue as u16) << 10 + } + + fn alpha_u16(&self) -> u16 { + 0 + } +} + +/// Each color value is encoded as a sequence of 16 bytes. +#[derive(Debug, Clone, Copy, PartialEq)] +struct RgbaF32 { + /// Red color channel, using 0.0 for 0% intensity and 1.0 for 100% + /// intensity. + red: f32, + /// Green color channel, using 0.0 for 0% intensity and 1.0 for 100% + /// intensity. + green: f32, + /// Blue color channel, using 0.0 for 0% intensity and 1.0 for 100% + /// intensity. + blue: f32, + /// Transparency channel between 0 and 100% transparency, mapped to byte + /// values 0.0 to 1.0. + alpha: f32, +} + +impl Color for RgbaF32 { + const SIZE: usize = 16; + + fn parse_bytes(reader: &mut impl Read) -> io::Result { + Ok(Self { + red: reader.read_f32::()?, + green: reader.read_f32::()?, + blue: reader.read_f32::()?, + alpha: reader.read_f32::()?, + }) + } + + fn from_rgba16_lossy(red: u16, green: u16, blue: u16, alpha: u16) -> Self { + Self { + red: (red as f32) / (u16::MAX as f32), + green: (green as f32) / (u16::MAX as f32), + blue: (blue as f32) / (u16::MAX as f32), + alpha: (alpha as f32) / (u16::MAX as f32), + } + } + + fn red_u16(&self) -> u16 { + (self.red * (u16::MAX as f32)) as u16 + } + + fn green_u16(&self) -> u16 { + (self.green * (u16::MAX as f32)) as u16 + } + + fn blue_u16(&self) -> u16 { + (self.blue * (u16::MAX as f32)) as u16 + } + + fn alpha_u16(&self) -> u16 { + (self.alpha * (u16::MAX as f32)) as u16 + } +} + +/// Each color value is encoded as a sequence of 8 bytes. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Rgba16 { + /// Red color channel between 0 and 100% intensity, mapped to a big-endian + /// 16-bit integer. + red: u16, + /// Green color channel between 0 and 100% intensity, mapped to a + /// big-endian 16-bit integer. + green: u16, + /// Blue color channel between 0 and 100% intensity, mapped to a big-endian + /// 16-bit integer. + blue: u16, + /// Transparency channel between 0 and 100% intensity, mapped to a + /// big-endian 16-bit integer. + alpha: u16, +} + +impl Color for Rgba16 { + const SIZE: usize = 8; + + fn parse_bytes(reader: &mut impl Read) -> io::Result { + Ok(Self { + red: reader.read_u16::()?, + green: reader.read_u16::()?, + blue: reader.read_u16::()?, + alpha: reader.read_u16::()?, + }) + } + + fn from_rgba16_lossy(red: u16, green: u16, blue: u16, alpha: u16) -> Self { + Self { + red, + green, + blue, + alpha, + } + } + + fn red_u16(&self) -> u16 { + self.red + } + + fn green_u16(&self) -> u16 { + self.green + } + + fn blue_u16(&self) -> u16 { + self.blue + } + + fn alpha_u16(&self) -> u16 { + self.alpha + } +} -- cgit v1.2.3