///|
/// Bilateral filter: edge-preserving smoothing. Each output pixel is a
/// normalized weighted average of its neighborhood where the weight is the
/// product of a spatial Gaussian (distance) and a range Gaussian (luma
/// difference) — flat regions are smoothed like a blur, while pixels
/// across a strong edge get near-zero weight and the edge stays sharp.

///|
/// Applies the bilateral filter with the given neighborhood `radius`,
/// spatial sigma (pixels) and range sigma (luma steps, 0..255 scale).
/// `radius <= 0` returns a copy; non-positive sigmas are clamped to a
/// small positive value. Alpha is preserved.
pub fn Image::bilateral(
  self : Image,
  radius : Int,
  sigma_space : Double,
  sigma_range : Double,
) -> Image {
  if radius <= 0 {
    return self.copy()
  }
  let r = if radius > 16 { 16 } else { radius }
  let ss = if sigma_space <= 0.0 { 0.1 } else { sigma_space }
  let sr = if sigma_range <= 0.0 { 0.1 } else { sigma_range }
  // Precompute the guide luma (integer BT.601 weights, like grayscale).
  let n = self.pixel_count()
  let luma : Array[Int] = Array::make(n, 0)
  for p in 0..