///|
/// Drawing primitives: Bresenham lines, rectangles and midpoint circles
/// (outline + filled). These mutate the image in place — like `set_pixel` —
/// so multiple primitives can be layered cheaply; every write is clipped
/// against the image bounds.
///|
/// Writes one RGBA pixel, silently skipping out-of-bounds coordinates so
/// callers never need to pre-clip.
fn Image::plot(
self : Image,
x : Int,
y : Int,
r : Byte,
g : Byte,
b : Byte,
a : Byte,
) -> Unit {
if x < 0 || x >= self.width || y < 0 || y >= self.height {
return
}
self.set_pixel(x, y, r, g, b, a)
}
///|
/// Draws a straight line from `(x0, y0)` to `(x1, y1)` inclusive with the
/// classic integer-only Bresenham algorithm.
pub fn Image::draw_line(
self : Image,
x0 : Int,
y0 : Int,
x1 : Int,
y1 : Int,
r : Byte,
g : Byte,
b : Byte,
a : Byte,
) -> Unit {
let dx = iabs(x1 - x0)
let dy = -iabs(y1 - y0)
let sx = if x0 < x1 { 1 } else { -1 }
let sy = if y0 < y1 { 1 } else { -1 }
let mut err = dx + dy
let mut x = x0
let mut y = y0
while true {
self.plot(x, y, r, g, b, a)
if x == x1 && y == y1 {
break
}
let e2 = err * 2
if e2 >= dy {
err = err + dy
x = x + sx
}
if e2 <= dx {
err = err + dx
y = y + sy
}
}
}
///|
/// Draws the 1-pixel outline of the rectangle with top-left corner
/// `(x, y)` and the given size. Degenerate sizes (< 1) draw nothing.
pub fn Image::draw_rect(
self : Image,
x : Int,
y : Int,
w : Int,
h : Int,
r : Byte,
g : Byte,
b : Byte,
a : Byte,
) -> Unit {
if w < 1 || h < 1 {
return
}
for i in 0.. Unit {
for j in 0.. Unit {
if radius < 0 {
return
}
if radius == 0 {
self.plot(cx, cy, r, g, b, a)
return
}
let mut x = radius
let mut y = 0
let mut err = 1 - radius
while x >= y {
self.plot(cx + x, cy + y, r, g, b, a)
self.plot(cx + y, cy + x, r, g, b, a)
self.plot(cx - y, cy + x, r, g, b, a)
self.plot(cx - x, cy + y, r, g, b, a)
self.plot(cx - x, cy - y, r, g, b, a)
self.plot(cx - y, cy - x, r, g, b, a)
self.plot(cx + y, cy - x, r, g, b, a)
self.plot(cx + x, cy - y, r, g, b, a)
y = y + 1
if err < 0 {
err = err + 2 * y + 1
} else {
x = x - 1
err = err + 2 * (y - x) + 1
}
}
}
///|
/// Fills the disk `dx^2 + dy^2 <= radius^2` centered at `(cx, cy)`.
pub fn Image::fill_circle(
self : Image,
cx : Int,
cy : Int,
radius : Int,
r : Byte,
g : Byte,
b : Byte,
a : Byte,
) -> Unit {
if radius < 0 {
return
}
let rr = radius * radius
for dy in -radius..<(radius + 1) {
for dx in -radius..<(radius + 1) {
if dx * dx + dy * dy <= rr {
self.plot(cx + dx, cy + dy, r, g, b, a)
}
}
}
}