code wiki / (root) / nx_png.nx

nx_png.nx source

↩ module page · 340 lines · 16678 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 PNG_ZLIB_HDR_BYTES: i64 = 2 // 0x78 0x01 9const PNG_ADLER_BYTES: i64 = 4 // adler32 trailer of the zlib stream 10const K_MAGIC_65521: i64 = 65521 11const K_MAGIC_65536: i64 = 65536 12// ONE OWNER for the deflate reserve, declared HERE because a module const must precede its first 13// reader -- the compiler refuses otherwise, and says why: it would silently read 0. write_png_buf 14// (the encoder) and png_buf_cap (the publisher bound) both read these, so the encoder reserve and 15// the publish buffer bound can never drift apart into two numbers that must agree. 16const PNG_DEFLATE_RESERVE_BYTES: i64 = 128 17const PNG_DEFLATE_EXPAND_DIV: i64 = 2 // + rawlen/2 headroom for an incompressible worst case 18 19func png_crc32(buf: *u8, off: i64, len: i64) -> i64 { 20 var crc: i64 = 0xFFFFFFFF 21 var i: i64 = 0 22 while i < len { 23 crc = crc ^ (buf[off + i] as i64) 24 var k: i64 = 0 25 while k < 8 { 26 let m: i64 = 0 - (crc & 1) 27 crc = ((crc >> 1) ^ (0xEDB88320 & m)) & 0xFFFFFFFF 28 k = k + 1 29 } 30 i = i + 1 31 } 32 return (crc ^ 0xFFFFFFFF) & 0xFFFFFFFF 33} 34func png_adler32(buf: *u8, off: i64, len: i64) -> i64 { 35 var s1: i64 = 1 36 var s2: i64 = 0 37 var i: i64 = 0 38 while i < len { 39 s1 = (s1 + (buf[off + i] as i64)) % K_MAGIC_65521 40 s2 = (s2 + s1) % K_MAGIC_65521 41 i = i + 1 42 } 43 return s2 * K_MAGIC_65536 + s1 44} 45func png_be32(b: *u8, o: i64, v: i64) -> i64 { 46 b[o] = ((v >> 24) & 0xff) as u8 47 b[o + 1] = ((v >> 16) & 0xff) as u8 48 b[o + 2] = ((v >> 8) & 0xff) as u8 49 b[o + 3] = (v & 0xff) as u8 50 return 0 51} 52func png_paeth(a: i64, b: i64, c: i64) -> i64 { // a=left b=up c=upleft 53 let p: i64 = a + b - c 54 var pa: i64 = p - a 55 if pa < 0 { pa = 0 - pa } 56 var pb: i64 = p - b 57 if pb < 0 { pb = 0 - pb } 58 var pc: i64 = p - c 59 if pc < 0 { pc = 0 - pc } 60 if pa <= pb { if pa <= pc { return a } } 61 if pb <= pc { return b } 62 return c 63} 64func png_sabs(v: i64) -> i64 { var x: i64 = v & 0xff; if x > 127 { x = 256 - x } return x } 65func png_g(img: *u8, w3: i64, r: i64, j: i64) -> i64 { // safe fetch, 0 outside bounds 66 if r < 0 { return 0 } 67 if j < 0 { return 0 } 68 return img[r * w3 + j] as i64 69} 70 71 72// same encoder as write_png but into a caller buffer `out` (size >= h*(1+w*3)*3/2 + 256); returns byte length. 73// For serving a PNG over HTTP without a temp file. Additive; write_png unchanged. 74func png_chunk_length_valid(n:i64)->i64 { return n>=0 && n<=2147483647 } 75 76func write_png_buf_checked(fb: *i64, fb_pixels:i64, w: i64, h: i64, out: *u8, capacity:i64) -> i64 { 77 let needed:i64=png_buf_cap(w,h) 78 if needed<0 || capacity<needed || (fb as i64)<=0 || (out as i64)<=0{return 0-1} 79 if fb_pixels<w*h{return 0-1} 80 let w3: i64 = w * 3 81 let img: *u8 = sys_mmap_try(h * w3) as *u8 82 if (img as i64)<=0{return 0-1} 83 var y: i64 = 0 84 while y < h { 85 var x: i64 = 0 86 while x < w { 87 let c: i64 = fb[y * w + x] 88 img[y * w3 + x * 3] = (c & 0xff) as u8 89 img[y * w3 + x * 3 + 1] = ((c >> 8) & 0xff) as u8 90 img[y * w3 + x * 3 + 2] = ((c >> 16) & 0xff) as u8 91 x = x + 1 92 } 93 y = y + 1 94 } 95 let rowbytes: i64 = 1 + w3 96 let rawlen: i64 = h * rowbytes 97 let raw: *u8 = sys_mmap_try(rawlen) as *u8 98 if (raw as i64)<=0{sys_munmap_direct(img,h*w3);return 0-1} 99 var o: i64 = 0 100 y = 0 101 while y < h { 102 var bestft: i64 = 0 103 var bestcost: i64 = 0 - 1 104 var ft: i64 = 0 105 while ft < 5 { 106 var cost: i64 = 0 107 var j: i64 = 0 108 while j < w3 { 109 let xb: i64 = png_g(img, w3, y, j) 110 let left: i64 = png_g(img, w3, y, j - 3) 111 let up: i64 = png_g(img, w3, y - 1, j) 112 let ul: i64 = png_g(img, w3, y - 1, j - 3) 113 var res: i64 = xb 114 if ft == 1 { res = xb - left } 115 if ft == 2 { res = xb - up } 116 if ft == 3 { res = xb - (left + up) / 2 } 117 if ft == 4 { res = xb - png_paeth(left, up, ul) } 118 cost = cost + png_sabs(res) 119 j = j + 1 120 } 121 if bestcost < 0 { bestcost = cost; bestft = ft } else { if cost < bestcost { bestcost = cost; bestft = ft } } 122 ft = ft + 1 123 } 124 raw[o] = bestft as u8; o = o + 1 125 var j2: i64 = 0 126 while j2 < w3 { 127 let xb: i64 = png_g(img, w3, y, j2) 128 let left: i64 = png_g(img, w3, y, j2 - 3) 129 let up: i64 = png_g(img, w3, y - 1, j2) 130 let ul: i64 = png_g(img, w3, y - 1, j2 - 3) 131 var res: i64 = xb 132 if bestft == 1 { res = xb - left } 133 if bestft == 2 { res = xb - up } 134 if bestft == 3 { res = xb - (left + up) / 2 } 135 if bestft == 4 { res = xb - png_paeth(left, up, ul) } 136 raw[o] = (res & 0xff) as u8; o = o + 1 137 j2 = j2 + 1 138 } 139 y = y + 1 140 } 141 let zcap: i64 = rawlen + rawlen / PNG_DEFLATE_EXPAND_DIV + PNG_DEFLATE_RESERVE_BYTES 142 let z: *u8 = sys_mmap_try(zcap) as *u8 143 if (z as i64)<=0{sys_munmap_direct(raw,rawlen);sys_munmap_direct(img,h*w3);return 0-1} 144 z[0] = 0x78 as u8; z[1] = 0x01 as u8 145 let body: *u8 = (z as i64 + 2) as *u8 146 let clen: i64 = dfe_compress(raw, rawlen, body) 147 if clen<0 || clen>zcap-PNG_ZLIB_HDR_BYTES-PNG_ADLER_BYTES{sys_munmap_direct(z,zcap);sys_munmap_direct(raw,rawlen);sys_munmap_direct(img,h*w3);return 0-1} 148 var zo: i64 = 2 + clen 149 png_be32(z, zo, png_adler32(raw, 0, rawlen)); zo = zo + 4 150 let zlen: i64 = zo 151 if png_chunk_length_valid(zlen)==0{sys_munmap_direct(z,zcap);sys_munmap_direct(raw,rawlen);sys_munmap_direct(img,h*w3);return 0-1} 152 out[0]=137 as u8; out[1]=80 as u8; out[2]=78 as u8; out[3]=71 as u8 153 out[4]=13 as u8; out[5]=10 as u8; out[6]=26 as u8; out[7]=10 as u8 154 var po: i64 = 8 155 png_be32(out, po, 13); po = po + 4 156 let ih: i64 = po 157 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 158 png_be32(out, po, w); po = po + 4 159 png_be32(out, po, h); po = po + 4 160 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 161 png_be32(out, po, png_crc32(out, ih, 17)); po = po + 4 162 png_be32(out, po, zlen); po = po + 4 163 let id: i64 = po 164 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 165 var j: i64 = 0 166 while j < zlen { out[po + j] = z[j]; j = j + 1 } 167 po = po + zlen 168 png_be32(out, po, png_crc32(out, id, 4 + zlen)); po = po + 4 169 png_be32(out, po, 0); po = po + 4 170 let ie: i64 = po 171 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 172 png_be32(out, po, png_crc32(out, ie, 4)); po = po + 4 173 sys_munmap_direct(z,zcap);sys_munmap_direct(raw,rawlen);sys_munmap_direct(img,h*w3) 174 return po 175} 176 177func write_png_buf(fb:*i64,w:i64,h:i64,out:*u8)->i64 { 178 let cap:i64=png_buf_cap(w,h);if cap<0{return 0-1} 179 return write_png_buf_checked(fb,w*h,w,h,out,cap) 180} 181 182// =================================================================================================== 183// PNG PUBLISH -- THE WRITE HALF THAT USED TO HAVE NO FAILURE MODE. 184// 185// WHAT WAS WRONG (debt 1787678931, MEASURED 2026-08-25). write_png used to end: 186// fd = openat(path); if fd < 0 { return 0 - 1 }; write(fd, p, po); close(fd); return 0 187// The write() return was DISCARDED and the success return was the CONSTANT 0. So a short write was 188// invisible, a truncated file was invisible, and the ONE failure it could report at all -- the open -- 189// was reported through a value that 192 of 197 call sites throw away (censused below). A capture that 190// never landed was byte-indistinguishable from one that did: two runs of nx_sdfrender_gate whose 191// framebuffers provably DIFFERED (skin 37624 vs 37626) left knowledge/nx_sdfbody.png byte-identical at 192// 142629 B across both, and the gate printed T5 PASS determinism + PNG. 193// AN ACTUATOR WHOSE RETURN IS DISCARDED HAS NO FAILURE MODE, AND A PUBLISH THAT CANNOT FAIL CANNOT BE 194// TRUSTED TO HAVE HAPPENED. Absent work does not crash; it just never appears. 195// 196// WHY THE FIX IS HERE AND NOT AT THE CALL SITES. FULL POPULATION, no sampling: 199 write_png lines over 197// 23,242 .nx sources / 123,025,782 B, coverage_complete=1 corpus_complete=1. Two of the 199 are this 198// file (a comment and the definition). Of the 197 call sites, 5 bind the return and 192 discard it. 199// Fixing 192 call sites leaves the 193rd silent; fixing the OWNER covers every one of them, including 200// the ones nobody has written yet. A DEFECT SHARED BY 97 PERCENT OF CALLERS IS A DEFECT IN THE CALLEE. 201// 202// THE SHAPE. Three verbs, ONE encoder, no second ruler: 203// png_publish() encodes, writes to EXHAUSTION, closes, RE-READS THE ARTIFACT, and returns 204// the byte count THAT IS ON DISK -- never a receipt. Negative on any failure, 205// and it ANNOUNCES fd/wrote/of/ondisk/path whenever anything goes wrong. 206// png_publish_announced() the same, announcing on EVERY call: for an evidence artifact the line 207// PNGWRITE ... RESULT=LANDED in the transcript IS the proof it exists. 208// write_png() UNCHANGED CONTRACT (0 ok / 0-1 fail) and unchanged healthy-path stdout, so 209// every existing caller is bit-identical BY CONSTRUCTION. It DELEGATES. 210// The encoder is no longer duplicated either: write_png used to carry a second byte-for-byte copy of 211// write_png_buf encoder -- two rulers for one format, which is how a fix lands in one and not the 212// other. There is now exactly one, and the neutrality of the collapse is PROVEN by rebuild, not asserted. 213// =================================================================================================== 214 215// rw-r--r--. NAMED: the estate measured 29 organs passing this mode as a bare literal no reader decodes. 216const PNG_MODE_0644: i64 = 0x1a4 217// Announcements go to STDOUT, not stderr, ON PURPOSE: a gate verdict is read POSITIONALLY (gv_last_line 218// takes the LAST line), and in a merged stdout+stderr capture a stderr line can land after the verdict. 219const PNG_ANN_FD: i64 = 1 220// i64 decimal is at most 19 digits; the sign takes one more. Every buffer below is DERIVED from this. 221const PNG_I64_MAX_DIGITS: i64 = 19 222// File-layout bounds. All are fixed by the PNG specification, each named for what it measures. 223const PNG_SIG_BYTES: i64 = 8 // 137 P N G CR LF SUB LF 224const PNG_IHDR_CHUNK_BYTES: i64 = 25 // 4 length + 4 type + 13 data + 4 crc 225const PNG_CHUNK_FRAME_BYTES: i64 = 12 // 4 length + 4 type + 4 crc -- once for IDAT, once for IEND 226// Failure sentinels, DELIBERATELY outside errno range (Linux errno is 1..133) so a caller can tell 227// the open failed with 0-13 EACCES from the bytes did not all arrive. 228const PNG_ERR_SHORT_WRITE: i64 = 1001 229const PNG_ERR_NOT_ON_DISK: i64 = 1002 230 231func pngw_emit(s: *u8) -> i64 { 232 var n: i64 = 0 233 while s[n] != (0 as u8) { n = n + 1 } 234 sys_write(PNG_ANN_FD, s, n) 235 return 0 236} 237func pngw_num(v: i64) -> i64 { 238 let cap: i64 = PNG_I64_MAX_DIGITS + 1 239 let b: *u8 = sys_mmap(cap) as *u8 240 var x: i64 = v 241 var neg: i64 = 0 242 if x < 0 { neg = 1; x = 0 - x } 243 var i: i64 = cap - 1 244 if x == 0 { b[i] = 48 as u8; i = i - 1 } 245 while x > 0 { b[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 } 246 if neg == 1 { b[i] = 45 as u8; i = i - 1 } 247 sys_write(PNG_ANN_FD, (b as i64 + i + 1) as *u8, cap - 1 - i) 248 return 0 249} 250 251// Upper bound on the encoded file, DERIVED from the encoder own reserves rather than guessed. 252// rawlen is h rows of (1 filter byte + 3 bytes per pixel); zcap is exactly what write_png_buf reserves; 253// the zlib stream can reach zcap plus its header and adler trailer, and the file frames that with a 254// signature, an IHDR chunk and two chunk frames (IDAT, IEND). 255func png_buf_cap(w:i64,h:i64)->i64 { 256 // PNG dimensions are unsigned31-bit by specification; remaining limits derive from i64 arithmetic. 257 let imax:i64=9223372036854775807 258 if w<=0 || h<=0 || w>2147483647 || h>2147483647{return 0-1} 259 if w*h>imax/__size_of(i64){return 0-1} 260 let row:i64=1+w*3 261 if h>imax/row{return 0-1} 262 let raw:i64=h*row 263 let overhead:i64=PNG_DEFLATE_RESERVE_BYTES+PNG_ADLER_BYTES+PNG_ZLIB_HDR_BYTES+PNG_SIG_BYTES+PNG_IHDR_CHUNK_BYTES+2*PNG_CHUNK_FRAME_BYTES 264 if raw>imax-overhead || raw/PNG_DEFLATE_EXPAND_DIV>imax-overhead-raw{return 0-1} 265 return raw+raw/PNG_DEFLATE_EXPAND_DIV+overhead 266} 267 268// THE PUBLISH. Returns the byte count that is ON DISK after the write, or a negative code. 269// ann != 0 announces on every call; ann == 0 announces only when something went wrong, which is what 270// keeps the 192 legacy call sites byte-identical on their healthy path. 271func png_publish(fb: *i64, w: i64, h: i64, path: *u8, ann: i64) -> i64 { 272 let cap: i64 = png_buf_cap(w, h) 273 if cap<0{return 0-1} 274 let p: *u8 = sys_mmap_try(cap) as *u8 275 if (p as i64)<=0{return 0-1} 276 let total: i64 = write_png_buf(fb, w, h, p) 277 if total<0{sys_munmap_direct(p,cap);return total} 278 let fd: i64 = sys_openat_wr(path, PNG_MODE_0644) 279 if fd < 0 { 280 pngw_emit("PNGWRITE path=" as *u8); pngw_emit(path) 281 pngw_emit(" fd=" as *u8); pngw_num(fd) 282 pngw_emit(" wrote=0 of=" as *u8); pngw_num(total) 283 pngw_emit(" ondisk=-1 RESULT=OPEN-FAILED -- the directory does not exist under this CWD, or this process cannot write that file\n" as *u8) 284 sys_munmap_direct(p,cap) 285 return fd 286 } 287 // write() is allowed to return short. Loop to exhaustion. A call that stops advancing is a FAILURE, 288 // not a reason to spin -- and the cursor is never clobbered to exit the loop (a loop-exit sentinel 289 // written into the cursor erases the very answer the caller needs). 290 var off: i64 = 0 291 var going: i64 = 1 292 while going == 1 { 293 if off >= total { going = 0 } 294 else { 295 let n: i64 = sys_write(fd, (p as i64 + off) as *u8, total - off) 296 if n <= 0 { going = 0 } else { off = off + n } 297 } 298 } 299 sys_close(fd) 300 // READ THE ARTIFACT BACK. I wrote N bytes is a receipt; the size on disk is the artifact, and the 301 // estate standing law is to check the artifact, never the receipt. 302 var ondisk: i64 = 0 - 1 303 let rfd: i64 = sys_openat_rd(path) 304 if rfd >= 0 { 305 ondisk = sys_lseek(rfd, 0, 2) 306 sys_close(rfd) 307 } 308 var res: i64 = ondisk 309 var bad: i64 = 0 310 if off != total { bad = 1; res = 0 - PNG_ERR_SHORT_WRITE } 311 if ondisk != total { if bad == 0 { bad = 1; res = 0 - PNG_ERR_NOT_ON_DISK } } 312 var say: i64 = ann 313 if bad == 1 { say = 1 } 314 if say != 0 { 315 pngw_emit("PNGWRITE path=" as *u8); pngw_emit(path) 316 pngw_emit(" fd=" as *u8); pngw_num(fd) 317 pngw_emit(" wrote=" as *u8); pngw_num(off) 318 pngw_emit(" of=" as *u8); pngw_num(total) 319 pngw_emit(" ondisk=" as *u8); pngw_num(ondisk) 320 if bad == 0 { pngw_emit(" RESULT=LANDED\n" as *u8) } 321 if off != total { pngw_emit(" RESULT=SHORT-WRITE -- the write stopped advancing before the end\n" as *u8) } 322 if off == total { if ondisk != total { pngw_emit(" RESULT=NOT-ON-DISK -- the file does not read back at the size written\n" as *u8) } } 323 } 324 sys_munmap_direct(p,cap) 325 return res 326} 327 328// Announce on every call. THE VERB FOR AN EVIDENCE ARTIFACT: the transcript line is the proof. 329func png_publish_announced(fb: *i64, w: i64, h: i64, path: *u8) -> i64 { 330 return png_publish(fb, w, h, path, 1) 331} 332 333// LEGACY CONTRACT, DELIBERATELY UNCHANGED: 0 on success, 0-1 on failure, silent when healthy. Every one 334// of the 192 discarding call sites is bit-identical BY CONSTRUCTION -- this narrows the richer answer to 335// the old one rather than replacing it, so no caller had to be touched to get the failure mode. 336func write_png(fb: *i64, w: i64, h: i64, path: *u8) -> i64 { 337 let n: i64 = png_publish(fb, w, h, path, 0) 338 if n < 0 { return 0 - 1 } 339 return 0 340}