code wiki / _hdl_build / nx_jpeg_color_write.nx

nx_jpeg_color_write.nx source

↩ module page · 115 lines · 6960 B

1// nx_jpeg_color_write.nx -- SOVEREIGN baseline COLOR (YCbCr 4:4:4) JFIF writer. Extends the grayscale J3 2// writer (nx_jpeg_write) to 3 components so photo thumbnails encode as real .jpg (far smaller than lossless 3// PNG). 2 DQT (luma id0 + chroma id1, via nx_qt_luma/nx_qt_chroma), 3-component SOF0/SOS, interleaved MCUs 4// (one 8x8 block per component, no chroma subsampling). REUSES (DRY): jw_b/jw_u16/jw_qround + nx_dct8 + 5// nx_zigzag + nx_jpeg_block_enc + nx_jpeg_huff_enc, sharing the LUMA DC/AC Huffman tables across all three 6// components (valid baseline -- table choice is the encoder's; the chroma compression win is the quant table). 7// RGB->YCbCr is the standard 8-bit-integer fast transform (NO floats). W,H must be multiples of 8. 8// JPEG/Annex K public since 1992 -> patent-clean. license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_h264_bitwriter.nx" 11import "nx_dct8.nx" 12import "nx_quant_table.nx" 13import "nx_zigzag.nx" 14import "nx_jpeg_huff_enc.nx" 15import "nx_jpeg_block_enc.nx" 16import "nx_jpeg_write.nx" // reuse jw_b / jw_u16 / jw_qround 17const K_MAGIC_4096: i64 = 4096 18 19// integer RGB->YCbCr (8-bit coefficients; sums: Y 77+150+29=256, Cb/Cr center on 128). clamps to [0,255]. 20func jcw_y(r: i64, g: i64, b: i64) -> i64 { var v: i64 = (77*r + 150*g + 29*b) >> 8; if v < 0 { v = 0 } if v > 255 { v = 255 } return v } 21func jcw_cb(r: i64, g: i64, b: i64) -> i64 { var v: i64 = (((0-43)*r - 85*g + 128*b) >> 8) + 128; if v < 0 { v = 0 } if v > 255 { v = 255 } return v } 22func jcw_cr(r: i64, g: i64, b: i64) -> i64 { var v: i64 = ((128*r - 107*g - 21*b) >> 8) + 128; if v < 0 { v = 0 } if v > 255 { v = 255 } return v } 23 24// encode one 8x8 block of `plane` at block (bx,by): level-shift, forward DCT, quantize with qt, zigzag, 25// entropy-encode (DC-diff vs prevdc + AC-RLE). returns the new prevdc. Uses caller-shared scratch buffers. 26func jcw_block(bw: *BitWriter, plane: *i64, W: i64, bx: i64, by: i64, qt: *i64, 27 dcCO: *i64, dcSI: *i64, acCO: *i64, acSI: *i64, to_zz: *i64, 28 DM: *i64, scratch: *i64, ti: *i64, to: *i64, 29 sh: *i64, co: *i64, q: *i64, zz: *i64, prevdc: i64) -> i64 { 30 var r: i64 = 0 31 while r < 8 { var c: i64 = 0; while c < 8 { sh[r*8+c] = plane[(by*8+r)*W + (bx*8+c)] - 128; c = c + 1 } r = r + 1 } 32 nx_dct8_forward_2d(DM, sh, co, scratch, ti, to) 33 var i: i64 = 0; while i < 64 { q[i] = jw_qround(co[i], qt[i]); i = i + 1 } 34 nx_zigzag_scan(q, to_zz, zz) 35 return jbe_encode_block(bw, dcCO, dcSI, acCO, acSI, zz, prevdc) 36} 37 38// Encode an interleaved RGB image (rgb[3*W*H], row-major, 0..255) into a baseline color JPEG in `out`. 39// Returns the JPEG byte length. dcB/dcV/acB/acV = Annex-K luma BITS/HUFFVAL (for the DHT markers); 40// dcCO/dcSI/acCO/acSI = generated canonical codes; qtL/qtC = luma/chroma quant tables; the rest = DCT/zigzag bufs. 41func jcw_write_color(out: *u8, W: i64, H: i64, rgb: *u8, 42 qtL: *i64, qtC: *i64, 43 dcB: *i64, dcV: *i64, acB: *i64, acV: *i64, 44 dcCO: *i64, dcSI: *i64, acCO: *i64, acSI: *i64, 45 to_zz: *i64, from_zz: *i64, DM: *i64, scratch: *i64, ti: *i64, to: *i64) -> i64 { 46 let npx: i64 = W * H 47 let yP: *i64 = sys_mmap(npx*8) as *i64 48 let cbP: *i64 = sys_mmap(npx*8) as *i64 49 let crP: *i64 = sys_mmap(npx*8) as *i64 50 var p: i64 = 0 51 while p < npx { 52 let r: i64 = rgb[p*3] as i64; let g: i64 = rgb[p*3+1] as i64; let b: i64 = rgb[p*3+2] as i64 53 yP[p] = jcw_y(r,g,b); cbP[p] = jcw_cb(r,g,b); crP[p] = jcw_cr(r,g,b) 54 p = p + 1 55 } 56 var o: i64 = 0 57 // SOI 58 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD8) 59 // APP0 / JFIF 60 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xE0); o=jw_u16(out,o,16) 61 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) 62 o=jw_b(out,o,1); o=jw_b(out,o,1); o=jw_b(out,o,0); o=jw_u16(out,o,1); o=jw_u16(out,o,1); o=jw_b(out,o,0); o=jw_b(out,o,0) 63 // DQT 0 (luma) + DQT 1 (chroma), each in zigzag order 64 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDB); o=jw_u16(out,o,67); o=jw_b(out,o,0x00) 65 var k: i64=0; while k<64 { o=jw_b(out,o, qtL[from_zz[k]]); k=k+1 } 66 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDB); o=jw_u16(out,o,67); o=jw_b(out,o,0x01) 67 k=0; while k<64 { o=jw_b(out,o, qtC[from_zz[k]]); k=k+1 } 68 // SOF0 baseline 8-bit, 3 components (len = 8 + 3*3 = 17) 69 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xC0); o=jw_u16(out,o,17); o=jw_b(out,o,8) 70 o=jw_u16(out,o,H); o=jw_u16(out,o,W); o=jw_b(out,o,3) 71 o=jw_b(out,o,1); o=jw_b(out,o,0x11); o=jw_b(out,o,0) // Y : id1, 1x1 sampling, quant table 0 72 o=jw_b(out,o,2); o=jw_b(out,o,0x11); o=jw_b(out,o,1) // Cb : id2, quant table 1 73 o=jw_b(out,o,3); o=jw_b(out,o,0x11); o=jw_b(out,o,1) // Cr : id3, quant table 1 74 // DHT: DC luma (class0 id0) + AC luma (class1 id0) -- shared by all 3 components 75 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) 76 var i: i64=1; while i<=16 { o=jw_b(out,o, dcB[i]); i=i+1 } 77 i=0; while i<12 { o=jw_b(out,o, dcV[i]); i=i+1 } 78 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) 79 i=1; while i<=16 { o=jw_b(out,o, acB[i]); i=i+1 } 80 i=0; while i<162 { o=jw_b(out,o, acV[i]); i=i+1 } 81 // SOS: 3 components, all selecting DC table 0 + AC table 0 (len = 6 + 2*3 = 12) 82 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xDA); o=jw_u16(out,o,12); o=jw_b(out,o,3) 83 o=jw_b(out,o,1); o=jw_b(out,o,0x00) // Y -> DC0/AC0 84 o=jw_b(out,o,2); o=jw_b(out,o,0x00) // Cb -> DC0/AC0 85 o=jw_b(out,o,3); o=jw_b(out,o,0x00) // Cr -> DC0/AC0 86 o=jw_b(out,o,0); o=jw_b(out,o,63); o=jw_b(out,o,0) 87 // interleaved entropy scan (4:4:4): per MCU encode Y, Cb, Cr (3 separate DC predictors) 88 let ebuf: *u8 = sys_mmap(npx*3 + K_MAGIC_4096) 89 let bw: *BitWriter = sys_mmap(64) as *BitWriter; bw_init(bw, ebuf, npx*3 + K_MAGIC_4096) 90 let sh: *i64=sys_mmap(64*8) as *i64; let co: *i64=sys_mmap(64*8) as *i64 91 let q: *i64=sys_mmap(64*8) as *i64; let zz: *i64=sys_mmap(64*8) as *i64 92 var pdY: i64=0; var pdCb: i64=0; var pdCr: i64=0 93 var by: i64=0 94 while by < H/8 { 95 var bx: i64=0 96 while bx < W/8 { 97 pdY = jcw_block(bw, yP, W, bx, by, qtL, dcCO,dcSI,acCO,acSI, to_zz, DM, scratch, ti, to, sh, co, q, zz, pdY) 98 pdCb = jcw_block(bw, cbP, W, bx, by, qtC, dcCO,dcSI,acCO,acSI, to_zz, DM, scratch, ti, to, sh, co, q, zz, pdCb) 99 pdCr = jcw_block(bw, crP, W, bx, by, qtC, dcCO,dcSI,acCO,acSI, to_zz, DM, scratch, ti, to, sh, co, q, zz, pdCr) 100 bx = bx + 1 101 } 102 by = by + 1 103 } 104 while bw.bit_pos != 0 { bw_write_bit(bw, 1) } 105 let elen: i64 = bw.byte_pos 106 i = 0 107 while i < elen { 108 let bb: i64 = ebuf[i] as i64 109 o = jw_b(out, o, bb) 110 if bb == 0xFF { o = jw_b(out, o, 0x00) } 111 i = i + 1 112 } 113 o=jw_b(out,o,0xFF); o=jw_b(out,o,0xD9) // EOI 114 return o 115}