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 } }