code wiki / (root) / nx_jpeg_sos.nx

nx_jpeg_sos.nx source

↩ module page · 116 lines · 4392 B

1// nx_jpeg_sos.nx -- JPEG Start-of-Scan header parser. 2// Per ITU-T Rec. T.81 sec B.2.3. 3// 4// SOS segment payload (immediately after the 2-byte length the 5// marker scanner already consumed): 6// 7// Ns (1 byte) number of components in this scan (1..4) 8// per component (2 bytes each): 9// Csj (1 byte) component selector (matches a Ci from the 10// active SOF frame descriptor) 11// TdjTaj (1 byte) high4 = Td DC Huffman table id (0..3), 12// low4 = Ta AC Huffman table id (0..3) 13// Ss (1 byte) start of spectral selection. Baseline JPEG 14// always 0. 15// Se (1 byte) end of spectral selection. Baseline JPEG 16// always 63. 17// AhAl (1 byte) high4 = Ah successive-approximation high, 18// low4 = Al successive-approximation low. 19// Baseline JPEG always 0x00. 20// 21// Total: 1 + 2 * Ns + 3 bytes. 22// 23// After this header, the entropy-coded segment begins (handled by 24// nx_jpeg_entropy). 25// 26// nx_safety_envelope: 27// intended_use: "Scan-header parsing for JPEG decode." 28// sil_target: SIL1 29// evidence: [t81_section_b_2_3_canonical_basis, 30// bounded_4_component_max] 31// hazard_register: [bug-tape-sos-bad-ns, 32// bug-tape-sos-progressive-mistaken-baseline] 33// residual_risk: "Progressive JPEG (Ss != 0 or Se != 63 or 34// AhAl != 0) is detected but not decoded by 35// this primitive." 36// verdict: NOT_YET_EVALUATED 37 38import "nx_syscalls.nx" 39 40const NX_JPEG_SOS_OK: i64 = 0 41const NX_JPEG_SOS_TRUNC: i64 = 1 42const NX_JPEG_SOS_BAD_NS: i64 = 2 43const NX_JPEG_SOS_BAD_TABLE: i64 = 3 44const NX_JPEG_SOS_NOT_BASELINE: i64 = 4 45const NX_JPEG_SOS_RESULT_N: i64 = 5 46 47func nx_jpeg_sos_result_is_valid(v: i64) -> i64 { 48 if v < 0 { return 0 } 49 if v >= NX_JPEG_SOS_RESULT_N { return 0 } 50 return 1 51} 52 53struct NxJpegSosComponent { 54 csj: i64, // component selector (matches Ci from SOF) 55 td: i64, // DC Huffman table id (0..3) 56 ta: i64 // AC Huffman table id (0..3) 57} 58 59const NX_JPEG_SOS_COMP_BYTES: i64 = 24 60 61struct NxJpegScan { 62 n_components: i64, 63 components: *NxJpegSosComponent, 64 ss: i64, // start of spectral selection (0 baseline) 65 se: i64, // end of spectral selection (63 baseline) 66 ah: i64, // successive-approx high (0 baseline) 67 al: i64 // successive-approx low (0 baseline) 68} 69 70const NX_JPEG_SCAN_BYTES: i64 = 48 71 72// Parse SOS payload. Caller pre-allocates scan.components as a 73// 4-slot NxJpegSosComponent array. 74func nx_jpeg_sos_parse(payload: *u8, payload_len: i64, 75 scan: *NxJpegScan) -> i64 { 76 if payload_len < 4 { return NX_JPEG_SOS_TRUNC } 77 let ns: i64 = payload[0] as i64 78 if ns == 0 { return NX_JPEG_SOS_BAD_NS } 79 if ns > 4 { return NX_JPEG_SOS_BAD_NS } 80 let comps_bytes: i64 = ns * 2 81 if 1 + comps_bytes + 3 > payload_len { return NX_JPEG_SOS_TRUNC } 82 83 scan.n_components = ns 84 var i: i64 = 0 85 while i < ns { 86 let off: i64 = 1 + i * 2 87 let csj: i64 = payload[off] as i64 88 let td_ta: i64 = payload[off + 1] as i64 89 let td: i64 = td_ta >> 4 90 let ta: i64 = td_ta & 0x0F 91 if td > 3 { return NX_JPEG_SOS_BAD_TABLE } 92 if ta > 3 { return NX_JPEG_SOS_BAD_TABLE } 93 let comp: *NxJpegSosComponent = (scan.components as i64 + i * NX_JPEG_SOS_COMP_BYTES) as *NxJpegSosComponent 94 comp.csj = csj 95 comp.td = td 96 comp.ta = ta 97 i = i + 1 98 } 99 let tail_off: i64 = 1 + comps_bytes 100 scan.ss = payload[tail_off] as i64 101 scan.se = payload[tail_off + 1] as i64 102 let ah_al: i64 = payload[tail_off + 2] as i64 103 scan.ah = ah_al >> 4 104 scan.al = ah_al & 0x0F 105 106 // Baseline JPEG predicate: Ss=0, Se=63, Ah=Al=0. Caller may 107 // still proceed for progressive decode, but this primitive 108 // surfaces a NOT_BASELINE verdict so the simple MCU walker 109 // can stop cleanly. 110 if scan.ss != 0 { return NX_JPEG_SOS_NOT_BASELINE } 111 if scan.se != 63 { return NX_JPEG_SOS_NOT_BASELINE } 112 if scan.ah != 0 { return NX_JPEG_SOS_NOT_BASELINE } 113 if scan.al != 0 { return NX_JPEG_SOS_NOT_BASELINE } 114 115 return NX_JPEG_SOS_OK 116}