code wiki / (root) / nx_apng_decode.nx

nx_apng_decode.nx source

↩ module page · 86 lines · 4611 B

1// nx_apng_decode.nx -- INGEST animated PNG -> frames (roadmap R1: video/gif/image ingest, the front door of the 2// video->pose->motion keystone). Reuses the sovereign nx_png_decode (inflate + unfilter) per frame: walk the 3// APNG chunks, and for each frame's IDAT (frame 0) / fdAT (frames 1+) reconstruct a standalone single-frame PNG 4// (sig+IHDR+IDAT+IEND) and decode it. Our encoder nx_apng writes exactly one IDAT/fdAT per full-frame, so the 5// split is 1:1. Bit-exact inverse of nx_apng. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_png.nx" // png_be32 / png_crc32 8import "nx_png_decoder.nx" // nx_png_decode -> *NxPngResult 9const K_MAGIC_1024: i64 = 1024 10const K_MAGIC_65536: i64 = 65536 11 12func ad_be32(buf: *u8, o: i64) -> i64 { return ((buf[o]&0xff)<<24)|((buf[o+1]&0xff)<<16)|((buf[o+2]&0xff)<<8)|(buf[o+3]&0xff) } 13func ad_type(buf: *u8, o: i64, a: i64, b: i64, c: i64, d: i64) -> i64 { if (buf[o]&0xff)==a { if (buf[o+1]&0xff)==b { if (buf[o+2]&0xff)==c { if (buf[o+3]&0xff)==d { return 1 } } } } return 0 } 14 15// reconstruct a standalone single-frame PNG (sig+IHDR+IDAT[zlib]+IEND) into out; returns byte length. 16func ad_build_png(ihdr13: *u8, zlib: *u8, zlen: i64, out: *u8) -> i64 { 17 out[0]=137 as u8; out[1]=80 as u8; out[2]=78 as u8; out[3]=71 as u8 18 out[4]=13 as u8; out[5]=10 as u8; out[6]=26 as u8; out[7]=10 as u8 19 var o: i64 = 8 20 png_be32(out, o, 13); o = o + 4 21 let ih: i64 = o 22 out[o]=73 as u8; out[o+1]=72 as u8; out[o+2]=68 as u8; out[o+3]=82 as u8; o = o + 4 // IHDR 23 var i: i64 = 0 24 while i < 13 { out[o+i] = ihdr13[i]; i = i + 1 } 25 o = o + 13 26 png_be32(out, o, png_crc32(out, ih, 17)); o = o + 4 27 png_be32(out, o, zlen); o = o + 4 28 let id: i64 = o 29 out[o]=73 as u8; out[o+1]=68 as u8; out[o+2]=65 as u8; out[o+3]=84 as u8; o = o + 4 // IDAT 30 var j: i64 = 0 31 while j < zlen { out[o+j] = zlib[j]; j = j + 1 } 32 o = o + zlen 33 png_be32(out, o, png_crc32(out, id, 4 + zlen)); o = o + 4 34 png_be32(out, o, 0); o = o + 4 35 let ie: i64 = o 36 out[o]=73 as u8; out[o+1]=69 as u8; out[o+2]=78 as u8; out[o+3]=68 as u8; o = o + 4 // IEND 37 png_be32(out, o, png_crc32(out, ie, 4)); o = o + 4 38 return o 39} 40 41// decode an APNG buffer into frames_out (packed R+G<<8+B<<16, w*h per frame, nframes stacked). wh_out[0]=w 42// wh_out[1]=h wh_out[2]=nframes. Returns nframes, or -1 on error. cap_frames = max frames the caller allocated. 43func apng_decode(buf: *u8, len: i64, frames_out: *i64, wh_out: *i64, cap_frames: i64) -> i64 { 44 if len < 8 { return 0 - 1 } 45 if (buf[0]&0xff) != 137 { return 0 - 1 } 46 let ihdr13: *u8 = sys_mmap(16) 47 var w: i64 = 0; var h: i64 = 0 48 var have_ihdr: i64 = 0 49 var declared: i64 = 0 50 let recon: *u8 = sys_mmap(8 * K_MAGIC_1024 * K_MAGIC_1024) 51 var nf: i64 = 0 52 var p: i64 = 8 // past the signature 53 while p + 8 <= len { 54 let clen: i64 = ad_be32(buf, p) 55 let dat: i64 = p + 8 // chunk data offset 56 if ad_type(buf, p + 4, 73, 72, 68, 82) == 1 { // IHDR 57 var k: i64 = 0 58 while k < 13 { ihdr13[k] = buf[dat + k]; k = k + 1 } 59 w = ad_be32(buf, dat); h = ad_be32(buf, dat + 4); have_ihdr = 1 60 } 61 if ad_type(buf, p + 4, 97, 99, 84, 76) == 1 { declared = ad_be32(buf, dat) } // acTL num_frames 62 var zoff: i64 = 0 - 1; var zlen: i64 = 0 63 if ad_type(buf, p + 4, 73, 68, 65, 84) == 1 { zoff = dat; zlen = clen } // IDAT (frame 0 zlib) 64 if ad_type(buf, p + 4, 102, 100, 65, 84) == 1 { zoff = dat + 4; zlen = clen - 4 } // fdAT: skip 4-byte seq 65 if zoff >= 0 { if have_ihdr == 1 { if nf < cap_frames { 66 let plen: i64 = ad_build_png(ihdr13, (buf as i64 + zoff) as *u8, zlen, recon) 67 let r: *NxPngResult = nx_png_decode(recon, plen) 68 if r.error_code == 0 { 69 let px: *u8 = r.pixels 70 let nch: i64 = r.n_channels 71 let fb: i64 = (frames_out as i64) + nf * w * h * 8 72 var q: i64 = 0 73 while q < w * h { 74 let rr: i64 = px[q*nch]&0xff; let gg: i64 = px[q*nch+1]&0xff; let bb: i64 = px[q*nch+2]&0xff 75 let fp: *i64 = (fb + q * 8) as *i64 76 fp[0] = rr + gg * 256 + bb * K_MAGIC_65536 77 q = q + 1 78 } 79 nf = nf + 1 80 } 81 } } } 82 p = dat + clen + 4 // next chunk: skip data + crc 83 } 84 wh_out[0] = w; wh_out[1] = h; wh_out[2] = nf 85 return nf 86}