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}