nx_img_core.nx source
↩ module page · 111 lines · 4375 B
1// nx_img_core.nx -- R1 of the SOVEREIGN scanning stack (hardware rung up): raw bytes -> pixels. A sovereign image
2// decoder + pixel buffer, the foundation every later rung (R2 threshold, R3 segment, R4 OCR) stands on. Decodes
3// the netpbm graymap format PGM -- P2 (ASCII) + P5 (binary) -- into a row-major 8-bit grayscale buffer. Netpbm is
4// the trivial, uncompressed, sovereign image format (a scanner or our own capture can emit it, or we convert to
5// it). Integer-exact, zero dependencies. Next rungs read this buffer. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7
8const IMG_FMT: i64 = 0 // dims[]: 2 = P2 (ASCII), 5 = P5 (binary)
9const IMG_W: i64 = 1
10const IMG_H: i64 = 2
11const IMG_MAX: i64 = 3
12
13func img_is_ws(ch: i64) -> i64 {
14 if ch == 32 { return 1 }
15 if ch == 9 { return 1 }
16 if ch == 10 { return 1 }
17 if ch == 13 { return 1 }
18 return 0
19}
20
21// skip whitespace + '#' comments (to end of line) starting at p; return the new position.
22func img_skip_ws(src: *u8, len: i64, p: i64) -> i64 {
23 var i: i64 = p; var run: i64 = 1
24 while run == 1 {
25 if i >= len { run = 0 }
26 else {
27 let ch: i64 = src[i] as i64
28 if img_is_ws(ch) == 1 { i = i + 1 }
29 else {
30 if ch == 35 { // '#'
31 var c2: i64 = 1
32 while c2 == 1 { if i >= len { c2 = 0 } else { if src[i] == (10 as u8) { c2 = 0 } else { i = i + 1 } } }
33 } else { run = 0 }
34 }
35 }
36 }
37 return i
38}
39
40// read an unsigned int (skipping leading ws/comments); advance pos[0] to the char after the last digit.
41func img_next_uint(src: *u8, len: i64, pos: *i64) -> i64 {
42 var i: i64 = img_skip_ws(src, len, pos[0])
43 var v: i64 = 0; var run: i64 = 1
44 while run == 1 {
45 if i >= len { run = 0 }
46 else {
47 let ch: i64 = src[i] as i64
48 if ch >= 48 { if ch <= 57 { v = v * 10 + (ch - 48); i = i + 1 } else { run = 0 } }
49 else { run = 0 }
50 }
51 }
52 pos[0] = i
53 return v
54}
55
56// parse the PGM header; fill dims[IMG_FMT/W/H/MAX]; return the offset where pixel data begins, or -1 on bad magic.
57func img_parse_header(src: *u8, len: i64, dims: *i64) -> i64 {
58 if len < 2 { return 0 - 1 }
59 if src[0] != (80 as u8) { return 0 - 1 } // 'P'
60 let f: i64 = src[1] as i64
61 var fmt: i64 = 0
62 if f == 50 { fmt = 2 }
63 if f == 53 { fmt = 5 }
64 if fmt == 0 { return 0 - 1 }
65 dims[IMG_FMT] = fmt
66 let pos: *i64 = sys_mmap(16) as *i64
67 pos[0] = 2
68 dims[IMG_W] = img_next_uint(src, len, pos)
69 dims[IMG_H] = img_next_uint(src, len, pos)
70 dims[IMG_MAX] = img_next_uint(src, len, pos)
71 var dstart: i64 = pos[0]
72 if fmt == 5 { dstart = dstart + 1 } // P5: exactly ONE whitespace separates maxval from the raw bytes
73 return dstart
74}
75
76// decode a PGM (P2 or P5) into pix[0..w*h) as 8-bit grayscale, row-major. Returns pixel count (0 on failure).
77func img_pgm_decode(src: *u8, len: i64, pix: *u8, cap: i64, dims: *i64) -> i64 {
78 let hdr_end: i64 = img_parse_header(src, len, dims)
79 if hdr_end < 0 { return 0 }
80 let w: i64 = dims[IMG_W]; let h: i64 = dims[IMG_H]
81 if w <= 0 { return 0 }
82 if h <= 0 { return 0 }
83 let npix: i64 = w * h
84 if npix > cap { return 0 }
85 if dims[IMG_FMT] == 5 {
86 var i: i64 = 0
87 while i < npix {
88 if hdr_end + i < len { pix[i] = src[hdr_end + i] } else { pix[i] = 0 as u8 }
89 i = i + 1
90 }
91 } else {
92 let pos: *i64 = sys_mmap(16) as *i64
93 pos[0] = hdr_end
94 var i: i64 = 0
95 while i < npix { pix[i] = img_next_uint(src, len, pos) as u8; i = i + 1 }
96 }
97 return npix
98}
99
100// --- pixel buffer ops (the surface later rungs use) ---
101func img_get(pix: *u8, w: i64, x: i64, y: i64) -> i64 { return pix[y * w + x] as i64 }
102func img_set(pix: *u8, w: i64, x: i64, y: i64, v: i64) -> i64 { pix[y * w + x] = v as u8; return 0 }
103func img_invert(pix: *u8, npix: i64, maxval: i64) -> i64 {
104 var i: i64 = 0; while i < npix { pix[i] = (maxval - (pix[i] as i64)) as u8; i = i + 1 } return 0
105}
106// 256-bin intensity histogram (feeds R2 Otsu thresholding).
107func img_histogram(pix: *u8, npix: i64, hist: *i64) -> i64 {
108 var i: i64 = 0; while i < 256 { hist[i] = 0; i = i + 1 }
109 i = 0; while i < npix { let v: i64 = pix[i] as i64; hist[v] = hist[v] + 1; i = i + 1 }
110 return 0
111}