code wiki / (root) / nx_pcx_decode.nx

nx_pcx_decode.nx source

↩ module page · 132 lines · 4725 B

1// nx_pcx_decode.nx -- ZSoft PCX reader (PC Paintbrush; the DOS-era standard). 2// 3// THE RLE IS PER-SCANLINE, NOT PER-IMAGE. A run may not cross a scanline 4// boundary, and a decoder that treats the stream as one flat run-length 5// sequence will still produce a plausible image on most files and shear on 6// the ones that pad. We decode exactly nplanes*bytes_per_line per row and 7// stop, which is also what makes a malformed row detectable. 8// 9// PLANES ARE SEPARATE, NOT INTERLEAVED. 24-bit PCX stores a whole row of R, 10// then a whole row of G, then B -- not RGB triples. The same row buffer is 11// therefore indexed plane*bytes_per_line + x, which is the one addressing 12// mistake that yields a recognisable-but-colour-smeared image. 13// 14// 8-bit palette lives at the END of the file: the last 769 bytes, introduced 15// by a 0x0C marker byte. Its absence on a 1-plane 8-bit file is a refusal, 16// not a reason to invent a grayscale ramp. 17// 18// genealogy_id: zsoft_pcx_5 19// lineage_id: nx_pcx_v1 20// license_tier: ORIGINAL 21 22import "nx_syscalls.nx" 23 24const NX_PCX_HDR: i64 = 128 25const NX_PCX_PAL: i64 = 769 26 27func pcx_b(d: *u8, o: i64) -> i64 { return (d[o] as i64) & 255 } 28func pcx_u16(d: *u8, o: i64) -> i64 { return pcx_b(d,o) | (pcx_b(d,o+1) << 8) } 29 30// Decode ONE scanline of `total` bytes into row[]. Returns the new source 31// position, or 0-1 on a malformed/overrunning stream. 32func pcx_row(raw: *u8, n: i64, sp0: i64, total: i64, row: *u8) -> i64 { 33 var sp: i64 = sp0 34 var dp: i64 = 0 35 while dp < total { 36 if sp >= n { return 0 - 1 } 37 let b: i64 = pcx_b(raw, sp) 38 sp = sp + 1 39 if (b & 192) == 192 { 40 let cnt: i64 = b & 63 41 if sp >= n { return 0 - 1 } 42 let v: u8 = raw[sp] 43 sp = sp + 1 44 if dp + cnt > total { return 0 - 1 } 45 var k: i64 = 0 46 while k < cnt { row[dp+k] = v; k = k + 1 } 47 dp = dp + cnt 48 } else { 49 row[dp] = b as u8 50 dp = dp + 1 51 } 52 } 53 return sp 54} 55 56func pcx_decode_rgb(raw: *u8, n: i64, out_wh: *i64) -> *u8 { 57 if n < NX_PCX_HDR + 1 { return 0 as *u8 } 58 if pcx_b(raw, 0) != 10 { return 0 as *u8 } 59 if pcx_b(raw, 2) != 1 { return 0 as *u8 } // only RLE encoding is defined 60 let bpp: i64 = pcx_b(raw, 3) 61 let xmin: i64 = pcx_u16(raw, 4) 62 let ymin: i64 = pcx_u16(raw, 6) 63 let xmax: i64 = pcx_u16(raw, 8) 64 let ymax: i64 = pcx_u16(raw, 10) 65 let nplanes: i64 = pcx_b(raw, 65) 66 let bpl: i64 = pcx_u16(raw, 66) 67 if xmax < xmin { return 0 as *u8 } 68 if ymax < ymin { return 0 as *u8 } 69 let w: i64 = xmax - xmin + 1 70 let h: i64 = ymax - ymin + 1 71 if w <= 0 { return 0 as *u8 } 72 if h <= 0 { return 0 as *u8 } 73 if bpl <= 0 { return 0 as *u8 } 74 if nplanes < 1 { return 0 as *u8 } 75 if nplanes > 4 { return 0 as *u8 } 76 // supported: 8bpp x1 plane (palette), 8bpp x3 planes (truecolour), 77 // 1bpp x1 plane (monochrome). Everything else refuses. 78 var mode: i64 = 0 79 if bpp == 8 { if nplanes == 1 { mode = 1 } } 80 if bpp == 8 { if nplanes == 3 { mode = 2 } } 81 if bpp == 1 { if nplanes == 1 { mode = 3 } } 82 if mode == 0 { return 0 as *u8 } 83 84 // 256-colour palette: last 769 bytes, marker 0x0C 85 var pal: i64 = 0 - 1 86 if mode == 1 { 87 if n < NX_PCX_HDR + NX_PCX_PAL { return 0 as *u8 } 88 let po: i64 = n - NX_PCX_PAL 89 if pcx_b(raw, po) != 12 { return 0 as *u8 } 90 pal = po + 1 91 } 92 93 let total: i64 = nplanes * bpl 94 let row: *u8 = sys_mmap(total + 64) 95 let rgb: *u8 = sys_mmap(w * h * 3 + 64) 96 var sp: i64 = NX_PCX_HDR 97 var y: i64 = 0 98 while y < h { 99 sp = pcx_row(raw, n, sp, total, row) 100 if sp < 0 { return 0 as *u8 } 101 var x: i64 = 0 102 while x < w { 103 let dof: i64 = (y*w + x) * 3 104 if mode == 2 { 105 rgb[dof] = row[x] 106 rgb[dof+1] = row[bpl + x] 107 rgb[dof+2] = row[2*bpl + x] 108 } else { 109 if mode == 1 { 110 let idx: i64 = pcx_b(row, x) 111 let eo: i64 = pal + idx*3 112 if eo + 3 > n { return 0 as *u8 } 113 rgb[dof] = raw[eo] 114 rgb[dof+1] = raw[eo+1] 115 rgb[dof+2] = raw[eo+2] 116 } else { 117 // 1bpp monochrome: MSB-first within each byte 118 let bit: i64 = (pcx_b(row, x >> 3) >> (7 - (x & 7))) & 1 119 var v: i64 = 0 120 if bit == 1 { v = 255 } 121 rgb[dof] = v as u8 122 rgb[dof+1] = v as u8 123 rgb[dof+2] = v as u8 124 } } 125 x = x + 1 126 } 127 y = y + 1 128 } 129 out_wh[0] = w 130 out_wh[1] = h 131 return rgb 132}