summaryrefslogtreecommitdiff
path: root/alligator_tvg/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alligator_tvg/src/lib.rs')
-rw-r--r--alligator_tvg/src/lib.rs151
1 files changed, 151 insertions, 0 deletions
diff --git a/alligator_tvg/src/lib.rs b/alligator_tvg/src/lib.rs
new file mode 100644
index 0000000..5cbe33c
--- /dev/null
+++ b/alligator_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)
+}