diff options
Diffstat (limited to 'alligator_tvg/src/header.rs')
| -rw-r--r-- | alligator_tvg/src/header.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/alligator_tvg/src/header.rs b/alligator_tvg/src/header.rs new file mode 100644 index 0000000..b3be494 --- /dev/null +++ b/alligator_tvg/src/header.rs @@ -0,0 +1,149 @@ +use std::io::{self, Read}; + +use byteorder::{LittleEndian, ReadBytesExt}; +use num_enum::TryFromPrimitive; +use raise::yeet; + +use crate::colors::ColorEncoding; +use crate::read_varuint; +use crate::TvgError; + +const MAGIC: [u8; 2] = [0x72, 0x56]; +pub const SUPPORTED_VERSION: u8 = 1; + +/// The coordinate range defines how many bits a Unit value uses. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)] +#[repr(u8)] +pub enum CoordinateRange { + /// Each [`Unit`] takes up 16 bits. + #[default] + Default = 0, + /// Each [`Unit`] takes up 8 bits. + Reduced = 1, + /// Each [`Unit`] takes up 32 bits. + Enhanced = 2, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)] +#[repr(u8)] +pub enum Scale { + _1 = 0, + _2 = 1, + _4 = 2, + _8 = 3, + _16 = 4, + _32 = 5, + _64 = 6, + _128 = 7, + _256 = 8, + _512 = 9, + _1024 = 10, + _2048 = 11, + _4096 = 12, + _8192 = 13, + _16384 = 14, + _32768 = 15, +} + +/// Each TVG file starts with a header defining some global values for the file +/// like scale and image size. This is a representation of the header, but not +/// necessarily an exact representation of the bits of a TVG header. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TvgHeader { + /// Must always be [0x72, 0x56] + magic: [u8; 2], + /// Must be 1. For future versions, this field might decide how the rest of + /// the format looks like. + version: u8, + /// Defines the number of fraction bits in a Unit value. + scale: Scale, + /// Defines the type of color information that is used in the + /// [`ColorTable`]. + color_encoding: ColorEncoding, + /// Defines the number of total bits in a Unit value and thus the overall + /// precision of the file. + coordinate_range: CoordinateRange, + /// Encodes the maximum width of the output file in *display units*. + /// + /// A value of 0 indicates that the image has the maximum possible width. + width: u32, + /// Encodes the maximum height of the output file in *display units*. + /// + /// A value of 0 indicates that the image has the maximum possible height. + height: u32, + /// The number of colors in the color table. + color_count: u32, +} + +impl TvgHeader { + pub fn read<R: Read>(reader: &mut R) -> Result<Self, TvgError> { + // magic number is used as a first line defense against invalid data + let magic = [reader.read_u8()?, reader.read_u8()?]; + if magic != MAGIC { + yeet!(TvgError::InvalidFile); + } + + // the version of tvg being used + let version = reader.read_u8()?; + if version != SUPPORTED_VERSION { + yeet!(TvgError::UnsupportedVersion(version)) + } + + // scale, color_encoding, and coordinate_range are stored in one byte + let byte = reader.read_u8()?; + let scale = Scale::try_from_primitive(byte & 0b0000_1111).expect("invalid scale"); + let color_encoding = ColorEncoding::try_from_primitive((byte & 0b0011_0000) >> 4) + .expect("invalid color encoding"); + let coordinate_range = CoordinateRange::try_from_primitive((byte & 0b1100_0000) >> 6) + .expect("invalid coordinate range"); + + // width and height depend on the coordinate range + let width = read_unsigned_unit(coordinate_range, reader)?; + let height = read_unsigned_unit(coordinate_range, reader)?; + + let color_count = read_varuint(reader)?; + + Ok(Self { + magic, + version, + scale, + color_encoding, + coordinate_range, + width, + height, + color_count, + }) + } + + /// Defines the number of total bits in a Unit value and thus the overall + /// precision of the file. + pub fn coordinate_range(&self) -> CoordinateRange { + self.coordinate_range + } + + /// Defines the type of color information that is used in the [`ColorTable`]. + pub fn color_encoding(&self) -> ColorEncoding { + self.color_encoding + } + + /// Defines the number of fraction bits in a Unit value. + pub fn scale(&self) -> Scale { + self.scale + } + + /// The number of colors in the [`ColorTable`]. + pub fn color_count(&self) -> u32 { + self.color_count + } +} + +fn read_unsigned_unit<R: Read>( + coordinate_range: CoordinateRange, + bytes: &mut R, +) -> io::Result<u32> { + Ok(match coordinate_range { + CoordinateRange::Reduced => bytes.read_u8()? as u32, + CoordinateRange::Default => bytes.read_u16::<LittleEndian>()? as u32, + CoordinateRange::Enhanced => bytes.read_u32::<LittleEndian>()?, + }) +} |
