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

View file

@ -102,7 +102,7 @@ pub mod fns {
use alloc::vec::Vec;
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},
};
@ -142,6 +142,7 @@ pub mod fns {
0xc2 "rect" => rect,
0xc3 "circle" => circle,
0xe0 "stroke" => stroke,
0xe1 "fill" => fill,
}
}
@ -509,4 +510,18 @@ pub mod fns {
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,
}
#[derive(Debug, Clone)]
pub struct Fill {
pub color: Rgba,
pub shape: Shape,
}
#[derive(Debug, Clone)]
pub enum Scribble {
Stroke(Stroke),
Fill(Fill),
}