code wiki / (root) / nx_jpeg_dqt.nx

nx_jpeg_dqt.nx source

↩ module page · 113 lines · 4827 B

1// nx_jpeg_dqt.nx -- JPEG quantization-table (DQT) parser per 2// ITU-T Rec. T.81 sec B.2.4.1. 3// 4// DQT payload layout (one or more tables, packed): 5// Pq (4 bits) Tq (4 bits) -- precision + table-id 6// Qk[64] -- table values in zig-zag scan order 7// 8// Pq = 0 means each Qk is an 8-bit unsigned value (64 bytes total). 9// Pq = 1 means each Qk is a 16-bit unsigned big-endian value (128 bytes 10// total). Pq=1 is only legal for sample precision > 8 (T.81 sec B.2.4.1); 11// baseline JPEG (SOF0) is 8-bit so 99% of real-world DQTs are Pq=0. 12// 13// Tq is 0..3. Multiple DQT segments may be concatenated within a single 14// payload; this parser walks the payload and emits each table separately. 15// 16// CRITICAL: JPEG quantization tables are stored in ZIG-ZAG order. 17// Decode pipeline must un-zigzag before dequantization or the IDCT will 18// produce garbage. This parser preserves the on-wire zig-zag order; 19// un-zigzag is the consumer's responsibility (it's a 64-byte permutation 20// lookup -- shipped as nx_jpeg_zigzag.nx in a follow-on stone). 21// 22// nx_safety_envelope: 23// intended_use: "Quantization-table parsing for JPEG decode." 24// sil_target: SIL1 25// evidence: [t81_section_b_2_4_1_canonical_basis, 26// sealed_precision_pq_2_state, 27// bounded_iteration, 28// substrate_honest_truncation_signal] 29// hazard_register: [bug-tape-dqt-precision-confusion, 30// bug-tape-multi-table-payload-overrun, 31// bug-tape-dqt-zero-coefficient-divide-by-zero] 32// residual_risk: "Caller must validate that Qk[*] != 0 before 33// using as divisor; substrate stores values 34// verbatim including zero." 35// verdict: NOT_YET_EVALUATED 36 37import "nx_syscalls.nx" 38 39const NX_JPEG_DQT_OK: i64 = 0 40const NX_JPEG_DQT_TRUNC: i64 = 1 // payload too short for declared precision 41const NX_JPEG_DQT_BAD_TQ: i64 = 2 // Tq > 3 42const NX_JPEG_DQT_BAD_PQ: i64 = 3 // Pq > 1 43const NX_JPEG_DQT_OVERFLOW: i64 = 4 // more than 4 tables in payload 44const NX_JPEG_DQT_RESULT_N: i64 = 5 45 46func nx_jpeg_dqt_result_is_valid(v: i64) -> i64 { 47 if v < 0 { return 0 } 48 if v >= NX_JPEG_DQT_RESULT_N { return 0 } 49 return 1 50} 51 52// One parsed quantization table. `values` always has 64 entries 53// (one per 8x8 DCT coefficient position) regardless of precision. 54struct NxJpegQTable { 55 tq: i64, // table-id 0..3 56 pq: i64, // precision 0 (8-bit) or 1 (16-bit) 57 values: *i64, // 64 entries -- caller-supplied; substrate writes here 58} 59 60const NX_JPEG_QTABLE_BYTES: i64 = 24 61 62// Output array: caller supplies space for up to 4 tables. 63// Parsed table count returned via *count_p. 64// 65// payload = bytes of one DQT segment payload (excluding the 2-byte 66// length prefix that nx_jpeg_seg_next already consumed) 67// payload_len = payload byte count 68// tables = caller buffer for up to 4 NxJpegQTable structs 69// value_bufs = caller-supplied 4 * 64-entry i64 arrays (256 i64 total) 70// count_p = receives number of tables parsed (0..4) 71func nx_jpeg_dqt_parse(payload: *u8, payload_len: i64, 72 tables: *NxJpegQTable, 73 value_bufs: *i64, 74 count_p: *i64) -> i64 { 75 var p: i64 = 0 76 var n_tables: i64 = 0 77 count_p[0] = 0 78 while p < payload_len { 79 if n_tables >= 4 { return NX_JPEG_DQT_OVERFLOW } 80 if p >= payload_len { return NX_JPEG_DQT_TRUNC } 81 let pq_tq: i64 = payload[p] as i64 82 p = p + 1 83 let pq: i64 = pq_tq >> 4 84 let tq: i64 = pq_tq & 0x0F 85 if pq > 1 { return NX_JPEG_DQT_BAD_PQ } 86 if tq > 3 { return NX_JPEG_DQT_BAD_TQ } 87 var elem_bytes: i64 = 1 88 if pq == 1 { elem_bytes = 2 } 89 let needed: i64 = 64 * elem_bytes 90 if p + needed > payload_len { return NX_JPEG_DQT_TRUNC } 91 // Slice value_bufs[n_tables*64 .. n_tables*64+64]. 92 let table_values: *i64 = (value_bufs as i64 + n_tables * 64 * 8) as *i64 93 var k: i64 = 0 94 while k < 64 { 95 if elem_bytes == 1 { 96 table_values[k] = payload[p + k] as i64 97 } else { 98 let hi: i64 = payload[p + k * 2] as i64 99 let lo: i64 = payload[p + k * 2 + 1] as i64 100 table_values[k] = (hi << 8) | lo 101 } 102 k = k + 1 103 } 104 let entry: *NxJpegQTable = (tables as i64 + n_tables * NX_JPEG_QTABLE_BYTES) as *NxJpegQTable 105 entry.tq = tq 106 entry.pq = pq 107 entry.values = table_values 108 n_tables = n_tables + 1 109 p = p + needed 110 } 111 count_p[0] = n_tables 112 return NX_JPEG_DQT_OK 113}