code wiki / (root) / nx_pnm_decode.nx

nx_pnm_decode.nx source

↩ module page · 187 lines · 5858 B

1// nx_pnm_decode.nx -- Netpbm reader: P1/P2/P3 (ASCII) and P4/P5/P6 (binary), 2// the Unix lingua franca that every 90s toolchain could emit. 3// 4// EXACTLY ONE WHITESPACE BYTE FOLLOWS THE HEADER of a binary variant, and the 5// payload starts immediately after it. Skipping "whitespace" greedily eats the 6// first pixel row whenever that row happens to begin with 0x20 or 0x0A -- a 7// corruption that appears only on some images, which is the worst kind. 8// 9// COMMENTS MAY APPEAR BETWEEN ANY TWO HEADER FIELDS, including in the middle 10// of the dimensions, so comment skipping belongs in the token reader rather 11// than in a one-shot header pre-pass. 12// 13// P1/P4 bitmaps invert the usual convention: 1 means BLACK. 14// 15// genealogy_id: netpbm_pnm 16// lineage_id: nx_pnm_v1 17// license_tier: ORIGINAL 18 19import "nx_syscalls.nx" 20 21func pnm_b(d: *u8, o: i64) -> i64 { return (d[o] as i64) & 255 } 22 23func pnm_is_ws(c: i64) -> i64 { 24 if c == 32 { return 1 } 25 if c == 9 { return 1 } 26 if c == 10 { return 1 } 27 if c == 13 { return 1 } 28 if c == 11 { return 1 } 29 if c == 12 { return 1 } 30 return 0 31} 32 33// Read the next integer token, skipping whitespace AND #-to-end-of-line 34// comments. pos[0] is advanced past the token; returns 0-1 on failure. 35func pnm_tok(raw: *u8, n: i64, pos: *i64) -> i64 { 36 var p: i64 = pos[0] 37 var scan: i64 = 1 38 while scan == 1 { 39 if p >= n { return 0 - 1 } 40 let c: i64 = pnm_b(raw, p) 41 if pnm_is_ws(c) == 1 { 42 p = p + 1 43 } else { 44 if c == 35 { 45 // '#' comment: consume through the newline (or to EOF) 46 var incm: i64 = 1 47 while incm == 1 { 48 if p >= n { incm = 0 } else { 49 if pnm_b(raw, p) == 10 { incm = 0 } 50 p = p + 1 51 } 52 } 53 } else { 54 scan = 0 55 } 56 } 57 } 58 var v: i64 = 0 59 var got: i64 = 0 60 var dig: i64 = 1 61 while dig == 1 { 62 if p >= n { dig = 0 } else { 63 let c: i64 = pnm_b(raw, p) 64 if c < 48 { dig = 0 } else { 65 if c > 57 { dig = 0 } else { 66 v = v*10 + (c - 48) 67 got = 1 68 p = p + 1 69 } 70 } 71 } 72 } 73 if got == 0 { return 0 - 1 } 74 pos[0] = p 75 return v 76} 77 78func pnm_decode_rgb(raw: *u8, n: i64, out_wh: *i64) -> *u8 { 79 if n < 8 { return 0 as *u8 } 80 if pnm_b(raw, 0) != 80 { return 0 as *u8 } // 'P' 81 let kind: i64 = pnm_b(raw, 1) - 48 82 if kind < 1 { return 0 as *u8 } 83 if kind > 6 { return 0 as *u8 } 84 85 let pos: *i64 = sys_mmap(16) as *i64 86 pos[0] = 2 87 let w: i64 = pnm_tok(raw, n, pos) 88 if w <= 0 { return 0 as *u8 } 89 let h: i64 = pnm_tok(raw, n, pos) 90 if h <= 0 { return 0 as *u8 } 91 var maxv: i64 = 1 92 if kind != 1 { if kind != 4 { 93 maxv = pnm_tok(raw, n, pos) 94 if maxv <= 0 { return 0 as *u8 } 95 if maxv > 65535 { return 0 as *u8 } 96 } } 97 98 let rgb: *u8 = sys_mmap(w * h * 3 + 64) 99 let npix: i64 = w * h 100 101 // ---- ASCII variants: every sample is a token ---- 102 if kind <= 3 { 103 var i: i64 = 0 104 while i < npix { 105 let dof: i64 = i * 3 106 if kind == 3 { 107 let r: i64 = pnm_tok(raw, n, pos) 108 let g: i64 = pnm_tok(raw, n, pos) 109 let b: i64 = pnm_tok(raw, n, pos) 110 if r < 0 { return 0 as *u8 } 111 if g < 0 { return 0 as *u8 } 112 if b < 0 { return 0 as *u8 } 113 rgb[dof] = ((r*255)/maxv) as u8 114 rgb[dof+1] = ((g*255)/maxv) as u8 115 rgb[dof+2] = ((b*255)/maxv) as u8 116 } else { 117 let v: i64 = pnm_tok(raw, n, pos) 118 if v < 0 { return 0 as *u8 } 119 var g: i64 = (v*255)/maxv 120 if kind == 1 { if v == 1 { g = 0 } else { g = 255 } } // 1 = black 121 rgb[dof] = g as u8 122 rgb[dof+1] = g as u8 123 rgb[dof+2] = g as u8 124 } 125 i = i + 1 126 } 127 out_wh[0] = w 128 out_wh[1] = h 129 return rgb 130 } 131 132 // ---- binary variants: EXACTLY ONE whitespace byte, then the payload ---- 133 var p: i64 = pos[0] 134 if p >= n { return 0 as *u8 } 135 if pnm_is_ws(pnm_b(raw, p)) == 0 { return 0 as *u8 } 136 p = p + 1 137 138 var bps: i64 = 1 139 if maxv > 255 { bps = 2 } 140 141 if kind == 4 { 142 // packed bitmap: rows are byte-aligned, MSB first, 1 = black 143 let stride: i64 = (w + 7) / 8 144 if p + stride*h > n { return 0 as *u8 } 145 var y: i64 = 0 146 while y < h { 147 var x: i64 = 0 148 while x < w { 149 let bit: i64 = (pnm_b(raw, p + y*stride + (x >> 3)) >> (7 - (x & 7))) & 1 150 var g: i64 = 255 151 if bit == 1 { g = 0 } 152 let dof: i64 = (y*w + x) * 3 153 rgb[dof] = g as u8 154 rgb[dof+1] = g as u8 155 rgb[dof+2] = g as u8 156 x = x + 1 157 } 158 y = y + 1 159 } 160 out_wh[0] = w 161 out_wh[1] = h 162 return rgb 163 } 164 165 var nch: i64 = 1 166 if kind == 6 { nch = 3 } 167 if p + npix*nch*bps > n { return 0 as *u8 } 168 var i: i64 = 0 169 while i < npix { 170 let dof: i64 = i * 3 171 let so: i64 = p + i*nch*bps 172 if nch == 3 { 173 rgb[dof] = ((pnm_b(raw, so)*255)/maxv) as u8 174 rgb[dof+1] = ((pnm_b(raw, so+bps)*255)/maxv) as u8 175 rgb[dof+2] = ((pnm_b(raw, so+2*bps)*255)/maxv) as u8 176 } else { 177 let g: i64 = (pnm_b(raw, so)*255)/maxv 178 rgb[dof] = g as u8 179 rgb[dof+1] = g as u8 180 rgb[dof+2] = g as u8 181 } 182 i = i + 1 183 } 184 out_wh[0] = w 185 out_wh[1] = h 186 return rgb 187}