code wiki / (root) / nx_png_write.nx

nx_png_write.nx source

↩ module page · 111 lines · 4497 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 STORED blocks (no compression -> trivial + correct; a real DEFLATE pass is a perf 4// rung). Self-contained: inline CRC-32 (IEEE) for chunks + Adler-32 for the zlib stream. license_tier: ORIGINAL 5import "nx_syscalls.nx" 6const K_MAGIC_65521: i64 = 65521 7const K_MAGIC_65534: i64 = 65534 8const K_MAGIC_65535: i64 = 65535 9 10func pw_u32be(fd: i64, v: i64) -> i64 { 11 let b: *u8 = sys_mmap(4) 12 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 13 sys_write(fd, b, 4); return 0 14} 15// CRC-32 (IEEE) over buf[off..off+len), continuing from `crc` (NOT finalized). 16func pw_crc_block(crc: i64, buf: *u8, off: i64, len: i64) -> i64 { 17 var c: i64 = crc 18 var i: i64 = 0 19 while i < len { 20 c = c ^ (buf[off+i] & 0xff) 21 var k: i64 = 0 22 while k < 8 { 23 let m: i64 = c & 1 24 c = c >> 1 25 if m == 1 { c = c ^ 0xEDB88320 } 26 k = k + 1 27 } 28 i = i + 1 29 } 30 return c 31} 32func pw_adler32(buf: *u8, off: i64, len: i64) -> i64 { 33 var a: i64 = 1 34 var b: i64 = 0 35 var i: i64 = 0 36 while i < len { 37 a = (a + (buf[off+i] & 0xff)) % K_MAGIC_65521 38 b = (b + a) % K_MAGIC_65521 39 i = i + 1 40 } 41 return (b << 16) | a 42} 43// write a chunk (type 4 bytes + data) with length + CRC. 44func pw_chunk(fd: i64, typ: *u8, data: *u8, dlen: i64) -> i64 { 45 pw_u32be(fd, dlen) 46 sys_write(fd, typ, 4) 47 if dlen > 0 { sys_write(fd, data, dlen) } 48 var crc: i64 = pw_crc_block(0xFFFFFFFF, typ, 0, 4) 49 if dlen > 0 { crc = pw_crc_block(crc, data, 0, dlen) } 50 pw_u32be(fd, crc ^ 0xFFFFFFFF) 51 return 0 52} 53 54// Write an 8-bit RGB image (rgb = w*h*3 bytes, row-major top-down) as a PNG file. Returns 0 ok. 55func nx_png_write_rgb(path: *u8, rgb: *u8, w: i64, h: i64) -> i64 { 56 let fd: i64 = sys_openat_wr(path, 420) 57 if fd < 0 { return 0 - 1 } 58 // signature 59 let sig: *u8 = sys_mmap(8) 60 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 61 sys_write(fd, sig, 8) 62 // IHDR 63 let ihdr: *u8 = sys_mmap(13) 64 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 65 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 66 ihdr[8]=8 as u8 // bit depth 67 ihdr[9]=2 as u8 // color type 2 = truecolor RGB 68 ihdr[10]=0 as u8 // compression 69 ihdr[11]=0 as u8 // filter 70 ihdr[12]=0 as u8 // interlace 71 pw_chunk(fd, "IHDR\x00" as *u8, ihdr, 13) 72 // filtered scanlines: each row = filter(0) + w*3 RGB 73 let rowbytes: i64 = w * 3 74 let raw_len: i64 = h * (1 + rowbytes) 75 let raw: *u8 = sys_mmap(raw_len + 16) 76 var y: i64 = 0 77 var rp: i64 = 0 78 while y < h { 79 raw[rp] = 0 as u8; rp = rp + 1 80 var x: i64 = 0 81 let srow: i64 = y * rowbytes 82 while x < rowbytes { raw[rp] = rgb[srow + x]; rp = rp + 1; x = x + 1 } 83 y = y + 1 84 } 85 // zlib STORED stream: 0x78 0x01 + blocks(<=65535) + adler32 86 let nblk: i64 = (raw_len + K_MAGIC_65534) / K_MAGIC_65535 87 let zlen: i64 = 2 + nblk * 5 + raw_len + 4 88 let z: *u8 = sys_mmap(zlen + 16) 89 z[0]=0x78 as u8; z[1]=0x01 as u8 90 var zp: i64 = 2 91 var pos: i64 = 0 92 while pos < raw_len { 93 var blen: i64 = raw_len - pos 94 if blen > K_MAGIC_65535 { blen = K_MAGIC_65535 } 95 var last: i64 = 0 96 if pos + blen >= raw_len { last = 1 } 97 z[zp] = last as u8; zp = zp + 1 // BFINAL/BTYPE=00 98 z[zp] = (blen & 255) as u8; z[zp+1] = ((blen>>8)&255) as u8; zp = zp + 2 // LEN (LE) 99 let nl: i64 = (blen ^ 0xFFFF) & 0xFFFF 100 z[zp] = (nl & 255) as u8; z[zp+1] = ((nl>>8)&255) as u8; zp = zp + 2 // NLEN 101 var i: i64 = 0 102 while i < blen { z[zp] = raw[pos + i]; zp = zp + 1; i = i + 1 } 103 pos = pos + blen 104 } 105 let ad: i64 = pw_adler32(raw, 0, raw_len) 106 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 107 pw_chunk(fd, "IDAT\x00" as *u8, z, zp) 108 pw_chunk(fd, "IEND\x00" as *u8, sig, 0) 109 sys_close(fd) 110 return 0 111}