///|
/// Distance transform: for every foreground pixel (luma > threshold), the
/// approximate Euclidean distance to the nearest background pixel. Uses the
/// two-pass chamfer algorithm with (3, 4) weights — 3 for orthogonal steps,
/// 4 for diagonal — which approximates true distance scaled by 3 in integer
/// arithmetic. The result is normalized to a 0..255 grayscale image where
/// brighter means deeper inside the shape.

///|
/// Computes the raw chamfer (3,4) distance map (in thirds of a pixel) from
/// each foreground pixel to the nearest background. Background pixels are 0.
fn Image::chamfer_map(self : Image, threshold : Int) -> Array[Int] {
  let w = self.width
  let h = self.height
  let n = w * h
  // A large sentinel that any real path stays below.
  let inf = 1 << 28
  let dist : Array[Int] = Array::make(n, 0)
  for p in 0.. threshold { inf } else { 0 }
  }
  // Forward pass: top-left to bottom-right, looking at already-processed
  // neighbors (W, NW, N, NE).
  for y in 0.. 0 && dist[p - 1] + 3 < best {
        best = dist[p - 1] + 3
      }
      if x > 0 && y > 0 && dist[p - w - 1] + 4 < best {
        best = dist[p - w - 1] + 4
      }
      if y > 0 && dist[p - w] + 3 < best {
        best = dist[p - w] + 3
      }
      if x + 1 < w && y > 0 && dist[p - w + 1] + 4 < best {
        best = dist[p - w + 1] + 4
      }
      dist[p] = best
    }
  }
  // Backward pass: bottom-right to top-left, looking at the other four
  // neighbors (E, SE, S, SW).
  for y = h - 1; y >= 0; y = y - 1 {
    for x = w - 1; x >= 0; x = x - 1 {
      let p = y * w + x
      if dist[p] == 0 {
        continue
      }
      let mut best = dist[p]
      if x + 1 < w && dist[p + 1] + 3 < best {
        best = dist[p + 1] + 3
      }
      if x + 1 < w && y + 1 < h && dist[p + w + 1] + 4 < best {
        best = dist[p + w + 1] + 4
      }
      if y + 1 < h && dist[p + w] + 3 < best {
        best = dist[p + w] + 3
      }
      if x > 0 && y + 1 < h && dist[p + w - 1] + 4 < best {
        best = dist[p + w - 1] + 4
      }
      dist[p] = best
    }
  }
  dist
}

///|
/// Distance transform normalized to a grayscale image: background stays
/// black, and foreground brightness grows with distance from the edge,
/// scaled so the deepest pixel maps to 255. Alpha is preserved.
pub fn Image::distance_transform(self : Image, threshold : Int) -> Image {
  let w = self.width
  let h = self.height
  let dist = self.chamfer_map(threshold)
  let mut max = 0
  for d in dist {
    if d < 1 << 28 && d > max {
      max = d
    }
  }
  let out = Image::new(w, h)
  for p in 0..<(w * h) {
    let base = p * 4
    let v = if max == 0 { 0 } else { dist[p] * 255 / max }
    let g = clamp_byte(v)
    out.data[base] = g
    out.data[base + 1] = g
    out.data[base + 2] = g
    out.data[base + 3] = self.data[base + 3]
  }
  out
}