///|
/// Alpha-mask utilities for transparent GIF composition and cleanup.
pub(all) struct MaskStats {
  pixels : Int
  covered : Int
  partial : Int
  mean_alpha : Int
  bounds : GifRect?
} derive(Debug)

///|
pub fn MaskStats::pixels(self : MaskStats) -> Int {
  self.pixels
}

///|
pub fn MaskStats::covered(self : MaskStats) -> Int {
  self.covered
}

///|
pub fn MaskStats::partial(self : MaskStats) -> Int {
  self.partial
}

///|
pub fn MaskStats::mean_alpha(self : MaskStats) -> Int {
  self.mean_alpha
}

///|
pub fn MaskStats::bounds(self : MaskStats) -> GifRect? {
  self.bounds
}

///|
/// Collect coverage information without reading RGB channels.
pub fn Canvas::mask_stats(self : Canvas, threshold : Byte) -> MaskStats {
  let mut covered = 0
  let mut partial = 0
  let mut total = 0
  let mut left = self.width
  let mut top = self.height
  let mut right = -1
  let mut bottom = -1
  for y in 0..= threshold.to_int() {
        covered += 1
        if alpha < 255 {
          partial += 1
        }
        if x < left {
          left = x
        }
        if y < top {
          top = y
        }
        if x > right {
          right = x
        }
        if y > bottom {
          bottom = y
        }
      }
    }
  }
  MaskStats::{
    pixels: self.len(),
    covered,
    partial,
    mean_alpha: if self.len() == 0 {
      0
    } else {
      total / self.len()
    },
    bounds: if right < 0 {
      None
    } else {
      Some(
        GifRect::new(
          left~,
          top~,
          width=right - left + 1,
          height=bottom - top + 1,
        ),
      )
    },
  }
}

///|
/// Build a feathered alpha mask from an input canvas.
pub fn Canvas::feather_mask(self : Canvas, radius : Int) -> Canvas {
  let safe_radius = if radius < 0 { 0 } else { radius }
  let result = Canvas::new(
    width=self.width,
    height=self.height,
    fill=rgba(255, 255, 255, 0),
  )
  for y in 0.. ()
            Some(color) => {
              total += color.alpha().to_int()
              count += 1
            }
          }
        }
      }
      let average = if count == 0 { 0 } else { total / count }
      result.pixels[x + y * self.width] = rgba(255, 255, 255, average.to_byte())
    }
  }
  result
}

///|
/// Multiply a canvas alpha channel by a mask alpha channel.
pub fn Canvas::apply_mask(self : Canvas, mask : Canvas) -> Canvas {
  let result = self.copy()
  let width = if self.width < mask.width { self.width } else { mask.width }
  let height = if self.height < mask.height { self.height } else { mask.height }
  for y in 0.. Canvas {
  let result = self.copy()
  for i in 0.. Canvas {
  let result = self.copy()
  for i in 0..= threshold { 255 } else { 0 }
    result.pixels[i] = result.pixels[i].with_alpha(output)
  }
  result
}

///|
/// Composite an image over a matte color using its alpha channel.
pub fn Canvas::matte(self : Canvas, color : Rgba) -> Canvas {
  let result = Canvas::new(width=self.width, height=self.height, fill=color)
  for i in 0.. Canvas {
  let result = self.copy()
  for i in 0.. Canvas {
  let result = self.copy()
  for i in 0.. Animation {
  let result = Animation::new(width=self.width, height=self.height)
    .with_loop_count(loop_count=self.loop_count)
    .with_background(background=self.background)
  for frame in self.frames {
    ignore(result.add_frame(frame.with_canvas(frame.canvas().apply_mask(mask))))
  }
  result
}

///|
/// Return the visible fraction of a mask in per mille.
pub fn Canvas::coverage_per_mille(self : Canvas, threshold : Byte) -> Int {
  let stats = self.mask_stats(threshold)
  if stats.pixels() == 0 {
    0
  } else {
    stats.covered() * 1000 / stats.pixels()
  }
}