code wiki / _hdl_build / nx_zpng.nx

nx_zpng.nx source

↩ module page · 113 lines · 6110 B

1// nx_zpng.nx -- a COMPRESSING sovereign PNG encoder (the unblocker for shipping generated art at size). Replaces 2// nx_png's STORED (uncompressed ~3B/px) zlib body with a real DEFLATE: PNG "Sub" row filter (flat horizontal 3// spans -> 0x00) + fixed-Huffman blocks + LZ77 RLE (dist=1 back-refs collapse the 0-runs). Reuses nx_png's proven 4// CRC32/Adler32/be32 + PNG framing. Output is standard zlib/DEFLATE -> any browser decodes it. No zlib/libpng. 5// Our generated images (gradients + flat fills) compress massively. license_tier: ORIGINAL 6import "nx_png.nx" 7import "nx_syscalls.nx" 8const K_MAGIC_1024: i64 = 1024 9 10// ---- bit writer: stream packed LSB-first into bytes; st[0]=zpos st[1]=bitbuf st[2]=bitcnt ---- 11func zp_putbit(z: *u8, st: *i64, b: i64) -> i64 { 12 st[1] = st[1] | ((b & 1) << st[2]); st[2] = st[2] + 1 13 if st[2] == 8 { z[st[0]] = (st[1] & 255) as u8; st[0] = st[0] + 1; st[1] = 0; st[2] = 0 } 14 return 0 15} 16func zp_lsb(z: *u8, st: *i64, val: i64, n: i64) -> i64 { var i: i64 = 0; while i < n { zp_putbit(z, st, (val >> i) & 1); i = i + 1 } return 0 } // extra bits / header (LSB first) 17func zp_huff(z: *u8, st: *i64, code: i64, n: i64) -> i64 { var i: i64 = n - 1; while i >= 0 { zp_putbit(z, st, (code >> i) & 1); i = i - 1 } return 0 } // Huffman codes (MSB first) 18func zp_flush(z: *u8, st: *i64) -> i64 { if st[2] > 0 { z[st[0]] = (st[1] & 255) as u8; st[0] = st[0] + 1; st[1] = 0; st[2] = 0 } return 0 } 19 20// fixed-Huffman literal/length symbol 21func zp_sym(z: *u8, st: *i64, s: i64) -> i64 { 22 if s <= 143 { zp_huff(z, st, 0x30 + s, 8) } 23 else { if s <= 255 { zp_huff(z, st, 0x190 + (s - 144), 9) } 24 else { if s <= 279 { zp_huff(z, st, s - 256, 7) } 25 else { zp_huff(z, st, 0xC0 + (s - 280), 8) } } } 26 return 0 27} 28// length -> (code, extra-bits, base) into out[0..2] 29func zp_lencode(len: i64, out: *i64) -> i64 { 30 if len <= 10 { out[0] = 257 + (len - 3); out[1] = 0; out[2] = len; return 0 } 31 if len <= 18 { let k: i64 = (len - 11)/2; out[0] = 265 + k; out[1] = 1; out[2] = 11 + k*2; return 0 } 32 if len <= 34 { let k: i64 = (len - 19)/4; out[0] = 269 + k; out[1] = 2; out[2] = 19 + k*4; return 0 } 33 if len <= 66 { let k: i64 = (len - 35)/8; out[0] = 273 + k; out[1] = 3; out[2] = 35 + k*8; return 0 } 34 if len <= 130 { let k: i64 = (len - 67)/16; out[0] = 277 + k; out[1] = 4; out[2] = 67 + k*16; return 0 } 35 if len <= 257 { let k: i64 = (len - 131)/32; out[0] = 281 + k; out[1] = 5; out[2] = 131 + k*32; return 0 } 36 out[0] = 285; out[1] = 0; out[2] = 258; return 0 37} 38// a dist=1 match of length `len` (3..258): length sym + extra, then distance code 0 (5 bits, 0 extra) 39func zp_match1(z: *u8, st: *i64, len: i64, lc: *i64) -> i64 { 40 zp_lencode(len, lc); zp_sym(z, st, lc[0]); zp_lsb(z, st, len - lc[2], lc[1]); zp_huff(z, st, 0, 5); return 0 41} 42// DEFLATE the raw bytes as ONE fixed-Huffman block with RLE (dist=1) for runs. st[0] = current z offset. 43func zp_deflate(raw: *u8, rawlen: i64, z: *u8, st: *i64) -> i64 { 44 let lc: *i64 = sys_mmap(32) as *i64 45 zp_lsb(z, st, 1, 1) // BFINAL = 1 46 zp_lsb(z, st, 1, 2) // BTYPE = 01 (fixed Huffman) 47 var i: i64 = 0 48 while i < rawlen { 49 var run: i64 = 1; var go: i64 = 1 50 while go == 1 { if i + run >= rawlen { go = 0 } else { if raw[i+run] != raw[i] { go = 0 } else { run = run + 1 } } } 51 zp_sym(z, st, raw[i] as i64) // one literal 52 var rem: i64 = run - 1 53 while rem >= 3 { var l: i64 = rem; if l > 258 { l = 258 } zp_match1(z, st, l, lc); rem = rem - l } 54 while rem > 0 { zp_sym(z, st, raw[i] as i64); rem = rem - 1 } 55 i = i + run 56 } 57 zp_sym(z, st, 256) // end of block 58 zp_flush(z, st) 59 return 0 60} 61 62// compressing RGB8 PNG writer (same interface as write_png). 63func write_png_z(fb: *i64, w: i64, h: i64, path: *u8) -> i64 { 64 let rowbytes: i64 = 1 + w * 3 65 let rawlen: i64 = h * rowbytes 66 let raw: *u8 = sys_mmap(rawlen) as *u8 67 var o: i64 = 0; var y: i64 = 0 68 while y < h { 69 raw[o] = 1 as u8; o = o + 1 // filter: Sub 70 var x: i64 = 0 71 while x < w { 72 let c: i64 = fb[y*w + x] 73 var lR: i64 = 0; var lG: i64 = 0; var lB: i64 = 0 74 if x > 0 { let pc: i64 = fb[y*w + x - 1]; lR = pc & 0xff; lG = (pc >> 8) & 0xff; lB = (pc >> 16) & 0xff } 75 raw[o] = ((c & 0xff) - lR) as u8 76 raw[o+1] = (((c >> 8) & 0xff) - lG) as u8 77 raw[o+2] = (((c >> 16) & 0xff) - lB) as u8 78 o = o + 3; x = x + 1 79 } 80 y = y + 1 81 } 82 let zcap: i64 = rawlen * 2 + K_MAGIC_1024 83 let z: *u8 = sys_mmap(zcap) as *u8 84 z[0] = 0x78 as u8; z[1] = 0x01 as u8 85 let st: *i64 = sys_mmap(32) as *i64; st[0] = 2; st[1] = 0; st[2] = 0 86 zp_deflate(raw, rawlen, z, st) 87 var zo: i64 = st[0] 88 png_be32(z, zo, png_adler32(raw, 0, rawlen)); zo = zo + 4 89 let zlen: i64 = zo 90 let pcap: i64 = 8 + 25 + 12 + zlen + 32 91 let p: *u8 = sys_mmap(pcap) as *u8 92 p[0]=137 as u8; p[1]=80 as u8; p[2]=78 as u8; p[3]=71 as u8; p[4]=13 as u8; p[5]=10 as u8; p[6]=26 as u8; p[7]=10 as u8 93 var po: i64 = 8 94 png_be32(p, po, 13); po = po + 4 95 let ih: i64 = po 96 p[po]=73 as u8; p[po+1]=72 as u8; p[po+2]=68 as u8; p[po+3]=82 as u8; po = po + 4 97 png_be32(p, po, w); po = po + 4; png_be32(p, po, h); po = po + 4 98 p[po]=8 as u8; p[po+1]=2 as u8; p[po+2]=0 as u8; p[po+3]=0 as u8; p[po+4]=0 as u8; po = po + 5 99 png_be32(p, po, png_crc32(p, ih, 17)); po = po + 4 100 png_be32(p, po, zlen); po = po + 4 101 let id: i64 = po 102 p[po]=73 as u8; p[po+1]=68 as u8; p[po+2]=65 as u8; p[po+3]=84 as u8; po = po + 4 103 var j: i64 = 0; while j < zlen { p[po + j] = z[j]; j = j + 1 } po = po + zlen 104 png_be32(p, po, png_crc32(p, id, 4 + zlen)); po = po + 4 105 png_be32(p, po, 0); po = po + 4 106 let ie: i64 = po 107 p[po]=73 as u8; p[po+1]=69 as u8; p[po+2]=78 as u8; p[po+3]=68 as u8; po = po + 4 108 png_be32(p, po, png_crc32(p, ie, 4)); po = po + 4 109 let fd: i64 = sys_openat_wr(path, 0x1a4) 110 if fd < 0 { return 0 - 1 } 111 sys_write(fd, p, po); sys_close(fd) 112 return po 113}