///|
/// Reflect an index into `[0, n)` with edge duplication (BORDER_REFLECT).
fn reflect_index(i : Int, n : Int) -> Int {
if n == 1 {
return 0
}
let period = 2 * n
let mut m = i % period
if m < 0 {
m = m + period
}
if m >= n {
period - 1 - m
} else {
m
}
}
///|
/// Wrap an index into `[0, n)`.
fn wrap_index(i : Int, n : Int) -> Int {
let m = i % n
if m < 0 {
m + n
} else {
m
}
}
///|
/// Pad the image by adding margins around it.
///
/// - `top`, `right`, `bottom`, `left`: margin sizes in pixels.
/// - `mode`: `PadMode` controlling how the new border pixels are filled:
/// - `Constant(r, g, b, a)` — fill with the given color.
/// - `Replicate` — copy the nearest edge pixel.
/// - `Reflect` — mirror with edge duplication.
/// - `Wrap` — tile the source.
///
/// The output size is `(img.h + top + bottom, img.w + left + right)`.
pub fn pad(
img : Image,
top : Int,
right : Int,
bottom : Int,
left : Int,
mode : PadMode,
) -> Image {
let nh = img.h + top + bottom
let nw = img.w + left + right
let out = Image::new(nh, nw)
for oy = 0; oy < nh; oy = oy + 1 {
for ox = 0; ox < nw; ox = ox + 1 {
let sy = oy - top
let sx = ox - left
let oo = out.offset(oy, ox)
if sy >= 0 && sy < img.h && sx >= 0 && sx < img.w {
copy_px(img, img.offset(sy, sx), out, oo)
} else {
match mode {
Constant(cr, cg, cb, ca) => {
out.data[oo] = cr
out.data[oo + 1] = cg
out.data[oo + 2] = cb
out.data[oo + 3] = ca
}
Replicate =>
copy_px(
img,
img.offset(clampi(sy, 0, img.h - 1), clampi(sx, 0, img.w - 1)),
out,
oo,
)
Reflect =>
copy_px(
img,
img.offset(reflect_index(sy, img.h), reflect_index(sx, img.w)),
out,
oo,
)
Wrap =>
copy_px(
img,
img.offset(wrap_index(sy, img.h), wrap_index(sx, img.w)),
out,
oo,
)
}
}
}
}
out
}
///|
/// Center-pad the image to at least `h x w` using the chosen border mode.
///
/// - `h`, `w`: target minimum dimensions.
/// - `mode`: `PadMode` for the new border pixels.
///
/// Padding is split evenly (left/top-biased on odd sizes). If the image
/// already meets or exceeds a dimension, no padding is added on that axis.
pub fn pad_to_size(img : Image, h : Int, w : Int, mode : PadMode) -> Image {
let dh = clampi(h - img.h, 0, h)
let dw = clampi(w - img.w, 0, w)
let top = dh / 2
let left = dw / 2
pad(img, top, dw - left, dh - top, left, mode)
}