diff options
| author | Micha White <botahamec@outlook.com> | 2023-02-13 00:24:07 -0500 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2023-02-13 00:24:07 -0500 |
| commit | 861b467b95be55db3a42182b77dba944869bf49f (patch) | |
| tree | f58057d5ab9ad53601332981a31553b0ad05fe1b /tvg/src/lib.rs | |
| parent | c31d51487082c6cf243ecd10da71a15a78d41add (diff) | |
Rename the subdirectories
Diffstat (limited to 'tvg/src/lib.rs')
| -rw-r--r-- | tvg/src/lib.rs | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/tvg/src/lib.rs b/tvg/src/lib.rs new file mode 100644 index 0000000..5cbe33c --- /dev/null +++ b/tvg/src/lib.rs @@ -0,0 +1,151 @@ +use std::io::{self, Read}; + +use byteorder::{LittleEndian, ReadBytesExt}; +use colors::{Color, ColorTable}; +use commands::Command; +use header::{CoordinateRange, Scale, TvgHeader}; +use thiserror::Error; + +mod colors; +mod commands; +mod header; +mod path; + +pub use colors::Rgba16; +pub use header::SUPPORTED_VERSION; + +pub struct TvgFile<C: Color> { + header: TvgHeader, + color_table: ColorTable<C>, + commands: Box<[Command]>, +} + +impl<C: Color + std::fmt::Debug> TvgFile<C> { + pub fn read_from(reader: &mut impl Read) -> Result<Self, TvgError> { + let header = TvgHeader::read(reader)?; + let color_table = + ColorTable::read_from_encoding(reader, header.color_count(), header.color_encoding())?; + + let mut commands = Vec::new(); + loop { + let command = Command::read(reader, &header)?; + commands.push(command.clone()); + + if command.is_end_of_document() { + break; + } + } + + Ok(Self { + header, + color_table, + commands: commands.into_boxed_slice(), + }) + } +} + +#[derive(Debug, Error)] +pub enum TvgError { + #[error("Not a valid TVG file. The magic number was invalid.")] + InvalidFile, + #[error("Expected version 1, but found version {}", .0)] + UnsupportedVersion(u8), + #[error("Found a coordinate range with an index of 3, which is invalid")] + InvalidCoordinateRange, + #[error("Found a command with an index of {}, which is invalid", .0)] + InvalidCommand(u8), + #[error("Found a style kind with an index of 3, which is invalid")] + InvalidStyleKind, + #[error("Polygons must have at least 3 points, found {}", .0)] + InvalidPolygon(u32), + #[error("The end of the document must have a `prim_style_kind` value of 0. Found {}", .0)] + InvalidEndOfDocument(u8), + #[error("{}", .0)] + IoError(#[from] io::Error), +} + +trait Decode: Sized { + fn read(reader: &mut impl Read, header: &TvgHeader) -> io::Result<Self>; + + fn read_multiple( + reader: &mut impl Read, + header: &TvgHeader, + count: u32, + ) -> io::Result<Box<[Self]>> { + let mut vec = Vec::with_capacity(count as usize); + for _ in 0..count { + vec.push(Self::read(reader, header)?); + } + + Ok(vec.into_boxed_slice()) + } +} + +/// The unit is the common type for both positions and sizes in the vector +/// graphic. It is encoded as a signed integer with a configurable amount of +/// bits (see [`CoordinateRange`]) and fractional bits. +fn read_unit(reader: &mut impl Read, header: &TvgHeader) -> io::Result<f64> { + let value = match header.coordinate_range() { + CoordinateRange::Reduced => reader.read_i8()? as i32, + CoordinateRange::Default => reader.read_i16::<LittleEndian>()? as i32, + CoordinateRange::Enhanced => reader.read_i32::<LittleEndian>()?, + }; + + let fractional = match header.scale() { + Scale::_1 => 1.0, + Scale::_2 => 2.0, + Scale::_4 => 4.0, + Scale::_8 => 8.0, + Scale::_16 => 16.0, + Scale::_32 => 32.0, + Scale::_64 => 64.0, + Scale::_128 => 128.0, + Scale::_256 => 256.0, + Scale::_512 => 512.0, + Scale::_1024 => 1024.0, + Scale::_2048 => 2048.0, + Scale::_4096 => 4096.0, + Scale::_8192 => 8192.0, + Scale::_16384 => 16_384.0, + Scale::_32768 => 32_768.0, + }; + + Ok((value as f64) / fractional) +} + +/// A X and Y coordinate pair. +#[derive(Debug, Clone, Copy)] +pub struct Point { + /// Horizontal distance of the point to the origin. + x: f64, + /// Vertical distance of the point to the origin. + y: f64, +} + +impl Decode for Point { + fn read(reader: &mut impl Read, header: &TvgHeader) -> io::Result<Self> { + Ok(Self { + x: read_unit(reader, header)?, + y: read_unit(reader, header)?, + }) + } +} + +fn read_varuint(reader: &mut impl Read) -> io::Result<u32> { + let mut count = 0; + let mut result = 0; + + loop { + let byte = reader.read_u8()? as u32; + let value = (byte & 0x7F) << (7 * count); + result |= value; + + if (byte & 0x80) == 0 { + break; + } + + count += 1; + } + + Ok(result) +} |
