///|
/// RGBA 图像数据结构:每像素 4 字节(R,G,B,A),行优先存储。
///
/// 内部用 `FixedArray[Byte]` 存储以便像素级原地修改(水印嵌入零复制);
/// 与 mizchi/image 的 `ImageData`(Bytes)互转见 `decode_png` / `encode_png`。
pub struct RgbaImage {
  width : Int
  height : Int
  data : FixedArray[Byte]
}

///|
/// 缩放采样方式。
pub(all) enum SampleMode {
  Nearest
  Bilinear
}

///|
/// 创建指定尺寸的纯色图像。
///
/// 参数 `rgba` 为打包颜色:`0xRRGGBBAA`。
pub fn RgbaImage::new(width : Int, height : Int, rgba : Int) -> RgbaImage {
  let bytes = FixedArray::make(width * height * 4, b'\x00')
  let img = { width: width, height: height, data: bytes }
  for i in 0..<(width * height) {
    let x = i % width
    let y = i / width
    img.set_pixel(x, y, rgba)
  }
  img
}

///|
/// 读取像素,返回打包 RGBA 颜色 `0xRRGGBBAA`。
pub fn RgbaImage::get_pixel(self : RgbaImage, x : Int, y : Int) -> Int {
  let idx = (y * self.width + x) * 4
  let d = self.data
  (d[idx].to_int() << 24) |
  (d[idx + 1].to_int() << 16) |
  (d[idx + 2].to_int() << 8) |
  d[idx + 3].to_int()
}

///|
/// 写入像素,`rgba` 为打包颜色 `0xRRGGBBAA`。
pub fn RgbaImage::set_pixel(self : RgbaImage, x : Int, y : Int, rgba : Int) -> Unit {
  let idx = (y * self.width + x) * 4
  let d = self.data
  d[idx] = ((rgba >> 24) & 0xFF).to_byte()
  d[idx + 1] = ((rgba >> 16) & 0xFF).to_byte()
  d[idx + 2] = ((rgba >> 8) & 0xFF).to_byte()
  d[idx + 3] = (rgba & 0xFF).to_byte()
}

///|
/// 从 PNG 字节解码为 `RgbaImage`。
pub fn decode_png(raw : Bytes) -> Result[RgbaImage, String] {
  try {
    let img = @image.decode_png(raw)
    Ok({
      width: img.width,
      height: img.height,
      data: img.data.to_fixedarray(),
    })
  } catch {
    _ => Err("decode_png: invalid png data")
  }
}

///|
/// 将 `RgbaImage` 编码为 PNG 字节。
pub fn RgbaImage::encode_png(self : RgbaImage) -> Result[Bytes, String] {
  try {
    let id : @image.ImageData = {
      width: self.width,
      height: self.height,
      data: Bytes::from_array(self.data),
    }
    Ok(@image.encode_png(id))
  } catch {
    _ => Err("encode_png: invalid image")
  }
}

///|
/// 从 JPEG 字节解码为 `RgbaImage`。
pub fn decode_jpeg(raw : Bytes) -> Result[RgbaImage, String] {
  try {
    let img = @image.decode_jpeg(raw)
    Ok({
      width: img.width,
      height: img.height,
      data: img.data.to_fixedarray(),
    })
  } catch {
    _ => Err("decode_jpeg: invalid jpeg data")
  }
}

///|
/// 将 `RgbaImage` 编码为 JPEG 字节(质量 0~100,用于鲁棒性测试)。
pub fn RgbaImage::encode_jpeg(self : RgbaImage, quality : Int) -> Result[Bytes, String] {
  try {
    let id : @image.ImageData = {
      width: self.width,
      height: self.height,
      data: Bytes::from_array(self.data),
    }
    Ok(@image.encode_jpeg(id, quality = quality))
  } catch {
    _ => Err("encode_jpeg: invalid image")
  }
}

///|
/// 缩放图像。非法尺寸时返回原图副本。
pub fn RgbaImage::resize(
  self : RgbaImage, width : Int, height : Int, sampler : SampleMode
) -> RgbaImage {
  let m = match sampler {
    Nearest => @image.ResizeMethod::Nearest
    Bilinear => @image.ResizeMethod::Bilinear
  }
  try {
    let id : @image.ImageData = {
      width: self.width,
      height: self.height,
      data: Bytes::from_array(self.data),
    }
    let out = @image.resize(id, width, height, m)
    {
      width: out.width,
      height: out.height,
      data: out.data.to_fixedarray(),
    }
  } catch {
    _ => { width: self.width, height: self.height, data: self.data }
  }
}

///|
/// 裁剪图像区域 `(x, y, w, h)`。越界坐标被钳制到图像范围内。
pub fn RgbaImage::crop(self : RgbaImage, x : Int, y : Int, w : Int, h : Int) -> RgbaImage {
  let cx0 = if x < 0 { 0 } else if x >= self.width { self.width - 1 } else { x }
  let cy0 = if y < 0 { 0 } else if y >= self.height { self.height - 1 } else { y }
  let cw = if cx0 + w > self.width { self.width - cx0 } else { w }
  let ch = if cy0 + h > self.height { self.height - cy0 } else { h }
  let out = FixedArray::make(cw * ch * 4, b'\x00')
  for row in 0..