code wiki / (root) / nx_jpeg_progressive.nx

nx_jpeg_progressive.nx source

↩ module page · 401 lines · 19088 B

1// nx_jpeg_progressive.nx -- PROGRESSIVE JPEG decode (SOF2), the eat-the-debt unblock for real web photos. 2// Baseline decodes one scan block->IDCT->pixel; progressive has MANY scans that each refine a spectral band of 3// a full-image COEFFICIENT buffer (spectral selection Ss..Se + successive approximation Ah/Al), then a single 4// IDCT pass at the end. Four scan kinds: DC-first, DC-refine, AC-first (EOBRUN), AC-refine (the fiddly one). 5// Faithful to T.81 G.1.2 / libjpeg jdphuff. Restart intervals (DRI>0) NOT yet handled (assumes DRI=0). ORIGINAL. 6import "nx_syscalls.nx" 7import "nx_jpeg_sof.nx" 8import "nx_jpeg_sos.nx" 9import "nx_jpeg_dqt.nx" 10import "nx_jpeg_dht.nx" 11import "nx_jpeg_entropy.nx" 12import "nx_jpeg_dequant.nx" 13import "nx_jpeg_idct.nx" 14import "nx_jpeg_mcu.nx" 15import "nx_jpeg_decode.nx" 16 17const NX_JPEG_PROG_OK: i64 = 0 18const NX_JPEG_PROG_FAIL: i64 = 1 19 20func pgw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 } 21func pgd(v: i64) -> i64 { 22 let b: *u8 = sys_mmap(32) as *u8 23 b[31] = 32 as u8 24 var x: i64 = v; var neg: i64 = 0; var i: i64 = 30 25 if x < 0 { neg = 1; x = 0 - x } 26 if x == 0 { b[i] = 48 as u8; i = i - 1 } 27 while x > 0 { b[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 } 28 if neg == 1 { b[i] = 45 as u8; i = i - 1 } 29 sys_write(2, (b as i64 + i + 1) as *u8, 31 - i) 30 return 0 31} 32 33// does this JPEG use progressive (SOF2) coding? scans markers for a SOF; returns 1 for SOF2, 0 otherwise. 34func nx_jpeg_is_progressive(jpeg: *u8, jpeg_len: i64) -> i64 { 35 var p: i64 = 2 36 while p + 3 < jpeg_len { 37 if (jpeg[p] & 0xff) == 0xFF { 38 let m: i64 = jpeg[p + 1] & 0xff 39 if m == 0xC2 { return 1 } // SOF2 progressive 40 if m == 0xC0 { return 0 } // SOF0 baseline 41 if m == 0xC1 { return 0 } // SOF1 extended-sequential 42 if m == 0xD8 { p = p + 2 } else { 43 if m == 0xD9 { return 0 } else { 44 if m == 0x01 { p = p + 2 } else { 45 if m >= 0xD0 { if m <= 0xD7 { p = p + 2 } else { p = p + 2 + prog_be16(jpeg, p + 2) } } else { p = p + 2 + prog_be16(jpeg, p + 2) } 46 } 47 } 48 } 49 } else { p = p + 1 } 50 } 51 return 0 52} 53 54// find the next REAL marker from `from` (skip stuffed FF00, fill FFs, and RSTn which are entropy). Returns the 55// offset of the 0xFF, or len if none. 56func prog_next_marker(j: *u8, len: i64, from: i64) -> i64 { 57 var i: i64 = from 58 while i < len - 1 { 59 if (j[i] & 0xff) == 0xFF { 60 let m: i64 = j[i + 1] & 0xff 61 if m != 0x00 { 62 if m == 0xFF { i = i + 1 } else { 63 if m >= 0xD0 { if m <= 0xD7 { i = i + 2 } else { return i } } else { return i } 64 } 65 } else { i = i + 2 } 66 } else { i = i + 1 } 67 } 68 return len 69} 70// apply one successive-approximation correction bit to an already-nonzero coefficient (AC refine + DC refine). 71func prog_refine_nz(coeffs: *i64, k: i64, p1: i64, bit: i64) -> i64 { 72 let c: i64 = coeffs[k] 73 if c == 0 { return 0 } 74 if bit == 0 { return 0 } 75 if (c & p1) != 0 { return 0 } // correction bit already applied 76 if c > 0 { coeffs[k] = c + p1 } else { coeffs[k] = c - p1 } 77 return 0 78} 79 80// Decode ONE progressive scan into the coefficient buffers. cf[comp] = coeff buffer; bwp[comp]/bhp[comp] = 81// padded block grid (stride bwp); cbw[comp]/cbh[comp] = the component's OWN block extent (non-interleaved AC). 82func prog_scan(j: *u8, len: i64, scan: *NxJpegScan, frame: *NxJpegFrame, 83 cf: *i64, bwp: *i64, cbw: *i64, cbh: *i64, prevdc: *i64, 84 dcslot: *i64, acslot: *i64, 85 byteidx: *i64, ent_start: i64, ent_end: i64, 86 mcus_x: i64, mcus_y: i64) -> i64 { 87 let bo: *i64 = sys_mmap(8) as *i64 88 let bi: *i64 = sys_mmap(8) as *i64 89 bo[0] = 0 90 bi[0] = ent_start 91 let ss: i64 = scan.ss 92 let se: i64 = scan.se 93 let ah: i64 = scan.ah 94 let al: i64 = scan.al 95 let eobrun: *i64 = sys_mmap(8) as *i64 96 eobrun[0] = 0 97 if ss == 0 { 98 // ---- DC scan (interleaved over all scan components, MCU order) ---- 99 var my: i64 = 0 100 while my < mcus_y { 101 var mx: i64 = 0 102 while mx < mcus_x { 103 var s: i64 = 0 104 while s < scan.n_components { 105 let sc: *NxJpegSosComponent = (scan.components as i64 + s * NX_JPEG_SOS_COMP_BYTES) as *NxJpegSosComponent 106 // map scan component -> frame component index by csj==ci 107 var fcidx: i64 = 0 108 var fi: i64 = 0 109 while fi < frame.n_components { 110 let fcp: *NxJpegSofComponent = (frame.components as i64 + fi * NX_JPEG_SOF_COMP_BYTES) as *NxJpegSofComponent 111 if fcp.ci == sc.csj { fcidx = fi } 112 fi = fi + 1 113 } 114 let fc: *NxJpegSofComponent = (frame.components as i64 + fcidx * NX_JPEG_SOF_COMP_BYTES) as *NxJpegSofComponent 115 let stride: i64 = bwp[fcidx] 116 let cbuf: i64 = cf[fcidx] 117 var v: i64 = 0 118 while v < fc.vi { 119 var hh: i64 = 0 120 while hh < fc.hi { 121 let bx: i64 = mx * fc.hi + hh 122 let by: i64 = my * fc.vi + v 123 let blk: *i64 = (cbuf + (by * stride + bx) * 64 * 8) as *i64 124 if ah == 0 { 125 let t: i64 = nx_jpeg_huff_decode_symbol((dcslot[sc.td]) as *NxJpegHTable, j, bo, bi, ent_end) 126 if t < 0 { return NX_JPEG_PROG_FAIL } 127 var diff: i64 = 0 128 if t > 0 { diff = nx_jpeg_ent_extend(nx_jpeg_ent_receive(t, j, bo, bi, ent_end), t) } 129 prevdc[fcidx] = prevdc[fcidx] + diff 130 blk[0] = prevdc[fcidx] << al 131 } else { 132 let bit: i64 = nx_jpeg_ent_receive(1, j, bo, bi, ent_end) 133 if bit == 1 { blk[0] = blk[0] | (1 << al) } 134 } 135 hh = hh + 1 136 } 137 v = v + 1 138 } 139 s = s + 1 140 } 141 mx = mx + 1 142 } 143 my = my + 1 144 } 145 byteidx[0] = bi[0] 146 return NX_JPEG_PROG_OK 147 } 148 // ---- AC scan (non-interleaved: exactly one component) ---- 149 let sc0: *NxJpegSosComponent = scan.components 150 var fcidx: i64 = 0 151 var fi: i64 = 0 152 while fi < frame.n_components { 153 let fcp: *NxJpegSofComponent = (frame.components as i64 + fi * NX_JPEG_SOF_COMP_BYTES) as *NxJpegSofComponent 154 if fcp.ci == sc0.csj { fcidx = fi } 155 fi = fi + 1 156 } 157 let stride: i64 = bwp[fcidx] 158 let cbuf: i64 = cf[fcidx] 159 let ac: *NxJpegHTable = (acslot[sc0.ta]) as *NxJpegHTable 160 let p1: i64 = 1 << al 161 let m1: i64 = (0 - 1) << al 162 var by: i64 = 0 163 while by < cbh[fcidx] { 164 var bx: i64 = 0 165 while bx < cbw[fcidx] { 166 let blk: *i64 = (cbuf + (by * stride + bx) * 64 * 8) as *i64 167 if ah == 0 { 168 // AC first pass 169 if eobrun[0] > 0 { eobrun[0] = eobrun[0] - 1 } else { 170 var k: i64 = ss 171 var run: i64 = 1 172 while run == 1 { 173 if k > se { run = 0 } else { 174 let rs: i64 = nx_jpeg_huff_decode_symbol(ac, j, bo, bi, ent_end) 175 if rs < 0 { return NX_JPEG_PROG_FAIL } 176 let r: i64 = rs >> 4 177 let sz: i64 = rs & 0x0F 178 if sz == 0 { 179 if r == 15 { k = k + 16 } else { 180 var eb: i64 = 1 << r 181 if r > 0 { eb = eb + nx_jpeg_ent_receive(r, j, bo, bi, ent_end) } 182 eobrun[0] = eb - 1 183 run = 0 184 } 185 } else { 186 k = k + r 187 if k > se { run = 0 } else { 188 blk[k] = nx_jpeg_ent_extend(nx_jpeg_ent_receive(sz, j, bo, bi, ent_end), sz) << al 189 k = k + 1 190 } 191 } 192 } 193 } 194 } 195 } else { 196 // AC refinement pass (T.81 G.1.2.3) 197 var k: i64 = ss 198 if eobrun[0] == 0 { 199 var run: i64 = 1 200 while run == 1 { 201 if k > se { run = 0 } else { 202 let rs: i64 = nx_jpeg_huff_decode_symbol(ac, j, bo, bi, ent_end) 203 if rs < 0 { return NX_JPEG_PROG_FAIL } 204 var r: i64 = rs >> 4 205 let sz: i64 = rs & 0x0F 206 var newval: i64 = 0 207 if sz == 0 { 208 if r != 15 { 209 var eb: i64 = 1 << r 210 if r > 0 { eb = eb + nx_jpeg_ent_receive(r, j, bo, bi, ent_end) } 211 eobrun[0] = eb 212 run = 0 213 } 214 // r==15 (ZRL): skip 16 zero coeffs (with correction of nonzeros between), newval stays 0 215 } else { 216 if nx_jpeg_ent_receive(1, j, bo, bi, ent_end) == 1 { newval = p1 } else { newval = m1 } 217 } 218 if run == 1 { 219 // advance over r ZERO coefficients, applying correction bits to nonzeros along the way 220 var adv: i64 = 1 221 while adv == 1 { 222 if k > se { adv = 0; run = 0 } else { 223 if blk[k] != 0 { 224 prog_refine_nz(blk, k, p1, nx_jpeg_ent_receive(1, j, bo, bi, ent_end)) 225 } else { 226 if r == 0 { adv = 0 } else { r = r - 1 } 227 } 228 if adv == 1 { k = k + 1 } 229 } 230 } 231 if run == 1 { 232 if sz != 0 { blk[k] = newval } 233 k = k + 1 234 } 235 } 236 } 237 } 238 } 239 if eobrun[0] > 0 { 240 // correction bits for all remaining nonzero coeffs in [k..se] 241 while k <= se { 242 if blk[k] != 0 { prog_refine_nz(blk, k, p1, nx_jpeg_ent_receive(1, j, bo, bi, ent_end)) } 243 k = k + 1 244 } 245 eobrun[0] = eobrun[0] - 1 246 } 247 } 248 bx = bx + 1 249 } 250 by = by + 1 251 } 252 byteidx[0] = bi[0] 253 return NX_JPEG_PROG_OK 254} 255 256func prog_be16(j: *u8, o: i64) -> i64 { return ((j[o] & 0xff) << 8) | (j[o + 1] & 0xff) } 257 258// TOP-LEVEL progressive decode: walk markers, decode every scan into coeff buffers, then a single IDCT pass 259// into the caller's planes (same layout the baseline back-end + upsample/colour-convert expect). ctx.frame is 260// already parsed (by the SOF probe). planes[c]/strides[c] are the MCU-padded component planes. 261func nx_jpeg_prog_decode(jpeg: *u8, jpeg_len: i64, ctx: *NxJpegDecCtx, planes: *u8, strides: *i64) -> i64 { 262 let frame: *NxJpegFrame = ctx.frame 263 let W: i64 = frame.width 264 let H: i64 = frame.height 265 let maxh: i64 = frame.max_h 266 let maxv: i64 = frame.max_v 267 let mcus_x: i64 = (W + 8 * maxh - 1) / (8 * maxh) 268 let mcus_y: i64 = (H + 8 * maxv - 1) / (8 * maxv) 269 let nf: i64 = frame.n_components 270 let cf: *i64 = sys_mmap(8 * nf) as *i64 271 let bwp: *i64 = sys_mmap(8 * nf) as *i64 272 let bhp: *i64 = sys_mmap(8 * nf) as *i64 273 let cbw: *i64 = sys_mmap(8 * nf) as *i64 274 let cbh: *i64 = sys_mmap(8 * nf) as *i64 275 let prevdc: *i64 = sys_mmap(8 * nf) as *i64 276 var c: i64 = 0 277 while c < nf { 278 let fc: *NxJpegSofComponent = (frame.components as i64 + c * NX_JPEG_SOF_COMP_BYTES) as *NxJpegSofComponent 279 let bw: i64 = mcus_x * fc.hi 280 let bh: i64 = mcus_y * fc.vi 281 let cw: i64 = (W * fc.hi + maxh - 1) / maxh 282 let ch: i64 = (H * fc.vi + maxv - 1) / maxv 283 cf[c] = sys_mmap(bw * bh * 64 * 8) as i64 // mmap zero-fills -> coeffs start at 0 284 bwp[c] = bw 285 bhp[c] = bh 286 cbw[c] = (cw + 7) / 8 287 cbh[c] = (ch + 7) / 8 288 prevdc[c] = 0 289 c = c + 1 290 } 291 let dcslot: *i64 = sys_mmap(8 * 4) as *i64 292 let acslot: *i64 = sys_mmap(8 * 4) as *i64 293 ctx.n_qtables = 0 294 ctx.n_htables = 0 295 let scan: *NxJpegScan = ctx.scan 296 let q_cnt: *i64 = sys_mmap(8) as *i64 297 let h_cnt: *i64 = sys_mmap(8) as *i64 298 let bidx: *i64 = sys_mmap(8) as *i64 299 // ---- manual marker walk (entropy-skipping between scans) ---- 300 var p: i64 = 2 // past SOI (FF D8) 301 var walking: i64 = 1 302 while walking == 1 { 303 if p + 1 >= jpeg_len { walking = 0 } else { 304 if (jpeg[p] & 0xff) != 0xFF { p = p + 1 } else { 305 while (jpeg[p + 1] & 0xff) == 0xFF { p = p + 1 } // skip fill FFs 306 let marker: i64 = jpeg[p + 1] & 0xff 307 p = p + 2 308 if marker == 0xD9 { walking = 0 } else { // EOI 309 var standalone: i64 = 0 310 if marker == 0x01 { standalone = 1 } // TEM (no payload) 311 if marker >= 0xD0 { if marker <= 0xD7 { standalone = 1 } } // RSTn (no payload) 312 if standalone == 0 { 313 let seglen: i64 = prog_be16(jpeg, p) 314 let poff: i64 = p + 2 315 let plen: i64 = seglen - 2 316 if marker == 0xDB { 317 nx_jpeg_dqt_parse((jpeg as i64 + poff) as *u8, plen, 318 (ctx.qtables as i64 + ctx.n_qtables * NX_JPEG_QTABLE_BYTES) as *NxJpegQTable, 319 (ctx.qvalue_bufs as i64 + ctx.n_qtables * 64 * 8) as *i64, q_cnt) 320 ctx.n_qtables = ctx.n_qtables + q_cnt[0] 321 } 322 if marker == 0xC4 { 323 let base_h: i64 = ctx.n_htables 324 nx_jpeg_dht_parse((jpeg as i64 + poff) as *u8, plen, 325 (ctx.htables as i64 + base_h * NX_JPEG_HTABLE_BYTES) as *NxJpegHTable, 326 (ctx.hbits_pool as i64 + base_h * 17 * 8) as *i64, 327 (ctx.hhuffval_pool as i64 + base_h * 256 * 8) as *i64, 328 (ctx.hmincode_pool as i64 + base_h * 17 * 8) as *i64, 329 (ctx.hmaxcode_pool as i64 + base_h * 17 * 8) as *i64, 330 (ctx.hvalptr_pool as i64 + base_h * 17 * 8) as *i64, h_cnt) 331 var ti: i64 = 0 332 while ti < h_cnt[0] { 333 let ht: *NxJpegHTable = (ctx.htables as i64 + (base_h + ti) * NX_JPEG_HTABLE_BYTES) as *NxJpegHTable 334 if ht.tc == 0 { dcslot[ht.th] = ht as i64 } else { acslot[ht.th] = ht as i64 } 335 ti = ti + 1 336 } 337 ctx.n_htables = ctx.n_htables + h_cnt[0] 338 } 339 if marker == 0xDA { 340 nx_jpeg_sos_parse((jpeg as i64 + poff) as *u8, plen, scan) // populates ss/se/ah/al 341 let ent_start: i64 = poff + plen 342 let ent_end: i64 = prog_next_marker(jpeg, jpeg_len, ent_start) 343 if scan.ss == 0 { if scan.ah == 0 { var rc2: i64 = 0; while rc2 < nf { prevdc[rc2] = 0; rc2 = rc2 + 1 } } } 344 let psr: i64 = prog_scan(jpeg, jpeg_len, scan, frame, cf, bwp, cbw, cbh, prevdc, 345 dcslot, acslot, bidx, ent_start, ent_end, mcus_x, mcus_y) 346 if psr != NX_JPEG_PROG_OK { return NX_JPEG_PROG_FAIL } 347 p = ent_end 348 } else { 349 p = poff + plen 350 } 351 } 352 } 353 } 354 } 355 } 356 // ---- final: dequant + IDCT every block into the planes ---- 357 let sc: *NxJpegMcuScratch = sys_mmap(NX_JPEG_MCU_SCRATCH_BYTES) as *NxJpegMcuScratch 358 nx_jpeg_mcu_scratch_init(sc) 359 c = 0 360 while c < nf { 361 let fc: *NxJpegSofComponent = (frame.components as i64 + c * NX_JPEG_SOF_COMP_BYTES) as *NxJpegSofComponent 362 var qt: *i64 = 0 as *i64 363 var qi: i64 = 0 364 while qi < ctx.n_qtables { 365 let qe: *NxJpegQTable = (ctx.qtables as i64 + qi * NX_JPEG_QTABLE_BYTES) as *NxJpegQTable 366 if qe.tq == fc.tqi { qt = qe.values } 367 qi = qi + 1 368 } 369 if (qt as i64) == 0 { return NX_JPEG_PROG_FAIL } 370 let stride: i64 = bwp[c] 371 let cbuf: i64 = cf[c] 372 let planes_p: *i64 = planes as *i64 373 let pl: *u8 = planes_p[c] as *u8 374 let pstride: i64 = strides[c] 375 var by: i64 = 0 376 while by < bhp[c] { 377 var bx: i64 = 0 378 while bx < bwp[c] { 379 let blk: *i64 = (cbuf + (by * stride + bx) * 64 * 8) as *i64 380 nx_jpeg_dequant_un_zigzag(blk, qt, sc.natural) 381 nx_jpeg_idct_8x8(sc.natural, sc.samples, sc.cos_tbl) 382 var ry: i64 = 0 383 while ry < 8 { 384 var rx: i64 = 0 385 while rx < 8 { 386 var vv: i64 = sc.samples[ry * 8 + rx] + 128 387 if vv < 0 { vv = 0 } 388 if vv > 255 { vv = 255 } 389 pl[(by * 8 + ry) * pstride + (bx * 8 + rx)] = vv as u8 390 rx = rx + 1 391 } 392 ry = ry + 1 393 } 394 bx = bx + 1 395 } 396 by = by + 1 397 } 398 c = c + 1 399 } 400 return NX_JPEG_PROG_OK 401}