///|
/// Headless 2D canvas backed by an RGBA8 (non-premultiplied) framebuffer.
///
/// Pixel layout: `(y * width + x) * 4` offset, `[r, g, b, a]` bytes, each
/// channel 0..=255. Alpha is straight (non-premultiplied).
///
/// Internally stored as `FixedArray[Byte]` so `clear()` and raster writes can
/// mutate in place. `to_image_data()` converts to a `Bytes` for output.
pub struct Canvas {
width : Int
height : Int
pixels : FixedArray[Byte]
antialias : Bool
}
///|
pub fn Canvas::new(
width : Int,
height : Int,
antialias? : Bool = true,
) -> Canvas {
let len = width * height * 4
{ width, height, pixels: FixedArray::make(len, b'\x00'), antialias }
}
///|
pub fn Canvas::clear(self : Canvas) -> Unit {
for i in 0.. @image.ImageData {
let pixels = self.pixels
let bytes = Bytes::makei(pixels.length(), fn(i) { pixels[i] })
{ width: self.width, height: self.height, data: bytes }
}
///|
pub fn Canvas::to_png(self : Canvas) -> Bytes raise @image.EncodeError {
@image.encode_png(self.to_image_data())
}
///|
/// Create a drawing context bound to this canvas. The returned context starts
/// with default state (black fill/stroke, 1px line width, identity transform,
/// global alpha 1.0, alphabetic text baseline, start text alignment).
pub fn Canvas::context(self : Canvas) -> Context {
Context::new(self)
}