///|
/// Immutable pixel snapshot plus layout metadata.
///
/// This mirrors the lightweight role of Skia's `SkPixmap`, but owns the copied
/// bytes on the MoonBit side so it is safe across backend boundaries.
pub(all) struct Pixmap {
  image_info : ImageInfo
  row_bytes : Int
  pixels : Bytes
} derive(Debug, Eq)

///|
pub fn Pixmap::new(
  image_info : ImageInfo,
  row_bytes : Int,
  pixels : Bytes,
) -> Pixmap? {
  if image_info.is_empty() || !image_info.valid_row_bytes(row_bytes) {
    None
  } else if pixels.length() < image_info.compute_byte_size(row_bytes) {
    None
  } else {
    Some({ image_info, row_bytes, pixels })
  }
}

///|
pub fn Pixmap::width(self : Pixmap) -> Int {
  self.image_info.width()
}

///|
pub fn Pixmap::height(self : Pixmap) -> Int {
  self.image_info.height()
}

///|
pub fn Pixmap::dimensions(self : Pixmap) -> ISize {
  self.image_info.dimensions
}

///|
pub fn Pixmap::bounds(self : Pixmap) -> IRect {
  self.image_info.bounds()
}

///|
pub fn Pixmap::color_type(self : Pixmap) -> ColorType {
  self.image_info.color_type()
}

///|
pub fn Pixmap::alpha_type(self : Pixmap) -> AlphaType {
  self.image_info.alpha_type()
}

///|
pub fn Pixmap::bytes_per_pixel(self : Pixmap) -> Int {
  self.image_info.bytes_per_pixel()
}

///|
pub fn Pixmap::shift_per_pixel(self : Pixmap) -> Int {
  self.image_info.shift_per_pixel()
}

///|
pub fn Pixmap::row_bytes(self : Pixmap) -> Int {
  self.row_bytes
}

///|
pub fn Pixmap::byte_size(self : Pixmap) -> Int {
  self.image_info.compute_byte_size(self.row_bytes)
}

///|
pub fn Pixmap::byte_size64(self : Pixmap) -> Int64 {
  self.image_info.compute_byte_size64(self.row_bytes)
}

///|
pub fn Pixmap::is_empty(self : Pixmap) -> Bool {
  self.image_info.is_empty() || self.pixels.is_empty()
}

///|
pub fn Pixmap::contains(self : Pixmap, point : IPoint) -> Bool {
  self.bounds().contains_point(point)
}

///|
pub fn Pixmap::offset(self : Pixmap, point : IPoint) -> Int? {
  if self.contains(point) {
    Some(self.image_info.compute_offset(point, self.row_bytes))
  } else {
    None
  }
}

///|
/// Copy the stored channel bytes for one pixel.
pub fn Pixmap::sample(self : Pixmap, point : IPoint) -> Bytes? {
  match self.offset(point) {
    None => None
    Some(offset) => {
      let bytes_per_pixel = self.bytes_per_pixel()
      if bytes_per_pixel <= 0 || offset + bytes_per_pixel > self.pixels.length() {
        None
      } else {
        let sample : Array[Byte] = Array::new(capacity=bytes_per_pixel)
        for index in 0.. Bool {
  match self.sample(point) {
    None => false
    Some(actual) => actual == sample
  }
}