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
// 纯 MoonBit PNM 解码器
// 支持 P5(PGM 二进制灰度,channels=1)和 P6(PPM 二进制 RGB,channels=3)
// 仅支持 8-bit(maxval < 256),不支持 16-bit(maxval ≥ 256)
// 规范: https://netpbm.sourceforge.net/doc/pgm.html / ppm.html
///|
fn pnm_is_whitespace(b : Int) -> Bool {
b == 0x20 || b == 0x09 || b == 0x0A || b == 0x0D
}
///|
fn pnm_skip_ws_and_comments(data : Bytes, pos : Ref[Int], len : Int) -> Unit {
while pos.val < len {
let b = data[pos.val].to_int()
if pnm_is_whitespace(b) {
pos.val += 1
} else if b == 0x23 {
// '#' 注释行,跳过至行尾(LF)
while pos.val < len && data[pos.val].to_int() != 0x0A {
pos.val += 1
}
// 跳过 LF 本身
if pos.val < len {
pos.val += 1
}
} else {
break
}
}
}
///|
fn pnm_read_ascii_int(
data : Bytes,
pos : Ref[Int],
len : Int,
) -> Int raise @types.LoadError {
let start = pos.val
while pos.val < len {
let b = data[pos.val].to_int()
if b >= 0x30 && b <= 0x39 {
pos.val += 1
} else {
break
}
}
if pos.val == start {
raise @types.LoadError::DecodeFailed("PNM header 数字解析失败")
}
let mut result = 0
for i in start..<pos.val {
result = result * 10 + (data[i].to_int() - 0x30)
}
result
}
///|
/// 解码 PNM 字节流为 Image
/// 支持 P5(PGM 二进制灰度,channels=1)和 P6(PPM 二进制 RGB,channels=3)
/// 仅支持 8-bit(maxval < 256),不支持 16-bit(maxval ≥ 256)
/// Header: magic(2 字节) + whitespace + width(ASCII) + whitespace + height(ASCII) + whitespace + maxval(ASCII) + 单 whitespace + 像素
/// 注释行(# 开头至行尾)可出现在 magic 后 header 任意位置
pub fn decode_pnm_pure(data : Bytes) -> @types.Image raise @types.LoadError {
let len = data.length()
if len < 2 {
raise @types.LoadError::DecodeFailed("PNM 数据过短")
}
let magic0 = data[0].to_int()
let magic1 = data[1].to_int()
// 仅支持 P5 和 P6
if !(magic0 == 0x50 && (magic1 == 0x35 || magic1 == 0x36)) {
raise @types.LoadError::DecodeFailed(
"不支持的 PNM magic,仅支持 P5 和 P6",
)
}
let channels = if magic1 == 0x36 { 3 } else { 1 }
let pos = Ref(2)
// 读取 width
pnm_skip_ws_and_comments(data, pos, len)
let width = pnm_read_ascii_int(data, pos, len)
// 读取 height
pnm_skip_ws_and_comments(data, pos, len)
let height = pnm_read_ascii_int(data, pos, len)
// 读取 maxval
pnm_skip_ws_and_comments(data, pos, len)
let maxval = pnm_read_ascii_int(data, pos, len)
// 仅支持 8-bit(maxval < 256)
if maxval >= 256 {
raise @types.LoadError::DecodeFailed(
"不支持的 PNM maxval,仅支持 8-bit(maxval < 256)",
)
}
if maxval <= 0 {
raise @types.LoadError::DecodeFailed("PNM maxval 无效")
}
// 尺寸校验
if width <= 0 || height <= 0 {
raise @types.LoadError::DecodeFailed("PNM 尺寸无效")
}
// maxval 后恰好 1 个 whitespace
if pos.val >= len {
raise @types.LoadError::DecodeFailed(
"PNM 像素数据起始 whitespace 缺失",
)
}
let sep = data[pos.val].to_int()
if !pnm_is_whitespace(sep) {
raise @types.LoadError::DecodeFailed("PNM maxval 后分隔符无效")
}
pos.val += 1
// 像素数据
@types.check_dims(width, height, channels)
let pixel_count = width * height
let pixel_bytes = pixel_count * channels
if pos.val + pixel_bytes > len {
raise @types.LoadError::DecodeFailed("PNM 像素数据不足")
}
let pixels : Array[Byte] = Array::make(pixel_bytes, b'\x00')
for i in 0..<pixel_bytes {
pixels[i] = data[pos.val + i]
}
@types.Image::{ width, height, channels, data: Bytes::from_array(pixels) }
}