summaryrefslogtreecommitdiff
path: root/alligator_tvg/src/commands.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alligator_tvg/src/commands.rs')
-rw-r--r--alligator_tvg/src/commands.rs613
1 files changed, 0 insertions, 613 deletions
diff --git a/alligator_tvg/src/commands.rs b/alligator_tvg/src/commands.rs
deleted file mode 100644
index f316a53..0000000
--- a/alligator_tvg/src/commands.rs
+++ /dev/null
@@ -1,613 +0,0 @@
-use std::io::{self, Read};
-
-use byteorder::ReadBytesExt;
-use raise::yeet;
-
-use crate::{header::TvgHeader, path::Path, read_unit, read_varuint, Decode, Point, TvgError};
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Rectangle {
- /// Horizontal distance of the left side to the origin.
- x: f64,
- /// Vertical distance of the upper side to the origin.
- y: f64,
- /// Horizontal extent of the rectangle.
- width: f64,
- /// Vertical extent of the rectangle.
- height: f64,
-}
-
-impl Decode for Rectangle {
- fn read(reader: &mut impl Read, header: &TvgHeader) -> io::Result<Self> {
- Ok(Self {
- x: read_unit(reader, header)?,
- y: read_unit(reader, header)?,
- width: read_unit(reader, header)?,
- height: read_unit(reader, header)?,
- })
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct Line {
- /// Start point of the line.
- start: Point,
- /// End point of the line.
- end: Point,
-}
-
-impl Decode for Line {
- fn read(reader: &mut impl Read, header: &TvgHeader) -> io::Result<Self> {
- Ok(Self {
- start: Point::read(reader, header)?,
- end: Point::read(reader, header)?,
- })
- }
-}
-
-/// A command that can be encoded.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u8)]
-enum CommandName {
- /// Determines end of file.
- EndOfDocument = 0,
- /// Fills an N-gon.
- FillPolygon = 1,
- /// Fills a set of [`Rectangle`]s.
- FillRectangles = 2,
- /// Fills a free-form [`Path`].
- FillPath = 3,
- /// Draws a set of lines.
- DrawLines = 4,
- /// Draws the outline of a polygon.
- DrawLineLoop = 5,
- /// Draws a list of end-to-end lines.
- DrawLineStrip = 6,
- /// Draws a free-form [`Path`].
- DrawLinePath = 7,
- /// Draws a filled polygon with an outline.
- OutlineFillPolygon = 8,
- /// Draws several filled [`Rectangle`]s with an outline.
- OutlineFillRectangles = 9,
- /// This command combines the [`FillPath`] and [`DrawLinePath`] command
- /// into one.
- OutlineFillPath = 10,
-}
-
-impl TryFrom<u8> for CommandName {
- type Error = TvgError;
-
- fn try_from(value: u8) -> Result<Self, Self::Error> {
- match value {
- 0 => Ok(Self::EndOfDocument),
- 1 => Ok(Self::FillPolygon),
- 2 => Ok(Self::FillRectangles),
- 3 => Ok(Self::FillPath),
- 4 => Ok(Self::DrawLines),
- 5 => Ok(Self::DrawLineLoop),
- 6 => Ok(Self::DrawLineStrip),
- 7 => Ok(Self::DrawLinePath),
- 8 => Ok(Self::OutlineFillPolygon),
- 9 => Ok(Self::OutlineFillRectangles),
- 10 => Ok(Self::OutlineFillPath),
- v => Err(TvgError::InvalidCommand(v)),
- }
- }
-}
-
-/// The type of style the command uses as a primary style.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u8)]
-pub enum StyleKind {
- /// The shape is uniformly colored with a single color.
- FlatColored = 0,
- /// The shape is colored with a linear gradient.
- LinearGradient = 1,
- /// The shape is colored with a radial gradient.
- RadialGradient = 2,
-}
-
-impl TryFrom<u8> for StyleKind {
- type Error = TvgError;
-
- fn try_from(value: u8) -> Result<Self, Self::Error> {
- match value {
- 0 => Ok(Self::FlatColored),
- 1 => Ok(Self::LinearGradient),
- 2 => Ok(Self::RadialGradient),
- _ => Err(TvgError::InvalidStyleKind),
- }
- }
-}
-
-/// The style kind, along with the colors and points used to render the style.
-#[derive(Debug, Clone, Copy)]
-pub enum Style {
- /// The shape is uniformly colored with the color at `color_index` in the
- /// [`ColorTable`].
- FlatColored {
- /// The index in the [`ColorTable`].
- color_index: u32,
- },
- /// The gradient is formed by a mental line between `point_0` and
- /// `point_1`.
- ///
- /// The color at `point_0` is the color at `color_index_0` in the color
- /// table. The color at `point_1` is the color at `color_index_1` in the
- /// [`ColorTable`]. On the line, the color is interpolated between the two
- /// points. Each point that is not on the line is orthogonally projected to
- /// the line and the color at that point is sampled. Points that are not
- /// projectable onto the line have either the color at `point_0` if they
- /// are closed to `point_0` or vice versa for `point_1`.
- LinearGradient {
- /// The start point of the gradient.
- point_0: Point,
- /// The end point of the gradient.
- point_1: Point,
- /// The color at [`point_0`].
- color_index_0: u32,
- /// The color at [`point_1`].
- color_index_1: u32,
- },
- /// The gradient is formed by a mental circle with the center at `point_0`
- /// and `point_1` being somewhere on the circle outline. Thus, the radius
- /// of said circle is the distance between `point_0` and `point_1`.
- ///
- /// The color at `point_0` is the color at `color_index_0` in the color
- /// table. The color on the circle outline is the color at `color_index_1`
- /// in the [`ColorTable`]. If a sampled point is inside the circle, the
- /// color is interpolated based on the distance to the center and the
- /// radius. If the point is not in the circle itself, the color at
- /// `color_index_1` is always taken.
- RadialGradient {
- /// The center point of the mental circle.
- point_0: Point,
- /// The end point of the gradient.
- point_1: Point,
- /// The color at `point_0`.
- color_index_0: u32,
- /// The color at `point_1`.
- color_index_1: u32,
- },
-}
-
-impl Style {
- fn read(reader: &mut impl Read, header: &TvgHeader, kind: StyleKind) -> io::Result<Self> {
- match kind {
- StyleKind::FlatColored => Self::read_flat_colored(reader),
- StyleKind::LinearGradient => Self::read_linear_gradient(reader, header),
- StyleKind::RadialGradient => Self::read_radial_gradient(reader, header),
- }
- }
-
- fn read_flat_colored(reader: &mut impl Read) -> io::Result<Self> {
- Ok(Self::FlatColored {
- color_index: read_varuint(reader)?,
- })
- }
-
- fn read_linear_gradient(reader: &mut impl Read, header: &TvgHeader) -> io::Result<Self> {
- Ok(Self::LinearGradient {
- point_0: Point::read(reader, header)?,
- point_1: Point::read(reader, header)?,
- color_index_0: read_varuint(reader)?,
- color_index_1: read_varuint(reader)?,
- })
- }
-
- fn read_radial_gradient(reader: &mut impl Read, header: &TvgHeader) -> io::Result<Self> {
- Ok(Self::RadialGradient {
- point_0: Point::read(reader, header)?,
- point_1: Point::read(reader, header)?,
- color_index_0: read_varuint(reader)?,
- color_index_1: read_varuint(reader)?,
- })
- }
-}
-
-/// TinyVG files contain a sequence of draw commands that must be executed in
-/// the defined order to get the final result. Each draw command adds a new 2D
-/// primitive to the graphic.
-#[derive(Debug, Clone)]
-pub enum Command {
- /// If this command is read, the TinyVG file has ended.
- ///
- /// This command must have prim_style_kind to be set to 0, so the last byte
- /// of every TinyVG file is `0x00`. Every byte after this command is
- /// considered not part of the TinyVG data and can be used for other
- /// purposes like metadata or similar.
- EndOfDocument,
- /// Fills a polygon with N [`Point`]s.
- ///
- /// The number of points must be at least 3. Files that encode a lower
- /// value must be discarded as ”invalid” by a conforming implementation.
- ///
- /// The polygon specified in polygon must be drawn using the even-odd rule.
- /// That means that if for any point to be inside the polygon, a line to
- /// infinity must cross an odd number of polygon segments.
- FillPolygon {
- /// The style that is used to fill the polygon.
- fill_style: Style,
- /// The points of the polygon.
- polygon: Box<[Point]>,
- },
- /// Fills a list of [`Rectangle`]s.
- ///
- /// The rectangles must be drawn first to last, which is the order they
- /// appear in the file.
- FillRectangles {
- /// The style that is used to fill all rectangles.
- fill_style: Style,
- /// The list of rectangles to be filled.
- rectangles: Box<[Rectangle]>,
- },
- /// Fills a [`Path`].
- ///
- /// For the filling, all path segments are considered a polygon each (drawn
- /// with even-odd rule) that, when overlap, also perform the even odd rule.
- /// This allows the user to carve out parts of the path and create
- /// arbitrarily shaped surfaces.
- FillPath {
- /// The style that is used to fill the path.
- fill_style: Style,
- /// A [`Path`] with `segment_count` segments.
- path: Path,
- },
- /// Draws a set of [`Line`]s.
- ///
- /// Each line is `line_width` units wide, and at least a single display
- /// pixel. This means that line_width of 0 is still visible, even though
- /// only marginally. This allows very thin outlines.
- DrawLines {
- /// The style that is used to draw the all lines.
- line_style: Style,
- /// The width of the lines.
- line_width: f64,
- /// The list of lines.
- lines: Box<[Line]>,
- },
- /// Draws a polygon.
- ///
- /// Each line is `line_width` units wide. The lines are drawn between
- /// consecutive points as well as the first and the last point.
- DrawLineLoop {
- /// The style that is used to draw the all lines.
- line_style: Style,
- /// The width of the line.
- line_width: f64,
- /// The points of the polygon.
- points: Box<[Point]>,
- },
- /// Draws a list of consecutive lines.
- ///
- /// The lines are drawn between consecutive points, but contrary to
- /// [`DrawLineLoop`], the first and the last point are not connected.
- DrawLineStrip {
- /// The style that is used to draw the all rectangles.
- line_style: Style,
- /// The width of the line.
- line_width: f64,
- /// The points of the line strip.
- points: Box<[Point]>,
- },
- /// Draws a [`Path`].
- ///
- /// The outline of the path is `line_width` units wide.
- DrawLinePath {
- /// The style that is used to draw the path.
- line_style: Style,
- /// The width of the line.
- line_width: f64,
- /// A path with `segment_count` segments.
- path: Path,
- },
- /// Fills a polygon and draws an outline at the same time.
- ///
- /// This command is a combination of [`FillPolygon`] and [`DrawLineLoop`].
- /// It first performs a [`FillPolygon`] with the `fill_style`, then
- /// performs [`DrawLineLoop`] with `line_style` and `line_width`.
- ///
- /// The outline commands use a reduced number of elements. The maximum
- /// number of points is 64.
- OutlineFillPolygon {
- /// The style that is used to fill the polygon.
- fill_style: Style,
- /// The style that is used to draw the outline of the polygon.
- line_style: Style,
- /// The width of the line.
- line_width: f64,
- /// The set of points of this polygon.
- points: Box<[Point]>,
- },
- /// Fills and outlines a list of [`Rectangle`]s.
- ///
- /// For each rectangle, it is first filled, then its outline is drawn, then
- /// the next rectangle is drawn.
- ///
- /// The outline commands use a reduced number of elements, the maximum
- /// number of points is 64.
- OutlineFillRectangles {
- /// The style that is used to fill the rectangles.
- fill_style: Style,
- /// The style that is used to draw the outline of the rectangles.
- line_style: Style,
- /// The width of the line.
- line_width: f64,
- /// The list of rectangles to be drawn.
- rectangles: Box<[Rectangle]>,
- },
- /// Fills a path and draws an outline at the same time.
- ///
- /// This command is a combination of [`FillPath`] and [`DrawLinePath`]. It
- /// first performs a [`FillPath`] with the `fill_style`, then performs
- /// [`DrawLinePath`] with `line_style` and `line_width`.
- OutlineFillPath {
- /// The style that is used to fill the path.
- fill_style: Style,
- /// The style that is used to draw the outline of the path.
- line_style: Style,
- /// The width of the line.
- line_width: f64,
- /// The path that should be drawn.
- path: Path,
- },
-}
-
-/// The header is different for outline commands, so we use this as a helper
-struct OutlineHeader {
- count: u32,
- fill_style: Style,
- line_style: Style,
- line_width: f64,
-}
-
-impl OutlineHeader {
- fn read(
- reader: &mut impl Read,
- header: &TvgHeader,
- prim_style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- // the count and secondary style kind are stores in the same byte
- let byte = reader.read_u8()?;
- let count = (byte & 0b0011_1111) as u32 + 1;
- let sec_style_kind = StyleKind::try_from((byte & 0b1100_0000) >> 6)?;
-
- let fill_style = Style::read(reader, header, prim_style_kind)?;
- let line_style = Style::read(reader, header, sec_style_kind)?;
-
- let line_width = read_unit(reader, header)?;
-
- Ok(Self {
- count,
- fill_style,
- line_style,
- line_width,
- })
- }
-}
-
-impl Command {
- pub fn read(reader: &mut impl Read, header: &TvgHeader) -> Result<Self, TvgError> {
- // the command name and primary style kind are stores in the same byte
- let byte = reader.read_u8()?;
- let command = CommandName::try_from(byte & 0b0011_1111)?;
- let style_kind = StyleKind::try_from((byte & 0b1100_0000) >> 6)?;
-
- match command {
- CommandName::EndOfDocument => Self::end_of_document(style_kind),
- CommandName::FillPolygon => Self::read_fill_polygon(reader, header, style_kind),
- CommandName::FillRectangles => Self::read_fill_rectangles(reader, header, style_kind),
- CommandName::FillPath => Self::read_fill_path(reader, header, style_kind),
- CommandName::DrawLines => Self::read_draw_lines(reader, header, style_kind),
- CommandName::DrawLineLoop => Self::read_draw_line_loop(reader, header, style_kind),
- CommandName::DrawLineStrip => Self::read_draw_line_strip(reader, header, style_kind),
- CommandName::DrawLinePath => Self::read_draw_line_path(reader, header, style_kind),
- CommandName::OutlineFillPolygon => {
- Self::read_outline_fill_polygon(reader, header, style_kind)
- }
- CommandName::OutlineFillRectangles => {
- Self::read_outline_fill_rectangles(reader, header, style_kind)
- }
- CommandName::OutlineFillPath => {
- Self::read_outline_fill_path(reader, header, style_kind)
- }
- }
- }
-
- pub fn is_end_of_document(&self) -> bool {
- matches!(self, Self::EndOfDocument)
- }
-
- fn read_command_header(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> io::Result<(u32, Style)> {
- // every command adds one to the count
- let count = read_varuint(reader)? + 1;
- let style = Style::read(reader, header, style_kind)?;
-
- Ok((count, style))
- }
-
- fn end_of_document(style_kind: StyleKind) -> Result<Self, TvgError> {
- if style_kind != StyleKind::FlatColored {
- Err(TvgError::InvalidEndOfDocument(style_kind as u8))
- } else {
- Ok(Self::EndOfDocument)
- }
- }
-
- fn read_fill_polygon(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (point_count, fill_style) = Self::read_command_header(reader, header, style_kind)?;
- if point_count < 3 {
- yeet!(TvgError::InvalidPolygon(point_count));
- }
-
- let polygon = Point::read_multiple(reader, header, point_count)?;
-
- Ok(Self::FillPolygon {
- fill_style,
- polygon,
- })
- }
-
- fn read_fill_rectangles(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (rectangle_count, fill_style) = Self::read_command_header(reader, header, style_kind)?;
- let rectangles = Rectangle::read_multiple(reader, header, rectangle_count)?;
-
- Ok(Self::FillRectangles {
- fill_style,
- rectangles,
- })
- }
-
- fn read_fill_path(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (segment_count, fill_style) = Self::read_command_header(reader, header, style_kind)?;
- let path = Path::read(reader, header, segment_count)?;
-
- Ok(Self::FillPath { fill_style, path })
- }
-
- fn read_draw_lines(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (line_count, line_style) = Self::read_command_header(reader, header, style_kind)?;
- let line_width = read_unit(reader, header)?;
- let lines = Line::read_multiple(reader, header, line_count)?;
-
- Ok(Self::DrawLines {
- line_style,
- line_width,
- lines,
- })
- }
-
- fn read_draw_line_loop(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (point_count, line_style) = Self::read_command_header(reader, header, style_kind)?;
- let line_width = read_unit(reader, header)?;
- let points = Point::read_multiple(reader, header, point_count)?;
-
- Ok(Self::DrawLineLoop {
- line_style,
- line_width,
- points,
- })
- }
-
- fn read_draw_line_strip(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (point_count, line_style) = Self::read_command_header(reader, header, style_kind)?;
- let line_width = read_unit(reader, header)?;
- let points = Point::read_multiple(reader, header, point_count)?;
-
- Ok(Self::DrawLineStrip {
- line_style,
- line_width,
- points,
- })
- }
-
- fn read_draw_line_path(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let (segment_count, line_style) = Self::read_command_header(reader, header, style_kind)?;
- let line_width = read_unit(reader, header)?;
- let path = Path::read(reader, header, segment_count)?;
-
- Ok(Self::DrawLinePath {
- line_style,
- line_width,
- path,
- })
- }
-
- fn read_outline_fill_polygon(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let OutlineHeader {
- count: segment_count,
- fill_style,
- line_style,
- line_width,
- } = OutlineHeader::read(reader, header, style_kind)?;
-
- let points = Point::read_multiple(reader, header, segment_count)?;
-
- Ok(Self::OutlineFillPolygon {
- fill_style,
- line_style,
- line_width,
- points,
- })
- }
-
- fn read_outline_fill_rectangles(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let OutlineHeader {
- count: rect_count,
- fill_style,
- line_style,
- line_width,
- } = OutlineHeader::read(reader, header, style_kind)?;
-
- let rectangles = Rectangle::read_multiple(reader, header, rect_count)?;
-
- Ok(Self::OutlineFillRectangles {
- fill_style,
- line_style,
- line_width,
- rectangles,
- })
- }
-
- fn read_outline_fill_path(
- reader: &mut impl Read,
- header: &TvgHeader,
- style_kind: StyleKind,
- ) -> Result<Self, TvgError> {
- let OutlineHeader {
- count: segment_count,
- fill_style,
- line_style,
- line_width,
- } = OutlineHeader::read(reader, header, style_kind)?;
-
- let path = Path::read(reader, header, segment_count)?;
-
- Ok(Self::OutlineFillPath {
- fill_style,
- line_style,
- line_width,
- path,
- })
- }
-}