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}