145 lines
3.8 KiB
Rust
145 lines
3.8 KiB
Rust
|
use core::iter;
|
||
|
|
||
|
use alloc::vec::Vec;
|
||
|
|
||
|
use crate::{
|
||
|
value::{Ref, Rgba, Scribble, Shape, Stroke, Value, Vec4},
|
||
|
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 struct RendererLimits {
|
||
|
pub bitmap_stack_capacity: usize,
|
||
|
pub transform_stack_capacity: usize,
|
||
|
}
|
||
|
|
||
|
pub struct Renderer {
|
||
|
bitmap_stack: Vec<Bitmap>,
|
||
|
transform_stack: Vec<Vec4>,
|
||
|
}
|
||
|
|
||
|
impl Renderer {
|
||
|
pub fn new(bitmap: Bitmap, limits: &RendererLimits) -> Self {
|
||
|
assert!(limits.bitmap_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 transform_stack = Vec::with_capacity(limits.transform_stack_capacity);
|
||
|
transform_stack.push(Vec4::default());
|
||
|
|
||
|
Self {
|
||
|
bitmap_stack: blend_stack,
|
||
|
transform_stack,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn create_exception(_vm: &Vm, _at: Value, message: &'static str) -> Exception {
|
||
|
Exception { message }
|
||
|
}
|
||
|
|
||
|
fn transform(&self) -> &Vec4 {
|
||
|
self.transform_stack.last().unwrap()
|
||
|
}
|
||
|
|
||
|
fn transform_mut(&mut self) -> &mut Vec4 {
|
||
|
self.transform_stack.last_mut().unwrap()
|
||
|
}
|
||
|
|
||
|
fn bitmap(&self) -> &Bitmap {
|
||
|
self.bitmap_stack.last().unwrap()
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn render(&mut self, vm: &Vm, value: Value) -> Result<(), Exception> {
|
||
|
static NOT_A_SCRIBBLE: &str = "cannot draw something that is not a scribble";
|
||
|
let (_id, scribble) = vm
|
||
|
.get_ref_value(value)
|
||
|
.ok_or_else(|| Self::create_exception(vm, value, NOT_A_SCRIBBLE))?;
|
||
|
let Ref::Scribble(scribble) = scribble else {
|
||
|
return Err(Self::create_exception(vm, value, NOT_A_SCRIBBLE));
|
||
|
};
|
||
|
|
||
|
match scribble {
|
||
|
Scribble::Stroke(stroke) => self.render_stroke(vm, value, stroke)?,
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn render_stroke(&mut self, _vm: &Vm, _value: Value, stroke: &Stroke) -> Result<(), Exception> {
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn finish(mut self) -> Bitmap {
|
||
|
self.bitmap_stack.drain(..).next().unwrap()
|
||
|
}
|
||
|
}
|