nx_gif_decode.nx source
↩ module page · 181 lines · 7685 B
1// nx_gif_decode.nx -- sovereign GIF87a/89a decoder (LZW). Core decodes to palette INDICES with
2// de-interlace row permutation (GIF interlace = 4 passes: rows 0,8,.. / 4,12,.. / 2,6,.. / 1,3,..),
3// then two consumers: gif_decode (w*h Rec.709 grayscale -- the historic perceptual-hash contract) and
4// gif_decode_rgb (w*h*3 palette COLOR -- the chokepoint/browser path; before this, every GIF lost its
5// palette to an R=G=B expand and interlaced GIFs rendered visually scrambled). First image descriptor
6// only (animated GIF = first frame). Returns 0 on non-GIF/failure. All bindings `var` where reassigned
7// (let is immutable in nx_cc). license_tier: ORIGINAL
8import "syscalls.nx"
9const K_MAGIC_4096: i64 = 4096
10
11func gif_u16le(buf: *u8, off: i64) -> i64 { return (buf[off] as i64) | ((buf[off+1] as i64)<<8) }
12
13// Decode to palette indices in DISPLAY row order (de-interlaced when the descriptor
14// flags interlace). meta: [0]=w [1]=h [2]=ct_off [3]=ct_n [4]=interlaced. 0 on failure.
15func gif_decode_core(raw: *u8, n: i64, meta: *i64) -> *u8 {
16 if n<13 { return 0 as *u8 }
17 if (raw[0] as i64)!=0x47 { return 0 as *u8 }
18 if (raw[1] as i64)!=0x49 { return 0 as *u8 }
19 if (raw[2] as i64)!=0x46 { return 0 as *u8 }
20 let packed: i64 = raw[10] as i64
21 let gct_flag: i64 = (packed>>7)&1
22 let gct_size: i64 = packed&7
23 var pos: i64 = 13
24 var ct_off: i64 = 0
25 var ct_n: i64 = 0
26 if gct_flag==1 { ct_n = 1<<(gct_size+1); ct_off = pos; pos = pos + ct_n*3 }
27 // walk blocks to the image descriptor (skipping extensions)
28 var img_w: i64 = 0
29 var img_h: i64 = 0
30 var min_code: i64 = 0
31 var inter: i64 = 0
32 var found: i64 = 0
33 var go: i64 = 1
34 while go==1 {
35 if pos>=n { go=0 } else {
36 let b: i64 = raw[pos] as i64
37 if b==0x3b { go=0 } else {
38 if b==0x21 {
39 pos = pos+2
40 var sg: i64 = 1
41 while sg==1 { if pos<n { let bl: i64 = raw[pos] as i64; pos=pos+1; if bl==0 { sg=0 } else { pos=pos+bl } } else { sg=0 } }
42 } else {
43 if b==0x2c {
44 img_w = gif_u16le(raw, pos+5)
45 img_h = gif_u16le(raw, pos+7)
46 let ip: i64 = raw[pos+9] as i64
47 let lct_flag: i64 = (ip>>7)&1
48 let lct_sz: i64 = ip&7
49 inter = (ip>>6)&1
50 pos = pos+10
51 if lct_flag==1 { ct_n = 1<<(lct_sz+1); ct_off = pos; pos = pos + ct_n*3 }
52 min_code = raw[pos] as i64
53 pos = pos+1
54 found = 1; go = 0
55 } else { go=0 } } }
56 }
57 }
58 if found==0 { return 0 as *u8 }
59 if img_w<=0 { return 0 as *u8 }
60 if img_h<=0 { return 0 as *u8 }
61 // gather LZW sub-block data into one buffer
62 let lzw: *u8 = sys_mmap(n+16)
63 var ll: i64 = 0
64 var sg2: i64 = 1
65 while sg2==1 { if pos<n { let bl: i64 = raw[pos] as i64; pos=pos+1; if bl==0 { sg2=0 } else { var c: i64 = 0; while c<bl { if pos+c<n { lzw[ll]=raw[pos+c]; ll=ll+1 } c=c+1 } pos=pos+bl } } else { sg2=0 } }
66 // LZW decode -> palette indices
67 let clear: i64 = 1<<min_code
68 let eoi: i64 = clear+1
69 let prefix: *i64 = sys_mmap(K_MAGIC_4096*8) as *i64
70 let suffix: *u8 = sys_mmap(K_MAGIC_4096)
71 let stack: *u8 = sys_mmap(K_MAGIC_4096)
72 let npix: i64 = img_w*img_h
73 let idx: *u8 = sys_mmap(npix+16)
74 var outp: i64 = 0
75 var i: i64 = 0
76 while i<clear { prefix[i]=0-1; suffix[i]=i as u8; i=i+1 }
77 var next_code: i64 = eoi+1
78 var width: i64 = min_code+1
79 var maxc: i64 = 1<<width
80 var bitbuf: i64 = 0
81 var bitcnt: i64 = 0
82 var bp: i64 = 0
83 var prev: i64 = 0-1
84 var dgo: i64 = 1
85 while dgo==1 {
86 var lack: i64 = 0
87 while bitcnt<width { if bp<ll { bitbuf = bitbuf | ((lzw[bp] as i64)<<bitcnt); bp=bp+1; bitcnt=bitcnt+8 } else { lack=1; bitcnt=width } }
88 if lack==1 { dgo=0 } else {
89 let code: i64 = bitbuf & (maxc-1)
90 bitbuf = bitbuf>>width
91 bitcnt = bitcnt-width
92 if code==eoi { dgo=0 } else {
93 if code==clear { next_code=eoi+1; width=min_code+1; maxc=1<<width; prev=0-1 } else {
94 var k: i64 = code
95 var extra: i64 = 0
96 if code>=next_code { k=prev; extra=1 }
97 var sp: i64 = 0
98 var w2: i64 = k
99 var guard: i64 = 0
100 while w2 != (0-1) { stack[sp]=suffix[w2]; sp=sp+1; w2=prefix[w2]; guard=guard+1; if guard>K_MAGIC_4096 { w2=0-1 } }
101 var firstc: i64 = 0
102 if sp>0 { firstc=stack[sp-1] as i64 }
103 while sp>0 { sp=sp-1; if outp<npix { idx[outp]=stack[sp]; outp=outp+1 } }
104 if extra==1 { if outp<npix { idx[outp]=firstc as u8; outp=outp+1 } }
105 if prev != (0-1) { if next_code<K_MAGIC_4096 {
106 prefix[next_code]=prev; suffix[next_code]=firstc as u8; next_code=next_code+1
107 if next_code>=maxc { if width<12 { width=width+1; maxc=1<<width } }
108 } }
109 prev=code
110 } }
111 }
112 }
113 // de-interlace: rows were produced in pass order; permute into display order.
114 var outbuf: *u8 = idx
115 if inter==1 {
116 let didx: *u8 = sys_mmap(npix+16)
117 var rr: i64 = 0
118 var y1: i64 = 0
119 while y1<img_h { var c1: i64=0; while c1<img_w { didx[y1*img_w+c1]=idx[rr*img_w+c1]; c1=c1+1 } rr=rr+1; y1=y1+8 }
120 var y2: i64 = 4
121 while y2<img_h { var c2: i64=0; while c2<img_w { didx[y2*img_w+c2]=idx[rr*img_w+c2]; c2=c2+1 } rr=rr+1; y2=y2+8 }
122 var y3: i64 = 2
123 while y3<img_h { var c3: i64=0; while c3<img_w { didx[y3*img_w+c3]=idx[rr*img_w+c3]; c3=c3+1 } rr=rr+1; y3=y3+4 }
124 var y4: i64 = 1
125 while y4<img_h { var c4: i64=0; while c4<img_w { didx[y4*img_w+c4]=idx[rr*img_w+c4]; c4=c4+1 } rr=rr+1; y4=y4+2 }
126 outbuf = didx
127 }
128 meta[0]=img_w; meta[1]=img_h; meta[2]=ct_off; meta[3]=ct_n; meta[4]=inter
129 return outbuf
130}
131
132// Historic contract: w*h Rec.709 grayscale (perceptual-hash consumers).
133func gif_decode(raw: *u8, n: i64, out_wh: *i64) -> *u8 {
134 let meta: *i64 = sys_mmap(48) as *i64
135 let idxb: *u8 = gif_decode_core(raw, n, meta)
136 if idxb==(0 as *u8) { return 0 as *u8 }
137 let w: i64=meta[0]
138 let h: i64=meta[1]
139 let cto: i64=meta[2]
140 let ctn: i64=meta[3]
141 let npix: i64=w*h
142 let gray: *u8 = sys_mmap(npix+16)
143 var p: i64 = 0
144 while p<npix {
145 let ci: i64 = idxb[p] as i64
146 var gv: i64 = 0
147 if ctn>0 { let co: i64 = cto + ci*3; if co+2<n { let r: i64=raw[co] as i64; let g: i64=raw[co+1] as i64; let bb: i64=raw[co+2] as i64; gv=(54*r+183*g+19*bb)>>8 } }
148 if gv>255 { gv=255 }
149 gray[p]=gv as u8
150 p=p+1
151 }
152 out_wh[0]=w; out_wh[1]=h
153 return gray
154}
155
156// Palette COLOR decode: w*h*3 packed RGB in display row order (the chokepoint path).
157func gif_decode_rgb(raw: *u8, n: i64, out_wh: *i64) -> *u8 {
158 let meta: *i64 = sys_mmap(48) as *i64
159 let idxb: *u8 = gif_decode_core(raw, n, meta)
160 if idxb==(0 as *u8) { return 0 as *u8 }
161 let w: i64=meta[0]
162 let h: i64=meta[1]
163 let cto: i64=meta[2]
164 let ctn: i64=meta[3]
165 let npix: i64=w*h
166 let rgb: *u8 = sys_mmap(npix*3+16)
167 var p: i64 = 0
168 while p<npix {
169 let ci: i64 = idxb[p] as i64
170 var rv: i64 = 0
171 var gv: i64 = 0
172 var bv: i64 = 0
173 if ctn>0 { let co: i64 = cto + ci*3; if co+2<n { rv=raw[co] as i64; gv=raw[co+1] as i64; bv=raw[co+2] as i64 } }
174 rgb[p*3]=rv as u8
175 rgb[p*3+1]=gv as u8
176 rgb[p*3+2]=bv as u8
177 p=p+1
178 }
179 out_wh[0]=w; out_wh[1]=h
180 return rgb
181}