a whole load of work in progress

This commit is contained in:
りき萌 2024-08-10 23:13:20 +02:00
parent caec0b8ac9
commit 26ba098183
63 changed files with 3234 additions and 321 deletions

View file

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
tiny-skia = { version = "0.11.4", default-features = false, features = ["no-std-float"] }

View file

@ -6,7 +6,7 @@ use core::{
use alloc::vec::Vec;
use crate::{
bytecode::{Chunk, DefError, DefId, Defs, EmitError, Opcode, CAPTURE_CAPTURE, CAPTURE_LOCAL},
bytecode::{Chunk, DefError, Defs, EmitError, Opcode, CAPTURE_CAPTURE, CAPTURE_LOCAL},
sexp::{Ast, NodeId, NodeKind, Span},
system::System,
};

View file

@ -1,66 +1,38 @@
use core::iter;
use alloc::vec::Vec;
use tiny_skia::{
BlendMode, Color, LineCap, Paint, PathBuilder, Pixmap, Shader, Stroke as SStroke, Transform,
};
use crate::{
value::{Ref, Rgba, Scribble, Shape, Stroke, Value, Vec4},
value::{Ref, Rgba, Scribble, Shape, Stroke, Value},
vm::{Exception, Vm},
};
pub struct Bitmap {
pub width: u32,
pub height: u32,
pub pixels: Vec<Rgba>,
}
impl Bitmap {
pub fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
pixels: Vec::from_iter(
iter::repeat(Rgba::default()).take(width as usize * height as usize),
),
}
}
pub fn pixel_index(&self, x: u32, y: u32) -> usize {
x as usize + y as usize * self.width as usize
}
pub fn get(&self, x: u32, y: u32) -> Rgba {
self.pixels[self.pixel_index(x, y)]
}
pub fn set(&mut self, x: u32, y: u32, rgba: Rgba) {
let index = self.pixel_index(x, y);
self.pixels[index] = rgba;
}
}
pub use tiny_skia;
pub struct RendererLimits {
pub bitmap_stack_capacity: usize,
pub pixmap_stack_capacity: usize,
pub transform_stack_capacity: usize,
}
pub struct Renderer {
bitmap_stack: Vec<Bitmap>,
transform_stack: Vec<Vec4>,
pixmap_stack: Vec<Pixmap>,
transform_stack: Vec<Transform>,
}
impl Renderer {
pub fn new(bitmap: Bitmap, limits: &RendererLimits) -> Self {
assert!(limits.bitmap_stack_capacity > 0);
pub fn new(pixmap: Pixmap, limits: &RendererLimits) -> Self {
assert!(limits.pixmap_stack_capacity > 0);
assert!(limits.transform_stack_capacity > 0);
let mut blend_stack = Vec::with_capacity(limits.bitmap_stack_capacity);
blend_stack.push(bitmap);
let mut blend_stack = Vec::with_capacity(limits.pixmap_stack_capacity);
blend_stack.push(pixmap);
let mut transform_stack = Vec::with_capacity(limits.transform_stack_capacity);
transform_stack.push(Vec4::default());
transform_stack.push(Transform::identity());
Self {
bitmap_stack: blend_stack,
pixmap_stack: blend_stack,
transform_stack,
}
}
@ -69,44 +41,21 @@ impl Renderer {
Exception { message }
}
fn transform(&self) -> &Vec4 {
self.transform_stack.last().unwrap()
fn transform(&self) -> Transform {
self.transform_stack.last().copied().unwrap()
}
fn transform_mut(&mut self) -> &mut Vec4 {
fn transform_mut(&mut self) -> &mut Transform {
self.transform_stack.last_mut().unwrap()
}
fn bitmap(&self) -> &Bitmap {
self.bitmap_stack.last().unwrap()
pub fn translate(&mut self, x: f32, y: f32) {
let translated = self.transform().post_translate(x, y);
*self.transform_mut() = translated;
}
fn bitmap_mut(&mut self) -> &mut Bitmap {
self.bitmap_stack.last_mut().unwrap()
}
pub fn translate(&mut self, translation: Vec4) {
let transform = self.transform_mut();
transform.x += translation.x;
transform.y += translation.y;
transform.z += translation.z;
transform.w += translation.w;
}
pub fn to_bitmap_coords(&self, point: Vec4) -> Option<(u32, u32)> {
let transform = self.transform();
let x = point.x + transform.x;
let y = point.y + transform.y;
if x >= 0.0 && y >= 0.0 {
let (x, y) = (x as u32, y as u32);
if x < self.bitmap().width && y < self.bitmap().height {
Some((x, y))
} else {
None
}
} else {
None
}
fn pixmap_mut(&mut self) -> &mut Pixmap {
self.pixmap_stack.last_mut().unwrap()
}
pub fn render(&mut self, vm: &Vm, value: Value) -> Result<(), Exception> {
@ -126,19 +75,75 @@ impl Renderer {
}
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();
match stroke.shape {
Shape::Point(vec) => {
if let Some((x, y)) = self.to_bitmap_coords(vec) {
// TODO: thickness
self.bitmap_mut().set(x, y, stroke.color);
}
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,
);
}
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();
self.pixmap_mut().stroke_path(
&path,
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
}
Ok(())
}
pub fn finish(mut self) -> Bitmap {
self.bitmap_stack.drain(..).next().unwrap()
pub fn finish(mut self) -> Pixmap {
self.pixmap_stack.drain(..).next().unwrap()
}
}
fn default_paint() -> Paint<'static> {
Paint {
shader: Shader::SolidColor(Color::BLACK),
blend_mode: BlendMode::SourceOver,
anti_alias: false,
force_hq_pipeline: false,
}
}
fn tiny_skia_color(color: Rgba) -> Color {
Color::from_rgba(
color.r.clamp(0.0, 1.0),
color.g.clamp(0.0, 1.0),
color.b.clamp(0.0, 1.0),
color.a.clamp(0.0, 1.0),
)
.unwrap()
}

View file

@ -203,6 +203,7 @@ impl<'a> Parser<'a> {
}
}
#[track_caller]
pub fn current(&self) -> char {
assert_ne!(self.fuel.get(), 0, "parser is stuck");
self.fuel.set(self.fuel.get() - 1);
@ -228,7 +229,7 @@ pub fn skip_whitespace_and_comments(p: &mut Parser<'_>) {
continue;
}
';' => {
while p.current() != '\n' {
while p.current() != '\n' && p.current() != '\0' {
p.advance();
}
}

View file

@ -134,7 +134,8 @@ pub mod fns {
0x89 ".a" => rgba_a,
0xc0 "to-shape" => to_shape_f,
0xc1 "stroke" => stroke,
0xc1 "line" => line,
0xe0 "stroke" => stroke,
}
}
@ -388,14 +389,16 @@ pub mod fns {
Ok(Value::Number(rgba.r))
}
fn to_shape(value: Value, _vm: &Vm) -> Option<Shape> {
fn to_shape(value: Value, vm: &Vm) -> Option<Shape> {
match value {
Value::Nil
| Value::False
| Value::True
| Value::Number(_)
| Value::Rgba(_)
| Value::Ref(_) => None,
Value::Nil | Value::False | Value::True | Value::Number(_) | Value::Rgba(_) => None,
Value::Ref(id) => {
if let Ref::Shape(shape) = vm.get_ref(id) {
Some(shape.clone())
} else {
None
}
}
Value::Vec4(vec) => Some(Shape::Point(vec)),
}
}
@ -413,6 +416,19 @@ pub mod fns {
}
}
pub fn line(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 2 {
return Err(vm.create_exception("(line) expects 2 arguments (line start end)"));
}
static ERROR: &str = "arguments to (line) must be (vec)";
let start = args.get_vec4(vm, 0, ERROR)?;
let end = args.get_vec4(vm, 1, ERROR)?;
let id = vm.create_ref(Ref::Shape(Shape::Line(start, end)))?;
Ok(Value::Ref(id))
}
pub fn stroke(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 3 {
return Err(

View file

@ -146,6 +146,7 @@ pub struct Closure {
#[derive(Debug, Clone)]
pub enum Shape {
Point(Vec4),
Line(Vec4, Vec4),
}
#[derive(Debug, Clone)]