nx_png_write.nx source
↩ module page · 108 lines · 4716 B
1// nx_png_write.nx -- sovereign PNG ENCODER so the Nishi ecosystem can output a render that any viewer
2// (and Claude's image reader) can SEE -- closing the "can't inspect our own output" gap. 8-bit RGB,
3// filter 0, zlib stream carrying a REAL DEFLATE payload via nx_deflate_enc.
4//
5// ★2026-07-30 ADOPTION FIX, not a new feature. This writer emitted zlib STORED blocks and its own
6// header called a real DEFLATE pass "a perf rung" -- while `nx_deflate_enc.nx` (LZ77 + fixed Huffman,
7// round-trip verified against nx_deflate's inflate) sat in the SAME DIRECTORY, its header stating it
8// existed to "turn our uncompressed PNGs into real compressed ones so renders ship full-resolution".
9// The encoder was written FOR THIS CALLER and this caller never called it. Same class as the
10// seg-store leak: the capability existed, the leaf never adopted it.
11// Self-contained CRC-32 (IEEE) for chunks + Adler-32 over the RAW (pre-deflate) bytes per RFC 1950.
12// license_tier: ORIGINAL
13import "nx_syscalls.nx"
14import "nx_deflate_enc.nx"
15const K_MAGIC_65521: i64 = 65521 // Adler-32 modulus (RFC 1950), hoisted by the NAS magic campaign
16
17func pw_u32be(fd: i64, v: i64) -> i64 {
18 let b: *u8 = sys_mmap(4)
19 b[0]=((v>>24)&255) as u8; b[1]=((v>>16)&255) as u8; b[2]=((v>>8)&255) as u8; b[3]=(v&255) as u8
20 sys_write(fd, b, 4); return 0
21}
22// CRC-32 (IEEE) over buf[off..off+len), continuing from `crc` (NOT finalized).
23func pw_crc_block(crc: i64, buf: *u8, off: i64, len: i64) -> i64 {
24 var c: i64 = crc
25 var i: i64 = 0
26 while i < len {
27 c = c ^ (buf[off+i] & 0xff)
28 var k: i64 = 0
29 while k < 8 {
30 let m: i64 = c & 1
31 c = c >> 1
32 if m == 1 { c = c ^ 0xEDB88320 }
33 k = k + 1
34 }
35 i = i + 1
36 }
37 return c
38}
39func pw_adler32(buf: *u8, off: i64, len: i64) -> i64 {
40 var a: i64 = 1
41 var b: i64 = 0
42 var i: i64 = 0
43 while i < len {
44 a = (a + (buf[off+i] & 0xff)) % K_MAGIC_65521
45 b = (b + a) % K_MAGIC_65521
46 i = i + 1
47 }
48 return (b << 16) | a
49}
50// write a chunk (type 4 bytes + data) with length + CRC.
51func pw_chunk(fd: i64, typ: *u8, data: *u8, dlen: i64) -> i64 {
52 pw_u32be(fd, dlen)
53 sys_write(fd, typ, 4)
54 if dlen > 0 { sys_write(fd, data, dlen) }
55 var crc: i64 = pw_crc_block(0xFFFFFFFF, typ, 0, 4)
56 if dlen > 0 { crc = pw_crc_block(crc, data, 0, dlen) }
57 pw_u32be(fd, crc ^ 0xFFFFFFFF)
58 return 0
59}
60
61// Write an 8-bit RGB image (rgb = w*h*3 bytes, row-major top-down) as a PNG file. Returns 0 ok.
62func nx_png_write_rgb(path: *u8, rgb: *u8, w: i64, h: i64) -> i64 {
63 let fd: i64 = sys_openat_wr(path, 420)
64 if fd < 0 { return 0 - 1 }
65 // signature
66 let sig: *u8 = sys_mmap(8)
67 sig[0]=137 as u8; sig[1]=80 as u8; sig[2]=78 as u8; sig[3]=71 as u8; sig[4]=13 as u8; sig[5]=10 as u8; sig[6]=26 as u8; sig[7]=10 as u8
68 sys_write(fd, sig, 8)
69 // IHDR
70 let ihdr: *u8 = sys_mmap(13)
71 ihdr[0]=((w>>24)&255) as u8; ihdr[1]=((w>>16)&255) as u8; ihdr[2]=((w>>8)&255) as u8; ihdr[3]=(w&255) as u8
72 ihdr[4]=((h>>24)&255) as u8; ihdr[5]=((h>>16)&255) as u8; ihdr[6]=((h>>8)&255) as u8; ihdr[7]=(h&255) as u8
73 ihdr[8]=8 as u8 // bit depth
74 ihdr[9]=2 as u8 // color type 2 = truecolor RGB
75 ihdr[10]=0 as u8 // compression
76 ihdr[11]=0 as u8 // filter
77 ihdr[12]=0 as u8 // interlace
78 pw_chunk(fd, "IHDR\x00" as *u8, ihdr, 13)
79 // filtered scanlines: each row = filter(0) + w*3 RGB
80 let rowbytes: i64 = w * 3
81 let raw_len: i64 = h * (1 + rowbytes)
82 let raw: *u8 = sys_mmap(raw_len + 16)
83 var y: i64 = 0
84 var rp: i64 = 0
85 while y < h {
86 raw[rp] = 0 as u8; rp = rp + 1
87 var x: i64 = 0
88 let srow: i64 = y * rowbytes
89 while x < rowbytes { raw[rp] = rgb[srow + x]; rp = rp + 1; x = x + 1 }
90 y = y + 1
91 }
92 // zlib stream: 0x78 0x01 + REAL deflate payload + adler32(raw).
93 // Output bound is the encoder's documented contract -- fixed-Huffman EXPANDS high-entropy input
94 // (~1.30x worst case), so sizing this at raw_len+64 would overflow on noisy images.
95 let zcap: i64 = 2 + raw_len + (raw_len >> 1) + 64 + 4
96 let z: *u8 = sys_mmap(zcap + 16)
97 z[0]=0x78 as u8; z[1]=0x01 as u8
98 let dz: *u8 = (z as i64 + 2) as *u8
99 let zn: i64 = dfe_compress(raw, raw_len, dz)
100 if zn <= 0 { sys_close(fd); return 0 - 2 }
101 var zp: i64 = 2 + zn
102 let ad: i64 = pw_adler32(raw, 0, raw_len)
103 z[zp]=((ad>>24)&255) as u8; z[zp+1]=((ad>>16)&255) as u8; z[zp+2]=((ad>>8)&255) as u8; z[zp+3]=(ad&255) as u8; zp = zp + 4
104 pw_chunk(fd, "IDAT\x00" as *u8, z, zp)
105 pw_chunk(fd, "IEND\x00" as *u8, sig, 0)
106 sys_close(fd)
107 return 0
108}