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}