///|
/// An in-memory RGBA raster for thumbnail previews and regression fixtures.
pub(all) struct Raster {
  width : Int
  height : Int
  pixels : Array[Color]
} derive(Eq, Debug, ToJson)

///|
pub fn Raster::new(
  width : Int,
  height : Int,
  fill? : Color = Color::rgb8(255, 255, 255),
) -> Raster {
  let pixels = []
  if width > 0 && height > 0 {
    for _ in 0..<(width * height) {
      pixels.push(fill)
    }
  }
  { width, height, pixels }
}

///|
pub fn Raster::in_bounds(self : Raster, x : Int, y : Int) -> Bool {
  x >= 0 && y >= 0 && x < self.width && y < self.height
}

///|
pub fn Raster::index(self : Raster, x : Int, y : Int) -> Int {
  y * self.width + x
}

///|
pub fn Raster::get(self : Raster, x : Int, y : Int) -> Color? {
  if self.in_bounds(x, y) {
    Some(self.pixels[self.index(x, y)])
  } else {
    None
  }
}

///|
pub fn Raster::set(self : Raster, x : Int, y : Int, color : Color) -> Bool {
  if self.in_bounds(x, y) {
    self.pixels[self.index(x, y)] = color
    true
  } else {
    false
  }
}

///|
pub fn Raster::fill_rect(self : Raster, rect : Rect, color : Color) -> Unit {
  let start_x = if rect.x.to_int() < 0 { 0 } else { rect.x.to_int() }
  let start_y = if rect.y.to_int() < 0 { 0 } else { rect.y.to_int() }
  let end_x = if rect.right().to_int() > self.width {
    self.width
  } else {
    rect.right().to_int()
  }
  let end_y = if rect.bottom().to_int() > self.height {
    self.height
  } else {
    rect.bottom().to_int()
  }
  for y in start_y.. Bool {
  match self.get(x, y) {
    Some(existing) => self.set(x, y, existing.over(color))
    None => false
  }
}

///|
/// Draws a filled polygon by scanning its bounding box.
pub fn Raster::fill_polygon(
  self : Raster,
  polygon : Array[Vec2],
  color : Color,
) -> Unit {
  let box_ = bounds_of_points(polygon)
  let min_x = if box_.x.to_int() < 0 { 0 } else { box_.x.to_int() }
  let min_y = if box_.y.to_int() < 0 { 0 } else { box_.y.to_int() }
  let max_x = if box_.right().to_int() > self.width {
    self.width
  } else {
    box_.right().to_int()
  }
  let max_y = if box_.bottom().to_int() > self.height {
    self.height
  } else {
    box_.bottom().to_int()
  }
  for y in min_y.. Raster {
  if factor <= 1 {
    self
  } else {
    let width = self.width / factor
    let height = self.height / factor
    let result = Raster::new(width, height)
    for y in 0.. {
                total_r += color.r
                total_g += color.g
                total_b += color.b
                total_a += color.a
                count += 1
              }
              None => ()
            }
          }
        }
        if count > 0 {
          let _ = result.set(
            x,
            y,
            Color::rgba(
              total_r / count.to_double(),
              total_g / count.to_double(),
              total_b / count.to_double(),
              a=total_a / count.to_double(),
            ),
          )
        }
      }
    }
    result
  }
}

///|
pub fn Raster::histogram(self : Raster, bins : Int) -> Array[Int] {
  let result = []
  if bins > 0 {
    for _ in 0..