summaryrefslogtreecommitdiff
path: root/tvg/src/colors.rs
blob: 9f4e7e5d5c3e1fb06eb4c7a595f5274409f66867 (plain)
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
	}
}