summaryrefslogtreecommitdiff
path: root/tvg/src/header.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvg/src/header.rs')
-rw-r--r--tvg/src/header.rs149
1 files changed, 149 insertions, 0 deletions
diff --git a/tvg/src/header.rs b/tvg/src/header.rs
new file mode 100644
index 0000000..b3be494
--- /dev/null
+++ b/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>()?,
+ })
+}