///|
/// Copy the 4 RGBA bytes at `so` in `src` to `do` in `dst`.
fn copy_px(src : Image, so : Int, dst : Image, dof : Int) -> Unit {
dst.data[dof] = src.data[so]
dst.data[dof + 1] = src.data[so + 1]
dst.data[dof + 2] = src.data[so + 2]
dst.data[dof + 3] = src.data[so + 3]
}
///|
/// Crop a rectangular region starting at `(y, x)` with size `h x w`.
///
/// - `y`, `x`: top-left corner of the region (inclusive).
/// - `h`, `w`: height and width of the region.
///
/// Raises `ImageError` if the region lies outside the image bounds.
pub fn crop(
img : Image,
y : Int,
x : Int,
h : Int,
w : Int,
) -> Image raise ImageError {
if y < 0 || x < 0 || h < 0 || w < 0 || y + h > img.h || x + w > img.w {
raise ImageError("crop: region out of bounds")
}
let out = Image::new(h, w)
for yy = 0; yy < h; yy = yy + 1 {
for xx = 0; xx < w; xx = xx + 1 {
copy_px(img, img.offset(y + yy, x + xx), out, out.offset(yy, xx))
}
}
out
}
///|
/// Alias of `crop` (this library stores contiguous buffers, so no true view).
pub fn Image::sub_image(
self : Image,
y : Int,
x : Int,
h : Int,
w : Int,
) -> Image raise ImageError {
crop(self, y, x, h, w)
}
///|
/// Mirror horizontally (left-right).
pub fn flip_horizontal(img : Image) -> Image {
let out = Image::new(img.h, img.w)
for y = 0; y < img.h; y = y + 1 {
for x = 0; x < img.w; x = x + 1 {
copy_px(img, img.offset(y, x), out, out.offset(y, img.w - 1 - x))
}
}
out
}
///|
/// Mirror vertically (top-bottom).
pub fn flip_vertical(img : Image) -> Image {
let out = Image::new(img.h, img.w)
for y = 0; y < img.h; y = y + 1 {
for x = 0; x < img.w; x = x + 1 {
copy_px(img, img.offset(y, x), out, out.offset(img.h - 1 - y, x))
}
}
out
}
///|
/// Rotate 90 degrees clockwise.
pub fn rotate_90(img : Image) -> Image {
let out = Image::new(img.w, img.h)
for y = 0; y < img.h; y = y + 1 {
for x = 0; x < img.w; x = x + 1 {
copy_px(img, img.offset(y, x), out, out.offset(x, img.h - 1 - y))
}
}
out
}
///|
/// Rotate 180 degrees.
pub fn rotate_180(img : Image) -> Image {
let out = Image::new(img.h, img.w)
for y = 0; y < img.h; y = y + 1 {
for x = 0; x < img.w; x = x + 1 {
copy_px(
img,
img.offset(y, x),
out,
out.offset(img.h - 1 - y, img.w - 1 - x),
)
}
}
out
}
///|
/// Rotate 270 degrees clockwise (90 counter-clockwise).
pub fn rotate_270(img : Image) -> Image {
let out = Image::new(img.w, img.h)
for y = 0; y < img.h; y = y + 1 {
for x = 0; x < img.w; x = x + 1 {
copy_px(img, img.offset(y, x), out, out.offset(img.w - 1 - x, y))
}
}
out
}