code wiki / (root) / nx_png.nx

nx_png.nx source

↩ module page · 268 lines · 10261 B

1// nx_png.nx -- SOVEREIGN PNG (RGB8) encoder in NishiLang. No zlib/libpng/python: adaptive per-scanline 2// FILTERING (None/Sub/Up/Avg/Paeth, min-abs-sum heuristic) + REAL DEFLATE compression (nx_deflate_enc, LZ77 + 3// fixed Huffman) -- so renders ship compact + full-resolution. CRC32 (chunk) + Adler32 (zlib) pure integer. 4// Upgraded 2026-07-04 from stored (uncompressed) blocks; same write_png() contract, valid PNG out, much smaller. 5// license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_deflate_enc.nx" 8const K_MAGIC_65521: i64 = 65521 9const K_MAGIC_65536: i64 = 65536 10 11func png_crc32(buf: *u8, off: i64, len: i64) -> i64 { 12 var crc: i64 = 0xFFFFFFFF 13 var i: i64 = 0 14 while i < len { 15 crc = crc ^ (buf[off + i] as i64) 16 var k: i64 = 0 17 while k < 8 { 18 let m: i64 = 0 - (crc & 1) 19 crc = ((crc >> 1) ^ (0xEDB88320 & m)) & 0xFFFFFFFF 20 k = k + 1 21 } 22 i = i + 1 23 } 24 return (crc ^ 0xFFFFFFFF) & 0xFFFFFFFF 25} 26func png_adler32(buf: *u8, off: i64, len: i64) -> i64 { 27 var s1: i64 = 1 28 var s2: i64 = 0 29 var i: i64 = 0 30 while i < len { 31 s1 = (s1 + (buf[off + i] as i64)) % K_MAGIC_65521 32 s2 = (s2 + s1) % K_MAGIC_65521 33 i = i + 1 34 } 35 return s2 * K_MAGIC_65536 + s1 36} 37func png_be32(b: *u8, o: i64, v: i64) -> i64 { 38 b[o] = ((v >> 24) & 0xff) as u8 39 b[o + 1] = ((v >> 16) & 0xff) as u8 40 b[o + 2] = ((v >> 8) & 0xff) as u8 41 b[o + 3] = (v & 0xff) as u8 42 return 0 43} 44func png_paeth(a: i64, b: i64, c: i64) -> i64 { // a=left b=up c=upleft 45 let p: i64 = a + b - c 46 var pa: i64 = p - a 47 if pa < 0 { pa = 0 - pa } 48 var pb: i64 = p - b 49 if pb < 0 { pb = 0 - pb } 50 var pc: i64 = p - c 51 if pc < 0 { pc = 0 - pc } 52 if pa <= pb { if pa <= pc { return a } } 53 if pb <= pc { return b } 54 return c 55} 56func png_sabs(v: i64) -> i64 { var x: i64 = v & 0xff; if x > 127 { x = 256 - x } return x } 57func png_g(img: *u8, w3: i64, r: i64, j: i64) -> i64 { // safe fetch, 0 outside bounds 58 if r < 0 { return 0 } 59 if j < 0 { return 0 } 60 return img[r * w3 + j] as i64 61} 62 63// fb = w*h packed R + G*256 + B*65536. Writes a compressed RGB8 PNG to path. 64func write_png(fb: *i64, w: i64, h: i64, path: *u8) -> i64 { 65 let w3: i64 = w * 3 66 // 1) unfiltered RGB image bytes 67 let img: *u8 = sys_mmap(h * w3) as *u8 68 var y: i64 = 0 69 while y < h { 70 var x: i64 = 0 71 while x < w { 72 let c: i64 = fb[y * w + x] 73 img[y * w3 + x * 3] = (c & 0xff) as u8 74 img[y * w3 + x * 3 + 1] = ((c >> 8) & 0xff) as u8 75 img[y * w3 + x * 3 + 2] = ((c >> 16) & 0xff) as u8 76 x = x + 1 77 } 78 y = y + 1 79 } 80 // 2) adaptive per-row filtering -> raw (filter byte + residuals per row) 81 let rowbytes: i64 = 1 + w3 82 let rawlen: i64 = h * rowbytes 83 let raw: *u8 = sys_mmap(rawlen) as *u8 84 var o: i64 = 0 85 y = 0 86 while y < h { 87 // pick the filter (0..4) with least sum of signed-abs residuals 88 var bestft: i64 = 0 89 var bestcost: i64 = 0 - 1 90 var ft: i64 = 0 91 while ft < 5 { 92 var cost: i64 = 0 93 var j: i64 = 0 94 while j < w3 { 95 let xb: i64 = png_g(img, w3, y, j) 96 let left: i64 = png_g(img, w3, y, j - 3) 97 let up: i64 = png_g(img, w3, y - 1, j) 98 let ul: i64 = png_g(img, w3, y - 1, j - 3) 99 var res: i64 = xb 100 if ft == 1 { res = xb - left } 101 if ft == 2 { res = xb - up } 102 if ft == 3 { res = xb - (left + up) / 2 } 103 if ft == 4 { res = xb - png_paeth(left, up, ul) } 104 cost = cost + png_sabs(res) 105 j = j + 1 106 } 107 if bestcost < 0 { bestcost = cost; bestft = ft } else { if cost < bestcost { bestcost = cost; bestft = ft } } 108 ft = ft + 1 109 } 110 raw[o] = bestft as u8 111 o = o + 1 112 var j2: i64 = 0 113 while j2 < w3 { 114 let xb: i64 = png_g(img, w3, y, j2) 115 let left: i64 = png_g(img, w3, y, j2 - 3) 116 let up: i64 = png_g(img, w3, y - 1, j2) 117 let ul: i64 = png_g(img, w3, y - 1, j2 - 3) 118 var res: i64 = xb 119 if bestft == 1 { res = xb - left } 120 if bestft == 2 { res = xb - up } 121 if bestft == 3 { res = xb - (left + up) / 2 } 122 if bestft == 4 { res = xb - png_paeth(left, up, ul) } 123 raw[o] = (res & 0xff) as u8 124 o = o + 1 125 j2 = j2 + 1 126 } 127 y = y + 1 128 } 129 // 3) zlib: header (0x78 0x01) + DEFLATE(raw) + adler32(raw) 130 let zcap: i64 = rawlen + rawlen / 2 + 128 131 let z: *u8 = sys_mmap(zcap) as *u8 132 z[0] = 0x78 as u8 133 z[1] = 0x01 as u8 134 let body: *u8 = (z as i64 + 2) as *u8 135 let clen: i64 = dfe_compress(raw, rawlen, body) 136 var zo: i64 = 2 + clen 137 png_be32(z, zo, png_adler32(raw, 0, rawlen)) 138 zo = zo + 4 139 let zlen: i64 = zo 140 // PNG file: sig + IHDR + IDAT + IEND, each chunk CRC32'd over type+data. 141 let pcap: i64 = 8 + 25 + 12 + zlen + 32 142 let p: *u8 = sys_mmap(pcap) as *u8 143 p[0] = 137 as u8; p[1] = 80 as u8; p[2] = 78 as u8; p[3] = 71 as u8 144 p[4] = 13 as u8; p[5] = 10 as u8; p[6] = 26 as u8; p[7] = 10 as u8 145 var po: i64 = 8 146 png_be32(p, po, 13) 147 po = po + 4 148 let ih: i64 = po 149 p[po] = 73 as u8; p[po + 1] = 72 as u8; p[po + 2] = 68 as u8; p[po + 3] = 82 as u8 // IHDR 150 po = po + 4 151 png_be32(p, po, w); po = po + 4 152 png_be32(p, po, h); po = po + 4 153 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 154 po = po + 5 155 png_be32(p, po, png_crc32(p, ih, 17)); po = po + 4 156 png_be32(p, po, zlen); po = po + 4 157 let id: i64 = po 158 p[po] = 73 as u8; p[po + 1] = 68 as u8; p[po + 2] = 65 as u8; p[po + 3] = 84 as u8 // IDAT 159 po = po + 4 160 var j: i64 = 0 161 while j < zlen { p[po + j] = z[j]; j = j + 1 } 162 po = po + zlen 163 png_be32(p, po, png_crc32(p, id, 4 + zlen)); po = po + 4 164 png_be32(p, po, 0); po = po + 4 165 let ie: i64 = po 166 p[po] = 73 as u8; p[po + 1] = 69 as u8; p[po + 2] = 78 as u8; p[po + 3] = 68 as u8 // IEND 167 po = po + 4 168 png_be32(p, po, png_crc32(p, ie, 4)); po = po + 4 169 let fd: i64 = sys_openat_wr(path, 0x1a4) 170 if fd < 0 { return 0 - 1 } 171 sys_write(fd, p, po) 172 sys_close(fd) 173 return 0 174} 175 176// same encoder as write_png but into a caller buffer `out` (size >= h*(1+w*3)*3/2 + 256); returns byte length. 177// For serving a PNG over HTTP without a temp file. Additive; write_png unchanged. 178func write_png_buf(fb: *i64, w: i64, h: i64, out: *u8) -> i64 { 179 let w3: i64 = w * 3 180 let img: *u8 = sys_mmap(h * w3) as *u8 181 var y: i64 = 0 182 while y < h { 183 var x: i64 = 0 184 while x < w { 185 let c: i64 = fb[y * w + x] 186 img[y * w3 + x * 3] = (c & 0xff) as u8 187 img[y * w3 + x * 3 + 1] = ((c >> 8) & 0xff) as u8 188 img[y * w3 + x * 3 + 2] = ((c >> 16) & 0xff) as u8 189 x = x + 1 190 } 191 y = y + 1 192 } 193 let rowbytes: i64 = 1 + w3 194 let rawlen: i64 = h * rowbytes 195 let raw: *u8 = sys_mmap(rawlen) as *u8 196 var o: i64 = 0 197 y = 0 198 while y < h { 199 var bestft: i64 = 0 200 var bestcost: i64 = 0 - 1 201 var ft: i64 = 0 202 while ft < 5 { 203 var cost: i64 = 0 204 var j: i64 = 0 205 while j < w3 { 206 let xb: i64 = png_g(img, w3, y, j) 207 let left: i64 = png_g(img, w3, y, j - 3) 208 let up: i64 = png_g(img, w3, y - 1, j) 209 let ul: i64 = png_g(img, w3, y - 1, j - 3) 210 var res: i64 = xb 211 if ft == 1 { res = xb - left } 212 if ft == 2 { res = xb - up } 213 if ft == 3 { res = xb - (left + up) / 2 } 214 if ft == 4 { res = xb - png_paeth(left, up, ul) } 215 cost = cost + png_sabs(res) 216 j = j + 1 217 } 218 if bestcost < 0 { bestcost = cost; bestft = ft } else { if cost < bestcost { bestcost = cost; bestft = ft } } 219 ft = ft + 1 220 } 221 raw[o] = bestft as u8; o = o + 1 222 var j2: i64 = 0 223 while j2 < w3 { 224 let xb: i64 = png_g(img, w3, y, j2) 225 let left: i64 = png_g(img, w3, y, j2 - 3) 226 let up: i64 = png_g(img, w3, y - 1, j2) 227 let ul: i64 = png_g(img, w3, y - 1, j2 - 3) 228 var res: i64 = xb 229 if bestft == 1 { res = xb - left } 230 if bestft == 2 { res = xb - up } 231 if bestft == 3 { res = xb - (left + up) / 2 } 232 if bestft == 4 { res = xb - png_paeth(left, up, ul) } 233 raw[o] = (res & 0xff) as u8; o = o + 1 234 j2 = j2 + 1 235 } 236 y = y + 1 237 } 238 let zcap: i64 = rawlen + rawlen / 2 + 128 239 let z: *u8 = sys_mmap(zcap) as *u8 240 z[0] = 0x78 as u8; z[1] = 0x01 as u8 241 let body: *u8 = (z as i64 + 2) as *u8 242 let clen: i64 = dfe_compress(raw, rawlen, body) 243 var zo: i64 = 2 + clen 244 png_be32(z, zo, png_adler32(raw, 0, rawlen)); zo = zo + 4 245 let zlen: i64 = zo 246 out[0]=137 as u8; out[1]=80 as u8; out[2]=78 as u8; out[3]=71 as u8 247 out[4]=13 as u8; out[5]=10 as u8; out[6]=26 as u8; out[7]=10 as u8 248 var po: i64 = 8 249 png_be32(out, po, 13); po = po + 4 250 let ih: i64 = po 251 out[po]=73 as u8; out[po+1]=72 as u8; out[po+2]=68 as u8; out[po+3]=82 as u8; po = po + 4 252 png_be32(out, po, w); po = po + 4 253 png_be32(out, po, h); po = po + 4 254 out[po]=8 as u8; out[po+1]=2 as u8; out[po+2]=0 as u8; out[po+3]=0 as u8; out[po+4]=0 as u8; po = po + 5 255 png_be32(out, po, png_crc32(out, ih, 17)); po = po + 4 256 png_be32(out, po, zlen); po = po + 4 257 let id: i64 = po 258 out[po]=73 as u8; out[po+1]=68 as u8; out[po+2]=65 as u8; out[po+3]=84 as u8; po = po + 4 259 var j: i64 = 0 260 while j < zlen { out[po + j] = z[j]; j = j + 1 } 261 po = po + zlen 262 png_be32(out, po, png_crc32(out, id, 4 + zlen)); po = po + 4 263 png_be32(out, po, 0); po = po + 4 264 let ie: i64 = po 265 out[po]=73 as u8; out[po+1]=69 as u8; out[po+2]=78 as u8; out[po+3]=68 as u8; po = po + 4 266 png_be32(out, po, png_crc32(out, ie, 4)); po = po + 4 267 return po 268}