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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 图像拼接与平铺(纯 MoonBit):hstack / vstack / tile / transpose

///|
/// 水平拼接。两图高度和通道数必须相同。
pub fn hstack(
  left : @types.Image,
  right : @types.Image,
) -> @types.Image raise @types.LoadError {
  if left.height != right.height || left.channels != right.channels {
    raise @types.LoadError::DecodeFailed(
      "hstack 两图高度/通道数必须相同",
    )
  }
  let ch = left.channels
  let new_w = left.width + right.width
  let h = left.height
  let out : Array[Byte] = Array::make(new_w * h * ch, b'\x00')
  for y in 0..<h {
    for x in 0..<left.width {
      let src_off = (y * left.width + x) * ch
      let dst_off = (y * new_w + x) * ch
      for c in 0..<ch {
        out[dst_off + c] = left.data[src_off + c]
      }
    }
    for x in 0..<right.width {
      let src_off = (y * right.width + x) * ch
      let dst_off = (y * new_w + left.width + x) * ch
      for c in 0..<ch {
        out[dst_off + c] = right.data[src_off + c]
      }
    }
  }
  @types.Image::{
    width: new_w,
    height: h,
    channels: ch,
    data: Bytes::from_array(out),
  }
}

///|
/// 垂直拼接。两图宽度和通道数必须相同。
pub fn vstack(
  top : @types.Image,
  bottom : @types.Image,
) -> @types.Image raise @types.LoadError {
  if top.width != bottom.width || top.channels != bottom.channels {
    raise @types.LoadError::DecodeFailed(
      "vstack 两图宽度/通道数必须相同",
    )
  }
  let ch = top.channels
  let w = top.width
  let new_h = top.height + bottom.height
  let out : Array[Byte] = Array::make(w * new_h * ch, b'\x00')
  for i in 0..<(w * top.height * ch) {
    out[i] = top.data[i]
  }
  let top_size = w * top.height * ch
  for i in 0..<(w * bottom.height * ch) {
    out[top_size + i] = bottom.data[i]
  }
  @types.Image::{
    width: w,
    height: new_h,
    channels: ch,
    data: Bytes::from_array(out),
  }
}

///|
/// 平铺图像。将 img 在 x 方向重复 cols 次,y 方向重复 rows 次。
pub fn tile(
  img : @types.Image,
  cols : Int,
  rows : Int,
) -> @types.Image raise @types.LoadError {
  if cols <= 0 || rows <= 0 {
    raise @types.LoadError::DecodeFailed("tile cols/rows 必须为正数")
  }
  let ch = img.channels
  let new_w = img.width * cols
  let new_h = img.height * rows
  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')
  for y in 0..<new_h {
    for x in 0..<new_w {
      let src_x = x % img.width
      let src_y = y % img.height
      let src_off = (src_y * img.width + src_x) * ch
      let dst_off = (y * new_w + x) * ch
      for c in 0..<ch {
        out[dst_off + c] = img.data[src_off + c]
      }
    }
  }
  @types.Image::{
    width: new_w,
    height: new_h,
    channels: ch,
    data: Bytes::from_array(out),
  }
}

///|
/// 转置(沿主对角线翻转)。宽高互换。
pub fn transpose(img : @types.Image) -> @types.Image {
  let ch = img.channels
  let new_w = img.height
  let new_h = img.width
  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')
  for y in 0..<img.height {
    for x in 0..<img.width {
      let src_off = (y * img.width + x) * ch
      let dst_off = (x * new_w + y) * ch
      for c in 0..<ch {
        out[dst_off + c] = img.data[src_off + c]
      }
    }
  }
  @types.Image::{
    width: new_w,
    height: new_h,
    channels: ch,
    data: Bytes::from_array(out),
  }
}