code wiki / (root) / nx_gif_decode.nx

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}