fill scribble

This commit is contained in:
liquidex 2024-08-21 22:08:41 +02:00
parent 7933057062
commit ccab723298
3 changed files with 75 additions and 80 deletions

View file

@ -1,11 +1,11 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use tiny_skia::{ use tiny_skia::{
BlendMode, Color, LineCap, Paint, PathBuilder, Pixmap, Rect, Shader, Stroke as SStroke, BlendMode, Color, FillRule, LineCap, Paint, Path, PathBuilder, Pixmap, Rect, Shader,
Transform, Stroke as SStroke, Transform,
}; };
use crate::{ use crate::{
value::{Ref, Rgba, Scribble, Shape, Stroke, Value}, value::{Fill, Ref, Rgba, Scribble, Shape, Stroke, Value},
vm::{Exception, Vm}, vm::{Exception, Vm},
}; };
@ -81,6 +81,7 @@ impl<'a> Renderer<'a> {
} }
Ref::Scribble(scribble) => match scribble { Ref::Scribble(scribble) => match scribble {
Scribble::Stroke(stroke) => self.render_stroke(vm, value, stroke)?, Scribble::Stroke(stroke) => self.render_stroke(vm, value, stroke)?,
Scribble::Fill(fill) => self.render_fill(vm, value, fill)?,
}, },
_ => return Err(Self::create_exception(vm, value, NOT_A_SCRIBBLE))?, _ => return Err(Self::create_exception(vm, value, NOT_A_SCRIBBLE))?,
} }
@ -88,92 +89,64 @@ impl<'a> Renderer<'a> {
Ok(()) Ok(())
} }
fn shape_to_path(shape: &Shape) -> Path {
let mut pb = PathBuilder::new();
match shape {
Shape::Point(vec) => {
pb.move_to(vec.x, vec.y);
pb.line_to(vec.x, vec.y);
}
Shape::Line(start, end) => {
pb.move_to(start.x, start.y);
pb.line_to(end.x, end.y);
}
Shape::Rect(position, size) => {
if let Some(rect) =
tiny_skia::Rect::from_xywh(position.x, position.y, size.x, size.y)
{
pb.push_rect(rect);
}
}
Shape::Circle(position, radius) => {
pb.push_circle(position.x, position.y, *radius);
}
}
pb.finish().unwrap()
}
fn render_stroke(&mut self, _vm: &Vm, _value: Value, stroke: &Stroke) -> Result<(), Exception> { fn render_stroke(&mut self, _vm: &Vm, _value: Value, stroke: &Stroke) -> Result<(), Exception> {
let paint = Paint { let paint = Paint {
shader: Shader::SolidColor(tiny_skia_color(stroke.color)), shader: Shader::SolidColor(tiny_skia_color(stroke.color)),
..default_paint() ..default_paint()
}; };
let transform = self.transform(); let transform = self.transform();
let path = Self::shape_to_path(&stroke.shape);
match stroke.shape { self.pixmap_mut().stroke_path(
Shape::Point(vec) => { &path,
let mut pb = PathBuilder::new(); &paint,
pb.move_to(vec.x, vec.y); &SStroke {
pb.line_to(vec.x, vec.y); width: stroke.thickness,
let path = pb.finish().unwrap(); line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
self.pixmap_mut().stroke_path( Ok(())
&path, }
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
Shape::Line(start, end) => { fn render_fill(&mut self, _vm: &Vm, _value: Value, fill: &Fill) -> Result<(), Exception> {
let mut pb = PathBuilder::new(); let paint = Paint {
pb.move_to(start.x, start.y); shader: Shader::SolidColor(tiny_skia_color(fill.color)),
pb.line_to(end.x, end.y); ..default_paint()
let path = pb.finish().unwrap(); };
let transform = self.transform();
let path = Self::shape_to_path(&fill.shape);
self.pixmap_mut().stroke_path( self.pixmap_mut()
&path, .fill_path(&path, &paint, FillRule::EvenOdd, transform, None);
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
Shape::Rect(position, size) => {
let mut pb = PathBuilder::new();
if let Some(rect) =
tiny_skia::Rect::from_xywh(position.x, position.y, size.x, size.y)
{
pb.push_rect(rect);
}
let path = pb.finish().unwrap();
self.pixmap_mut().stroke_path(
&path,
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
Shape::Circle(position, radius) => {
let mut pb = PathBuilder::new();
pb.push_circle(position.x, position.y, radius);
let path = pb.finish().unwrap();
self.pixmap_mut().stroke_path(
&path,
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
}
Ok(()) Ok(())
} }

View file

@ -102,7 +102,7 @@ pub mod fns {
use alloc::vec::Vec; use alloc::vec::Vec;
use crate::{ use crate::{
value::{List, Ref, Rgba, Scribble, Shape, Stroke, Value, Vec2, Vec4}, value::{Fill, List, Ref, Rgba, Scribble, Shape, Stroke, Value, Vec2, Vec4},
vm::{Exception, FnArgs, Vm}, vm::{Exception, FnArgs, Vm},
}; };
@ -142,6 +142,7 @@ pub mod fns {
0xc2 "rect" => rect, 0xc2 "rect" => rect,
0xc3 "circle" => circle, 0xc3 "circle" => circle,
0xe0 "stroke" => stroke, 0xe0 "stroke" => stroke,
0xe1 "fill" => fill,
} }
} }
@ -509,4 +510,18 @@ pub mod fns {
Ok(Value::Nil) Ok(Value::Nil)
} }
} }
pub fn fill(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 2 {
return Err(vm.create_exception("(fill) expects 2 arguments (fill color shape)"));
}
let color = args.get_rgba(vm, 0, "1st argument to (fill) must be a color (rgba)")?;
if let Some(shape) = to_shape(args.get(vm, 1), vm) {
let id = vm.create_ref(Ref::Scribble(Scribble::Fill(Fill { color, shape })))?;
Ok(Value::Ref(id))
} else {
Ok(Value::Nil)
}
}
} }

View file

@ -179,7 +179,14 @@ pub struct Stroke {
pub shape: Shape, pub shape: Shape,
} }
#[derive(Debug, Clone)]
pub struct Fill {
pub color: Rgba,
pub shape: Shape,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Scribble { pub enum Scribble {
Stroke(Stroke), Stroke(Stroke),
Fill(Fill),
} }