use std::io::{self, Read};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use num_enum::TryFromPrimitive;
use crate::TvgError;
/// 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<C: Color> {
colors: Box<[C]>,
}
impl<C: Color> ColorTable<C> {
/// Read in one encoding, and convert it to another. If `encoding` is
/// [`ColorEncoding::Custom`], then return
/// [`TvgError::UnsupportedColorEncoding`].
pub fn read_from_encoding(
reader: &mut impl Read,
color_count: u32,
encoding: ColorEncoding,
) -> Result<Self, TvgError> {
Ok(match encoding {
ColorEncoding::Rgba8888 => (&ColorTable::<Rgba8888>::read(reader, color_count)?).into(),
ColorEncoding::Rgb565 => (&ColorTable::<Rgb565>::read(reader, color_count)?).into(),
ColorEncoding::RgbaF32 => (&ColorTable::<RgbaF32>::read(reader, color_count)?).into(),
ColorEncoding::Custom => (&ColorTable::<Rgba16>::read(reader, color_count)?).into(),
})
}
/// Read in one encoding, and convert it into another. If `encoding` is
/// [`ColorEncoding::Custom`], then use the given encoding.
pub fn read_from_encoding_with_custom<Custom: Color>(
reader: &mut impl Read,
color_count: u32,
encoding: ColorEncoding,
) -> io::Result<Self> {
Ok(match encoding {
ColorEncoding::Rgba8888 => (&ColorTable::<Rgba8888>::read(reader, color_count)?).into(),
ColorEncoding::Rgb565 => (&ColorTable::<Rgb565>::read(reader, color_count)?).into(),
ColorEncoding::RgbaF32 => (&ColorTable::<RgbaF32>::read(reader, color_count)?).into(),
ColorEncoding::Custom => (&ColorTable::<Custom>::read(reader, color_count)?).into(),
})
}
/// Parse a color table.
fn read(reader: &mut impl Read, color_count: u32) -> io::Result<Self> {
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<Item = &C> {
self.colors.iter()
}
}
impl ColorTable<Rgba16> {}
impl<Old: Color, New: Color> From<&ColorTable<Old>> for ColorTable<New> {
fn from(value: &ColorTable<Old>) -> 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<Self>;
/// 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)]
pub 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<Self> {
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)]
pub 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<Self> {
let color = reader.read_u16::<LittleEndian>()?;
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)]
pub 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<Self> {
Ok(Self {
red: reader.read_f32::<LittleEndian>()?,
green: reader.read_f32::<LittleEndian>()?,
blue: reader.read_f32::<LittleEndian>()?,
alpha: reader.read_f32::<LittleEndian>()?,
})
}
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<Self> {
Ok(Self {
red: reader.read_u16::<BigEndian>()?,
green: reader.read_u16::<BigEndian>()?,
blue: reader.read_u16::<BigEndian>()?,
alpha: reader.read_u16::<BigEndian>()?,
})
}
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
}
}
|