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
// 纯 MoonBit BMP 解码器
// 支持 24-bit / 32-bit 无压缩 BMP(BI_RGB,compression=0)
// 规范: https://en.wikipedia.org/wiki/BMP_file_format

///|
/// 解码 BMP 字节流为 Image(24-bit -> channels=3, 32-bit -> channels=4)
/// 仅支持 BITMAPINFOHEADER(DIB 头 40 字节)+ BI_RGB(无压缩)
pub fn decode_bmp_pure(data : Bytes) -> @types.Image raise @types.LoadError {
  let len = data.length()
  // BMP 文件头 14 字节 + DIB 头 40 字节 = 54 字节最小
  if len < 54 {
    raise @types.LoadError::DecodeFailed("BMP 数据过短")
  }
  // 检查 magic "BM"
  if data[0].to_int() != 0x42 || data[1].to_int() != 0x4D {
    raise @types.LoadError::DecodeFailed("BMP magic 不匹配")
  }
  let data_offset = read_u32_le(data, 10)
  let dib_size = read_u32_le(data, 14)
  // 仅支持 BITMAPINFOHEADER(40 字节)
  if dib_size != 40 {
    raise @types.LoadError::DecodeFailed(
      "不支持的 BMP DIB 头大小,仅支持 BITMAPINFOHEADER(40 字节)",
    )
  }
  let width = read_u32_le(data, 18)
  let height = read_u32_le(data, 22)
  let bpp = read_u16_le(data, 28)
  let compression = read_u32_le(data, 30)
  // 仅支持 24-bit 和 32-bit
  if bpp != 24 && bpp != 32 {
    raise @types.LoadError::DecodeFailed(
      "不支持的 BMP 位深,仅支持 24-bit 和 32-bit",
    )
  }
  // 仅支持 BI_RGB(compression=0)
  if compression != 0 {
    raise @types.LoadError::DecodeFailed(
      "不支持的 BMP 压缩方式,仅支持 BI_RGB(无压缩)",
    )
  }
  // 尺寸校验
  if width == 0 || height == 0 {
    raise @types.LoadError::DecodeFailed("BMP 尺寸无效")
  }
  let abs_width = if width < 0 { -width } else { width }
  let abs_height = if height < 0 { -height } else { height }
  // 行序:height > 0 自下而上,height < 0 自上而下
  let bottom_up = height > 0
  // 每像素字节数
  let bytes_per_pixel = bpp / 8
  // 行数据字节数(4 字节对齐)
  let row_size = (abs_width * bytes_per_pixel + 3) / 4 * 4
  // 输出 channels
  let out_channels = bytes_per_pixel
  @types.check_dims(abs_width, abs_height, out_channels)
  // 输出像素数据
  let pixel_count = abs_width * abs_height
  let pixels : Array[Byte] = Array::make(pixel_count * out_channels, b'\x00')
  // 逐行读取
  for file_row in 0..<abs_height {
    // 计算输出行号
    let out_row = if bottom_up { abs_height - 1 - file_row } else { file_row }
    let row_start = data_offset + file_row * row_size
    for x in 0..<abs_width {
      let pixel_offset = row_start + x * bytes_per_pixel
      if pixel_offset + bytes_per_pixel > len {
        raise @types.LoadError::DecodeFailed("BMP 像素数据越界")
      }
      // BGR(A) -> RGB(A)
      let b = data[pixel_offset].to_int()
      let g = data[pixel_offset + 1].to_int()
      let r = data[pixel_offset + 2].to_int()
      let out_idx = (out_row * abs_width + x) * out_channels
      pixels[out_idx] = r.to_byte()
      pixels[out_idx + 1] = g.to_byte()
      pixels[out_idx + 2] = b.to_byte()
      if out_channels == 4 {
        let a = data[pixel_offset + 3].to_int()
        pixels[out_idx + 3] = a.to_byte()
      }
    }
  }
  @types.Image::{
    width: abs_width,
    height: abs_height,
    channels: out_channels,
    data: Bytes::from_array(pixels),
  }
}