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}