code wiki / (root) / nx_apng_write.nx

nx_apng_write.nx source

↩ module page · 119 lines · 4861 B

1// nx_apng_write.nx -- sovereign APNG (animated PNG) ENCODER: the test-evidence VIDEO-CLIP container 2// (operator: "video clips of the functionality being tested"). APNG plays natively in every browser 3// (grounded: knowledge/fetched/ev_apng.raw), so an evidence page just embeds the .png and it MOVES. 4// Built ON nx_png_write's proven chunk/CRC/adler primitives (imported, not duplicated). Full-frame 5// animation (blend SOURCE, dispose NONE), 8-bit RGB, zlib STORED (correct-first; deflate = a perf rung). 6// Structure: sig + IHDR + acTL(nframes,loops) + [fcTL + IDAT] (frame0) + [fcTL + fdAT]* + IEND, with the 7// shared fcTL/fdAT sequence counter the spec requires. license_tier: ORIGINAL 8import "nx_png_write.nx" 9const NX_MAGIC_65535: i64 = 65535 10 11struct ApngState { 12 fd: i64, 13 w: i64, 14 h: i64, 15 seq: i64, // shared fcTL/fdAT sequence counter 16 frame_idx: i64, 17 delay_num: i64, 18 delay_den: i64 19} 20const NX_APNG_STATE_BYTES: i64 = 56 21 22func ap_be32(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v>>24)&255) as u8; b[o+1]=((v>>16)&255) as u8; b[o+2]=((v>>8)&255) as u8; b[o+3]=(v&255) as u8; return 0 } 23func ap_be16(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v>>8)&255) as u8; b[o+1]=(v&255) as u8; return 0 } 24 25// open the clip: writes signature + IHDR + acTL. delay = delay_num/delay_den seconds per frame. 26func apng_open(st: *ApngState, path: *u8, w: i64, h: i64, nframes: i64, delay_num: i64, delay_den: i64) -> i64 { 27 let fd: i64 = sys_openat_wr(path, 420) 28 if fd < 0 { return 0 - 1 } 29 st.fd = fd 30 st.w = w 31 st.h = h 32 st.seq = 0 33 st.frame_idx = 0 34 st.delay_num = delay_num 35 st.delay_den = delay_den 36 let sig: *u8 = sys_mmap(8) 37 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 38 sys_write(fd, sig, 8) 39 let ihdr: *u8 = sys_mmap(13) 40 ap_be32(ihdr, 0, w) 41 ap_be32(ihdr, 4, h) 42 ihdr[8]=8 as u8; ihdr[9]=2 as u8; ihdr[10]=0 as u8; ihdr[11]=0 as u8; ihdr[12]=0 as u8 43 pw_chunk(fd, "IHDR\x00" as *u8, ihdr, 13) 44 let actl: *u8 = sys_mmap(8) 45 ap_be32(actl, 0, nframes) 46 ap_be32(actl, 4, 0) // num_plays 0 = loop forever 47 pw_chunk(fd, "acTL\x00" as *u8, actl, 8) 48 return 0 49} 50 51// build the zlib STORED stream for an RGB frame into z; returns zlen. (filter-0 rows, same as nx_png_write.) 52func ap_zstream(rgb: *u8, w: i64, h: i64, z: *u8) -> i64 { 53 let rowbytes: i64 = w * 3 54 let raw_len: i64 = h * (1 + rowbytes) 55 let raw: *u8 = sys_mmap(raw_len + 16) 56 var y: i64 = 0 57 var rp: i64 = 0 58 while y < h { 59 raw[rp] = 0 as u8; rp = rp + 1 60 var x: i64 = 0 61 let srow: i64 = y * rowbytes 62 while x < rowbytes { raw[rp] = rgb[srow + x]; rp = rp + 1; x = x + 1 } 63 y = y + 1 64 } 65 z[0]=0x78 as u8; z[1]=0x01 as u8 66 var zp: i64 = 2 67 var pos: i64 = 0 68 while pos < raw_len { 69 var blen: i64 = raw_len - pos 70 if blen > NX_MAGIC_65535 { blen = NX_MAGIC_65535 } 71 var last: i64 = 0 72 if pos + blen >= raw_len { last = 1 } 73 z[zp] = last as u8; zp = zp + 1 74 z[zp] = (blen & 255) as u8; z[zp+1] = ((blen>>8)&255) as u8; zp = zp + 2 75 let nl: i64 = (blen ^ 0xFFFF) & 0xFFFF 76 z[zp] = (nl & 255) as u8; z[zp+1] = ((nl>>8)&255) as u8; zp = zp + 2 77 var i: i64 = 0 78 while i < blen { z[zp] = raw[pos + i]; zp = zp + 1; i = i + 1 } 79 pos = pos + blen 80 } 81 let ad: i64 = pw_adler32(raw, 0, raw_len) 82 ap_be32(z, zp, ad) 83 return zp + 4 84} 85 86// append one full frame (rgb = w*h*3 top-down). Frame 0 -> fcTL+IDAT; later -> fcTL+fdAT(seq||zstream). 87func apng_frame(st: *ApngState, rgb: *u8) -> i64 { 88 let fctl: *u8 = sys_mmap(26) 89 ap_be32(fctl, 0, st.seq); st.seq = st.seq + 1 90 ap_be32(fctl, 4, st.w) 91 ap_be32(fctl, 8, st.h) 92 ap_be32(fctl, 12, 0) 93 ap_be32(fctl, 16, 0) 94 ap_be16(fctl, 20, st.delay_num) 95 ap_be16(fctl, 22, st.delay_den) 96 fctl[24]=0 as u8 // dispose NONE 97 fctl[25]=0 as u8 // blend SOURCE 98 pw_chunk(st.fd, "fcTL\x00" as *u8, fctl, 26) 99 let zcap: i64 = st.h * (1 + st.w*3) + (st.h * (1 + st.w*3))/NX_MAGIC_65535*5 + 64 100 if st.frame_idx == 0 { 101 let z: *u8 = sys_mmap(zcap) 102 let zl: i64 = ap_zstream(rgb, st.w, st.h, z) 103 pw_chunk(st.fd, "IDAT\x00" as *u8, z, zl) 104 } else { 105 let zf: *u8 = sys_mmap(zcap + 8) 106 ap_be32(zf, 0, st.seq); st.seq = st.seq + 1 107 let zl2: i64 = ap_zstream(rgb, st.w, st.h, ((zf as i64)+4) as *u8) 108 pw_chunk(st.fd, "fdAT\x00" as *u8, zf, zl2 + 4) 109 } 110 st.frame_idx = st.frame_idx + 1 111 return 0 112} 113 114func apng_close(st: *ApngState) -> i64 { 115 let e: *u8 = sys_mmap(4) 116 pw_chunk(st.fd, "IEND\x00" as *u8, e, 0) 117 sys_close(st.fd) 118 return 0 119}