code wiki / _hdl_build / nx_jpeg_write.nx

nx_jpeg_write.nx source

↩ module page · 187 lines · 9068 B

1// nx_jpeg_write.nx -- SOVEREIGN baseline grayscale JFIF writer (J3). Assembles a complete, standards- 2// compliant baseline JPEG file (a real .jpg any decoder opens) from an 8-bit luma image: SOI, APP0/JFIF, 3// DQT (quant table in zigzag order), SOF0 (8-bit, 1 component), DHT (luma DC + AC standard Annex-K 4// tables), SOS, the entropy-coded scan (per-8x8-block DC-diff + AC-RLE via nx_jpeg_block_enc, final byte 5// 1-padded, 0xFF byte-stuffed), EOI. Composes the audited nx_dct8 / nx_quant_table / nx_zigzag + 6// nx_jpeg_huff_enc (J1) + nx_jpeg_block_enc (J2). W,H must be multiples of 8. JPEG/Annex K is public 7// since 1992 -> patent-clean. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_h264_bitwriter.nx" 10import "nx_dct8.nx" 11import "nx_zigzag.nx" 12import "nx_jpeg_huff_enc.nx" 13import "nx_jpeg_block_enc.nx" 14const K_MAGIC_1024: i64 = 1024 15 16func jw_b(out: *u8, off: i64, v: i64) -> i64 { out[off]=(v & 0xff) as u8; return off+1 } 17func jw_u16(out: *u8, off: i64, v: i64) -> i64 { out[off]=((v>>8)&0xff) as u8; out[off+1]=(v&0xff) as u8; return off+2 } 18func jw_qround(v: i64, q: i64) -> i64 { if v>=0 { return (v + q/2)/q } return 0 - (((0-v) + q/2)/q) } 19 20// Write a full baseline grayscale JPEG of pixels[W*H] (row-major, 0..255) into out; return byte length. 21// qt = row-major luma quant table; dcB/dcV/acB/acV = standard Annex-K BITS/HUFFVAL; dcCO/dcSI/acCO/acSI 22// = generated canonical codes; to_zz/from_zz = zigzag maps; DM/scratch/ti/to = DCT working buffers. 23func jw_write_jpeg(out: *u8, W: i64, H: i64, qt: *i64, 24 dcB: *i64, dcV: *i64, acB: *i64, acV: *i64, 25 dcCO: *i64, dcSI: *i64, acCO: *i64, acSI: *i64, 26 to_zz: *i64, from_zz: *i64, DM: *i64, scratch: *i64, ti: *i64, to: *i64, 27 pixels: *i64) -> i64 { 28 var o: i64=0 29 // SOI 30 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD8) 31 // APP0 / JFIF 32 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xE0); o=jw_u16(out,o,16) 33 o=jw_b(out,o,0x4A); o=jw_b(out,o,0x46); o=jw_b(out,o,0x49); o=jw_b(out,o,0x46); o=jw_b(out,o,0x00) 34 o=jw_b(out,o,1); o=jw_b(out,o,1); o=jw_b(out,o,0) 35 o=jw_u16(out,o,1); o=jw_u16(out,o,1); o=jw_b(out,o,0); o=jw_b(out,o,0) 36 // DQT (quant table in zigzag order) 37 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDB); o=jw_u16(out,o,67); o=jw_b(out,o,0x00) 38 var k: i64=0 39 while k<64 { o=jw_b(out,o, qt[from_zz[k]]); k=k+1 } 40 // SOF0 (baseline, 8-bit, 1 component) 41 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC0); o=jw_u16(out,o,11); o=jw_b(out,o,8) 42 o=jw_u16(out,o,H); o=jw_u16(out,o,W); o=jw_b(out,o,1) 43 o=jw_b(out,o,1); o=jw_b(out,o,0x11); o=jw_b(out,o,0) 44 // DHT - DC luma 45 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC4); o=jw_u16(out,o, 2+1+16+12); o=jw_b(out,o,0x00) 46 var i: i64=1; while i<=16 { o=jw_b(out,o, dcB[i]); i=i+1 } 47 i=0; while i<12 { o=jw_b(out,o, dcV[i]); i=i+1 } 48 // DHT - AC luma 49 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC4); o=jw_u16(out,o, 2+1+16+162); o=jw_b(out,o,0x10) 50 i=1; while i<=16 { o=jw_b(out,o, acB[i]); i=i+1 } 51 i=0; while i<162 { o=jw_b(out,o, acV[i]); i=i+1 } 52 // SOS 53 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDA); o=jw_u16(out,o,8); o=jw_b(out,o,1) 54 o=jw_b(out,o,1); o=jw_b(out,o,0x00); o=jw_b(out,o,0); o=jw_b(out,o,63); o=jw_b(out,o,0) 55 // entropy scan: encode every 8x8 block into a bit buffer 56 let ebuf: *u8=sys_mmap(W*H + K_MAGIC_1024) 57 let bw: *BitWriter=sys_mmap(64) as *BitWriter; bw_init(bw, ebuf, W*H + K_MAGIC_1024) 58 let blk: *i64=sys_mmap(64*8) as *i64 59 let sh: *i64=sys_mmap(64*8) as *i64 60 let co: *i64=sys_mmap(64*8) as *i64 61 let q: *i64=sys_mmap(64*8) as *i64 62 let zz: *i64=sys_mmap(64*8) as *i64 63 var prevdc: i64=0 64 var by: i64=0 65 while by < H/8 { 66 var bx: i64=0 67 while bx < W/8 { 68 var r: i64=0 69 while r<8 { var c: i64=0; while c<8 { sh[r*8+c]=pixels[(by*8+r)*W+(bx*8+c)]-128; c=c+1 } r=r+1 } 70 nx_dct8_forward_2d(DM, sh, co, scratch, ti, to) 71 i=0; while i<64 { q[i]=jw_qround(co[i], qt[i]); i=i+1 } 72 nx_zigzag_scan(q, to_zz, zz) 73 prevdc=jbe_encode_block(bw, dcCO, dcSI, acCO, acSI, zz, prevdc) 74 bx=bx+1 75 } 76 by=by+1 77 } 78 // pad the final partial byte with 1-bits (JPEG convention) 79 while bw.bit_pos != 0 { bw_write_bit(bw, 1) } 80 let elen: i64=bw.byte_pos 81 // copy the scan into out with 0xFF byte-stuffing 82 i=0 83 while i<elen { 84 let bb: i64=ebuf[i] as i64 85 o=jw_b(out,o,bb) 86 if bb==0xFF { o=jw_b(out,o,0x00) } 87 i=i+1 88 } 89 // EOI 90 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD9) 91 return o 92} 93 94// Flush the accumulated interval bits: 1-pad the final partial byte, copy into out 95// with 0xFF 0x00 stuffing, then re-arm the bit writer for the next interval. 96func jw_flush_interval(out: *u8, o: i64, bw: *BitWriter, ebuf: *u8, ecap: i64) -> i64 { 97 while bw.bit_pos != 0 { bw_write_bit(bw, 1) } 98 var oo: i64=o 99 var fi: i64=0 100 while fi<bw.byte_pos { 101 let bb: i64=ebuf[fi] as i64 102 oo=jw_b(out,oo,bb) 103 if bb==0xFF { oo=jw_b(out,oo,0x00) } 104 fi=fi+1 105 } 106 // BitWriter ORs set-bits into a ZEROED buffer -- re-arming over stale interval 107 // bytes would OR the next interval into the last one's data. Zero what we used. 108 var zi: i64=0 109 while zi<bw.byte_pos { ebuf[zi]=0 as u8; zi=zi+1 } 110 bw_init(bw, ebuf, ecap) 111 return oo 112} 113 114// jw_write_jpeg with restart intervals (T.81 F.2.1.3.1): ri > 0 emits a DRI segment 115// and, every ri MCUs, closes the entropy interval (1-padded, stuffed) and writes an 116// UNSTUFFED RSTn marker (n cycling 0..7), resetting the DC predictor. The marker is 117// written straight to out AFTER the interval flush precisely so the stuffing pass 118// can never mangle it. ri == 0 produces byte-identical output to jw_write_jpeg. 119func jw_write_jpeg_ri(out: *u8, W: i64, H: i64, qt: *i64, 120 dcB: *i64, dcV: *i64, acB: *i64, acV: *i64, 121 dcCO: *i64, dcSI: *i64, acCO: *i64, acSI: *i64, 122 to_zz: *i64, from_zz: *i64, DM: *i64, scratch: *i64, ti: *i64, to: *i64, 123 pixels: *i64, ri: i64) -> i64 { 124 var o: i64=0 125 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD8) 126 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xE0); o=jw_u16(out,o,16) 127 o=jw_b(out,o,0x4A); o=jw_b(out,o,0x46); o=jw_b(out,o,0x49); o=jw_b(out,o,0x46); o=jw_b(out,o,0x00) 128 o=jw_b(out,o,1); o=jw_b(out,o,1); o=jw_b(out,o,0) 129 o=jw_u16(out,o,1); o=jw_u16(out,o,1); o=jw_b(out,o,0); o=jw_b(out,o,0) 130 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDB); o=jw_u16(out,o,67); o=jw_b(out,o,0x00) 131 var k: i64=0 132 while k<64 { o=jw_b(out,o, qt[from_zz[k]]); k=k+1 } 133 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC0); o=jw_u16(out,o,11); o=jw_b(out,o,8) 134 o=jw_u16(out,o,H); o=jw_u16(out,o,W); o=jw_b(out,o,1) 135 o=jw_b(out,o,1); o=jw_b(out,o,0x11); o=jw_b(out,o,0) 136 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC4); o=jw_u16(out,o, 2+1+16+12); o=jw_b(out,o,0x00) 137 var i: i64=1; while i<=16 { o=jw_b(out,o, dcB[i]); i=i+1 } 138 i=0; while i<12 { o=jw_b(out,o, dcV[i]); i=i+1 } 139 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC4); o=jw_u16(out,o, 2+1+16+162); o=jw_b(out,o,0x10) 140 i=1; while i<=16 { o=jw_b(out,o, acB[i]); i=i+1 } 141 i=0; while i<162 { o=jw_b(out,o, acV[i]); i=i+1 } 142 if ri>0 { o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDD); o=jw_u16(out,o,4); o=jw_u16(out,o,ri) } 143 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDA); o=jw_u16(out,o,8); o=jw_b(out,o,1) 144 o=jw_b(out,o,1); o=jw_b(out,o,0x00); o=jw_b(out,o,0); o=jw_b(out,o,63); o=jw_b(out,o,0) 145 let ecap: i64=W*H + K_MAGIC_1024 146 let ebuf: *u8=sys_mmap(ecap) 147 let bw: *BitWriter=sys_mmap(64) as *BitWriter; bw_init(bw, ebuf, ecap) 148 let sh: *i64=sys_mmap(64*8) as *i64 149 let co: *i64=sys_mmap(64*8) as *i64 150 let q: *i64=sys_mmap(64*8) as *i64 151 let zz: *i64=sys_mmap(64*8) as *i64 152 var prevdc: i64=0 153 var mcus_done: i64=0 154 var ri_left: i64=ri 155 var rst_n: i64=0 156 let mcu_total: i64=(H/8)*(W/8) 157 var by: i64=0 158 while by < H/8 { 159 var bx: i64=0 160 while bx < W/8 { 161 var r: i64=0 162 while r<8 { var c: i64=0; while c<8 { sh[r*8+c]=pixels[(by*8+r)*W+(bx*8+c)]-128; c=c+1 } r=r+1 } 163 nx_dct8_forward_2d(DM, sh, co, scratch, ti, to) 164 i=0; while i<64 { q[i]=jw_qround(co[i], qt[i]); i=i+1 } 165 nx_zigzag_scan(q, to_zz, zz) 166 prevdc=jbe_encode_block(bw, dcCO, dcSI, acCO, acSI, zz, prevdc) 167 mcus_done=mcus_done+1 168 if ri>0 { 169 ri_left=ri_left-1 170 if ri_left==0 { 171 if mcus_done < mcu_total { 172 o=jw_flush_interval(out, o, bw, ebuf, ecap) 173 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD0+rst_n) 174 rst_n=rst_n+1; if rst_n==8 { rst_n=0 } 175 prevdc=0 176 } 177 ri_left=ri 178 } 179 } 180 bx=bx+1 181 } 182 by=by+1 183 } 184 o=jw_flush_interval(out, o, bw, ebuf, ecap) 185 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD9) 186 return o 187}