1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
// 纯 MoonBit 图像缩放(全目标可用:native/wasm/js)
// 仅依赖 @types,不依赖 @core/FFI
// 支持最近邻 + 双线性插值

///|
/// 双线性插值采样。在 (x, y) 处采样通道 c 的值。
fn resize_sample_bilinear(
  data : Array[Int],
  w : Int,
  h : Int,
  ch : Int,
  c : Int,
  x : Float,
  y : Float,
) -> Int {
  let x0 = x.to_int()
  let y0 = y.to_int()
  let w_minus_1 = w - 1
  let h_minus_1 = h - 1
  let x0c = x0.clamp(min=0, max=w_minus_1)
  let y0c = y0.clamp(min=0, max=h_minus_1)
  let x1 = (x0 + 1).clamp(min=0, max=w_minus_1)
  let y1 = (y0 + 1).clamp(min=0, max=h_minus_1)
  let fx = x - Float::from_int(x0)
  let fy = y - Float::from_int(y0)
  let p00 = data[(y0c * w + x0c) * ch + c]
  let p10 = data[(y0c * w + x1) * ch + c]
  let p01 = data[(y1 * w + x0c) * ch + c]
  let p11 = data[(y1 * w + x1) * ch + c]
  let top = Float::from_int(p00) * (1.0F - fx) + Float::from_int(p10) * fx
  let bot = Float::from_int(p01) * (1.0F - fx) + Float::from_int(p11) * fx
  (top * (1.0F - fy) + bot * fy + 0.5F).to_int()
}

///|
/// 最近邻采样。在 (x, y) 处采样通道 c 的值。
fn resize_sample_nearest(
  data : Array[Int],
  w : Int,
  h : Int,
  ch : Int,
  c : Int,
  x : Float,
  y : Float,
) -> Int {
  let xi = (x + 0.5F).to_int().clamp(min=0, max=w - 1)
  let yi = (y + 0.5F).to_int().clamp(min=0, max=h - 1)
  data[(yi * w + xi) * ch + c]
}

///|
/// resize 8-bit 图像(纯 MoonBit,全目标可用)。
/// 使用双线性插值(filter = PointSample 时使用最近邻)。
/// edge 参数目前统一按 Clamp 处理(边缘像素重复)。
pub fn resize_pure(
  img : @types.Image,
  out_w : Int,
  out_h : Int,
  filter? : @types.ResizeFilter = @types.ResizeFilter::Default,
  edge? : @types.ResizeEdge = @types.ResizeEdge::Clamp,
) -> @types.Image raise @types.LoadError {
  let _ = edge
  if out_w <= 0 || out_h <= 0 {
    raise @types.LoadError::DecodeFailed("resize 输出尺寸必须为正数")
  }
  let ch = img.channels
  let w = img.width
  let h = img.height
  let data_int : Array[Int] = Array::make(w * h * ch, 0)
  for i in 0..<(w * h * ch) {
    data_int[i] = img.data[i].to_int()
  }
  let scale_x = Float::from_int(w) / Float::from_int(out_w)
  let scale_y = Float::from_int(h) / Float::from_int(out_h)
  let use_nearest = match filter {
    PointSample => true
    _ => false
  }
  let out : Array[Byte] = Array::make(out_w * out_h * ch, b'\x00')
  for dy in 0..<out_h {
    let sy = (Float::from_int(dy) + 0.5F) * scale_y - 0.5F
    for dx in 0..<out_w {
      let sx = (Float::from_int(dx) + 0.5F) * scale_x - 0.5F
      for ci in 0..<ch {
        let val = if use_nearest {
          resize_sample_nearest(data_int, w, h, ch, ci, sx, sy)
        } else {
          resize_sample_bilinear(data_int, w, h, ch, ci, sx, sy)
        }
        out[(dy * out_w + dx) * ch + ci] = val.clamp(min=0, max=255).to_byte()
      }
    }
  }
  @types.Image::{
    width: out_w,
    height: out_h,
    channels: ch,
    data: Bytes::from_array(out),
  }
}