summaryrefslogtreecommitdiff
path: root/tvg/src/render.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvg/src/render.rs')
-rw-r--r--tvg/src/render.rs506
1 files changed, 0 insertions, 506 deletions
diff --git a/tvg/src/render.rs b/tvg/src/render.rs
deleted file mode 100644
index f8c15a1..0000000
--- a/tvg/src/render.rs
+++ /dev/null
@@ -1,506 +0,0 @@
-use crate::{
- colors::{self, blend, lerp, Color, ColorTable},
- commands::{Command, Point, Rectangle, Style, Vector},
- header::TvgHeader,
- path::{Instruction, InstructionData, Path, Sweep},
- TvgFile,
-};
-
-fn distance(p0: Point, p1: Point) -> f64 {
- (p0 - p1).magnitude().abs()
-}
-
-fn rotation_matrix(angle: f64) -> [[f64; 2]; 2] {
- let s = angle.sin();
- let c = angle.cos();
- [[c, -s], [s, c]]
-}
-
-fn apply_matrix(matrix: [[f64; 2]; 2], vector: Vector) -> Vector {
- Vector {
- x: vector.x * matrix[0][0] + vector.y * matrix[0][1],
- y: vector.x * matrix[1][0] + vector.y * matrix[1][1],
- }
-}
-
-fn apply_matrix_point(matrix: [[f64; 2]; 2], point: Point) -> Point {
- Point {
- x: point.x * matrix[0][0] + point.y * matrix[0][1],
- y: point.x * matrix[1][0] + point.y * matrix[1][1],
- }
-}
-
-fn lerp_f64(a: f64, b: f64, x: f64) -> f64 {
- a + (b - a) * x
-}
-
-fn lerp_and_reduce(vals: &[f64], f: f64) -> Vec<f64> {
- let mut result = Vec::with_capacity(vals.len() - 1);
- for i in 0..(vals.len() - 1) {
- result.push(lerp_f64(vals[i], vals[i + 1], f));
- }
- result
-}
-
-fn lerp_and_reduce_to_one(vals: &[f64], f: f64) -> f64 {
- if vals.len() == 1 {
- vals[0]
- } else {
- lerp_and_reduce_to_one(&lerp_and_reduce(vals, f), f)
- }
-}
-
-/// A version of [`Point`] that uses usizes for a PixMap
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-struct UPoint {
- x: usize,
- y: usize,
-}
-
-impl UPoint {
- fn new(x: usize, y: usize) -> UPoint {
- Self { x, y }
- }
-}
-
-struct FrameBuffer<C: Color> {
- width: usize,
- height: usize,
- pixels: Box<[C]>,
- scale_x: f64,
- scale_y: f64,
-}
-
-impl<C: Color + Clone + Default> FrameBuffer<C> {
- fn new(width: usize, height: usize, scale_x: f64, scale_y: f64) -> Self {
- let pixel_count = width * height;
- Self {
- width,
- height,
- pixels: vec![C::default(); pixel_count].into_boxed_slice(),
- scale_x,
- scale_y,
- }
- }
-
- fn point_to_upoint(&self, point: Point) -> UPoint {
- todo!()
- }
-
- fn upoint_to_point(&self, upoint: UPoint) -> Point {
- todo!()
- }
-
- /// Blends the new color with the given pixel. Returns `None` if the destination is invalid
- fn set_pixel(&mut self, x: usize, y: usize, color: C) -> Option<()> {
- if x >= self.width || y >= self.height {
- return None;
- }
-
- let offset = y * self.width + x;
- let destination_pixel = self.pixels.get_mut(offset)?;
-
- blend(destination_pixel, &color);
-
- Some(())
- }
-
- fn draw_line(
- &mut self,
- color_table: ColorTable<C>,
- style: Style,
- width_start: f64,
- width_end: f64,
- line_start: Point,
- line_end: Point,
- ) -> Option<()> {
- let mut min_x = usize::MAX;
- let mut min_y = usize::MAX;
- let mut max_x = usize::MIN;
- let mut max_y = usize::MIN;
-
- let max_width = f64::max(width_start, width_end);
-
- let points = [line_start, line_end];
- for pt in points {
- min_x = usize::min(min_x, (self.scale_x * (pt.x - max_width)).floor() as usize);
- min_y = usize::min(min_y, (self.scale_y * (pt.y - max_width)).floor() as usize);
- max_x = usize::max(max_x, (self.scale_x * (pt.x - max_width)).floor() as usize);
- max_y = usize::max(max_y, (self.scale_y * (pt.y - max_width)).floor() as usize);
- }
-
- // limit to framebuffer size
- min_x = usize::min(min_x, self.width);
- min_y = usize::min(min_y, self.height);
- max_x = usize::min(max_x, self.width);
- max_y = usize::min(max_y, self.height);
-
- for y in min_y..=max_y {
- for x in min_x..=max_x {
- let point = self.upoint_to_point(UPoint { x, y });
- let dist = todo!() as f64;
-
- if dist >= 0.0 {
- self.set_pixel(x, y, style.get_color_at(&color_table, point)?);
- }
- }
- }
-
- Some(())
- }
-}
-
-#[derive(Debug, Default)]
-struct PathMaker {
- points: Vec<Point>,
- width: Vec<f64>,
-}
-
-impl PathMaker {
- fn new(width: f64, start: Point) -> Self {
- let points = vec![start];
- let width = vec![width];
-
- Self { points, width }
- }
-
- fn render_line(&mut self, width: f64, to_point: Point) {
- self.points.push(to_point);
- self.width.push(width);
- }
-
- fn render_horizontal_line(&mut self, width: f64, x: f64) {
- self.points.push(Point {
- x,
- y: self.points.last().unwrap().y,
- });
- self.width.push(width);
- }
-
- fn render_vertical_line(&mut self, width: f64, y: f64) {
- self.points.push(Point {
- x: self.points.last().unwrap().x,
- y,
- });
- self.width.push(width);
- }
-
- fn render_cubic_bezier(
- &mut self,
- control_0: Point,
- control_1: Point,
- point: Point,
- start_width: f64,
- end_width: f64,
- ) {
- const BEZIER_POLY_COUNT: usize = 16;
-
- let previous = self.points.last().unwrap();
- let oct0x = [previous.x, control_0.x, control_1.x, point.x];
- let oct0y = [previous.y, control_0.y, control_1.y, point.y];
-
- for i in 1..BEZIER_POLY_COUNT {
- let f = i as f64 / BEZIER_POLY_COUNT as f64;
- let x = lerp_and_reduce_to_one(&oct0x, f);
- let y = lerp_and_reduce_to_one(&oct0y, f);
-
- self.points.push(Point { x, y });
- self.width.push(lerp_f64(start_width, end_width, f));
- }
-
- self.points.push(point);
- self.width.push(end_width);
- }
-
- fn render_quadratic_bezier(
- &mut self,
- control: Point,
- target: Point,
- start_width: f64,
- end_width: f64,
- ) {
- const BEZIER_POLY_COUNT: usize = 16;
-
- let previous = self.points.last().unwrap();
- let oct0x = [previous.x, control.x, target.x];
- let oct0y = [previous.y, control.y, target.y];
-
- for i in 1..BEZIER_POLY_COUNT {
- let f = i as f64 / BEZIER_POLY_COUNT as f64;
- let x = lerp_and_reduce_to_one(&oct0x, f);
- let y = lerp_and_reduce_to_one(&oct0y, f);
-
- self.points.push(Point { x, y });
- self.width.push(lerp_f64(start_width, end_width, f));
- }
-
- self.points.push(target);
- self.width.push(end_width);
- }
-
- fn render_circle(
- &mut self,
- p0: Point,
- p1: Point,
- mut radius: f64,
- large_arc: bool,
- turn_left: bool,
- end_width: f64,
- ) {
- const CIRCLE_POLY_COUNT: usize = 100;
-
- let start_width = *self.width.last().unwrap();
- let left_side = turn_left == large_arc;
- let delta = (p1 - p0).scale(0.5);
- let midpoint = p0 + delta;
-
- let radius_vec = if left_side {
- Vector {
- x: -delta.y,
- y: delta.x,
- }
- } else {
- Vector {
- x: delta.y,
- y: -delta.x,
- }
- };
-
- let len_squared = radius_vec.x * radius_vec.x + radius_vec.y * radius_vec.y;
- if (len_squared - 0.03 > radius * radius) || radius < 0.0 {
- radius = len_squared.sqrt();
- }
-
- let to_center = radius_vec.scale(f64::max(0.0, radius * radius / len_squared - 1.0).sqrt());
- let center = midpoint + to_center;
- let angle = len_squared.sqrt().clamp(-1.0, 1.0).asin() * 2.0;
- let arc = if large_arc {
- std::f64::consts::TAU - angle
- } else {
- angle
- };
-
- let position = p0 - center;
- for i in 0..CIRCLE_POLY_COUNT {
- let arc = if turn_left { -arc } else { arc };
- let step_matrix = rotation_matrix(i as f64 * arc / CIRCLE_POLY_COUNT as f64);
- let point = center + apply_matrix(step_matrix, position);
- self.points.push(point);
- self.width.push(lerp_f64(
- start_width,
- end_width,
- i as f64 / CIRCLE_POLY_COUNT as f64,
- ));
- }
-
- self.points.push(p1);
- }
-
- fn render_ellipse(
- &mut self,
- p0: Point,
- p1: Point,
- radius_x: f64,
- radius_y: f64,
- rotation: f64,
- large_arc: bool,
- turn_left: bool,
- end_width: f64,
- ) {
- let radius_min = distance(p0, p1) / 2.0;
- let radius_max = (radius_x * radius_x + radius_y * radius_y).sqrt();
- let upscale = if radius_max < radius_min {
- radius_min / radius_max
- } else {
- 1.0
- };
-
- let ratio = radius_x / radius_y;
- let rotation = rotation_matrix(-rotation.to_radians());
- let transform = [
- [rotation[0][0] / upscale, rotation[0][1] / upscale],
- [
- rotation[1][0] / upscale * ratio,
- rotation[1][1] / upscale * ratio,
- ],
- ];
- let transform_back = [
- [rotation[1][1] * upscale, -rotation[0][1] / ratio * upscale],
- [-rotation[1][0] * upscale, rotation[0][0] / ratio * upscale],
- ];
-
- let mut tmp = PathMaker::default();
- tmp.render_circle(
- apply_matrix_point(transform, p0),
- apply_matrix_point(transform, p1),
- radius_x * upscale,
- large_arc,
- turn_left,
- end_width,
- );
-
- for i in 0..tmp.points.len() {
- let point = tmp.points[i];
- self.points.push(apply_matrix_point(transform_back, point));
- self.width.push(tmp.width[i]);
- }
- }
-
- fn close(&mut self, width: f64) {
- self.points.push(self.points[0]);
- self.width.push(width)
- }
-}
-
-fn render_path(path: Path, width: f64) {
- for segment in path.segments.iter() {
- let mut path_maker = PathMaker::new(width, segment.start);
- for instruction in segment.instructions.iter() {
- let line_width = instruction
- .line_width
- .unwrap_or(*path_maker.width.last().unwrap());
- let data = instruction.data;
- match data {
- InstructionData::Line { position } => path_maker.render_line(line_width, position),
- InstructionData::HorizontalLine { x } => {
- path_maker.render_horizontal_line(line_width, x)
- }
- InstructionData::VerticalLine { y } => {
- path_maker.render_vertical_line(line_width, y)
- }
- InstructionData::CubicBezier {
- control_0,
- control_1,
- point_1,
- } => path_maker.render_cubic_bezier(
- control_0,
- control_1,
- point_1,
- *path_maker.width.last().unwrap(),
- line_width,
- ),
- InstructionData::ArcCircle {
- large_arc,
- sweep,
- radius,
- target,
- } => path_maker.render_circle(
- *path_maker.points.last().unwrap(),
- target,
- radius,
- large_arc,
- sweep == Sweep::Left,
- line_width,
- ),
- InstructionData::ArcEllipse {
- large_arc,
- sweep,
- radius_x,
- radius_y,
- rotation,
- target,
- } => path_maker.render_ellipse(
- *path_maker.points.last().unwrap(),
- target,
- radius_x,
- radius_y,
- rotation,
- large_arc,
- sweep == Sweep::Left,
- line_width,
- ),
- InstructionData::ClosePath => path_maker.close(line_width),
- InstructionData::QuadraticBezier { control, target } => path_maker
- .render_quadratic_bezier(
- control,
- target,
- *path_maker.width.last().unwrap(),
- line_width,
- ),
- }
- }
- }
-}
-
-pub enum AntiAliasing {
- X1 = 1,
- X4 = 2,
- X9 = 3,
- X16 = 4,
- X25 = 6,
- X49 = 7,
- X64 = 8,
-}
-
-pub fn render<C: Color + Clone + Default>(
- file: &TvgFile<C>,
- width: u32,
- height: u32,
- scale_x: f32,
- scale_y: f32,
- anti_alias: AntiAliasing,
-) {
- let mut framebuffer: FrameBuffer<C> = FrameBuffer::new(
- width as usize,
- height as usize,
- scale_x.into(),
- scale_y.into(),
- );
- let header = &file.header;
- let color_table = &file.color_table;
-
- for command in file.commands.iter() {
- match command {
- Command::EndOfDocument => break,
- Command::FillPolygon {
- fill_style,
- polygon,
- } => {
- todo!()
- }
- Command::FillRectangles {
- fill_style,
- rectangles,
- } => todo!(),
- Command::FillPath { fill_style, path } => todo!(),
- Command::DrawLines {
- line_style,
- line_width,
- lines,
- } => todo!(),
- Command::DrawLineLoop {
- line_style,
- line_width,
- points,
- } => todo!(),
- Command::DrawLineStrip {
- line_style,
- line_width,
- points,
- } => todo!(),
- Command::DrawLinePath {
- line_style,
- line_width,
- path,
- } => todo!(),
- Command::OutlineFillPolygon {
- fill_style,
- line_style,
- line_width,
- points,
- } => todo!(),
- Command::OutlineFillRectangles {
- fill_style,
- line_style,
- line_width,
- rectangles,
- } => todo!(),
- Command::OutlineFillPath {
- fill_style,
- line_style,
- line_width,
- path,
- } => todo!(),
- }
- }
-}