Skip to main content

j2k_codec_math/
classic.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Shared JPEG 2000 classic Tier-1 probability and context tables.
4
5/// One JPEG 2000 MQ probability-state transition.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct ClassicMqState {
8    /// Probability estimate.
9    pub qe: u32,
10    /// Next state after an MPS event.
11    pub nmps: u8,
12    /// Next state after an LPS event.
13    pub nlps: u8,
14    /// Whether an LPS event switches the MPS bit.
15    pub switch: bool,
16}
17
18macro_rules! mq_states {
19    ($($qe:expr, $nmps:expr, $nlps:expr, $switch:expr),+ $(,)?) => {
20        [$(ClassicMqState { qe: $qe, nmps: $nmps, nlps: $nlps, switch: $switch }),+]
21    };
22}
23
24/// MQ values and state transitions from ITU-T T.800 Table C.2.
25#[rustfmt::skip]
26pub const MQ_STATES: [ClassicMqState; 47] = mq_states!(
27    0x5601, 1, 1, true,
28    0x3401, 2, 6, false,
29    0x1801, 3, 9, false,
30    0x0AC1, 4, 12, false,
31    0x0521, 5, 29, false,
32    0x0221, 38, 33, false,
33    0x5601, 7, 6, true,
34    0x5401, 8, 14, false,
35    0x4801, 9, 14, false,
36    0x3801, 10, 14, false,
37    0x3001, 11, 17, false,
38    0x2401, 12, 18, false,
39    0x1C01, 13, 20, false,
40    0x1601, 29, 21, false,
41    0x5601, 15, 14, true,
42    0x5401, 16, 14, false,
43    0x5101, 17, 15, false,
44    0x4801, 18, 16, false,
45    0x3801, 19, 17, false,
46    0x3401, 20, 18, false,
47    0x3001, 21, 19, false,
48    0x2801, 22, 19, false,
49    0x2401, 23, 20, false,
50    0x2201, 24, 21, false,
51    0x1C01, 25, 22, false,
52    0x1801, 26, 23, false,
53    0x1601, 27, 24, false,
54    0x1401, 28, 25, false,
55    0x1201, 29, 26, false,
56    0x1101, 30, 27, false,
57    0x0AC1, 31, 28, false,
58    0x09C1, 32, 29, false,
59    0x08A1, 33, 30, false,
60    0x0521, 34, 31, false,
61    0x0441, 35, 32, false,
62    0x02A1, 36, 33, false,
63    0x0221, 37, 34, false,
64    0x0141, 38, 35, false,
65    0x0111, 39, 36, false,
66    0x0085, 40, 37, false,
67    0x0049, 41, 38, false,
68    0x0025, 42, 39, false,
69    0x0015, 43, 40, false,
70    0x0009, 44, 41, false,
71    0x0005, 45, 42, false,
72    0x0001, 45, 43, false,
73    0x5601, 46, 46, false,
74);
75
76const fn mq_qe_values() -> [u32; 47] {
77    let mut values = [0; 47];
78    let mut index = 0;
79    while index < values.len() {
80        values[index] = MQ_STATES[index].qe;
81        index += 1;
82    }
83    values
84}
85
86const fn packed_mq_transitions() -> [u32; 47] {
87    let mut values = [0; 47];
88    let mut index = 0;
89    while index < values.len() {
90        let state = MQ_STATES[index];
91        values[index] =
92            state.nmps as u32 | ((state.nlps as u32) << 8) | ((state.switch as u32) << 16);
93        index += 1;
94    }
95    values
96}
97
98/// Primitive MQ probability values for device compilers that cannot lower arrays of structs.
99pub const MQ_QE_VALUES: [u32; 47] = mq_qe_values();
100/// Packed MQ MPS/LPS transitions and switch flag for device lookup tables.
101pub const PACKED_MQ_TRANSITION_VALUES: [u32; 47] = packed_mq_transitions();
102
103/// Sign-coding context and XOR bit indexed by packed cardinal-neighbor state.
104#[rustfmt::skip]
105pub const SIGN_CONTEXT_LOOKUP: [(u8, u8); 256] = [
106    (9,0), (10,0), (10,1), (0,0), (12,0), (13,0), (11,0), (0,0), (12,1), (11,1),
107    (13,1), (0,0), (0,0), (0,0), (0,0), (0,0), (12,0), (13,0), (11,0), (0,0),
108    (12,0), (13,0), (11,0), (0,0), (9,0), (10,0), (10,1), (0,0), (0,0), (0,0),
109    (0,0), (0,0), (12,1), (11,1), (13,1), (0,0), (9,0), (10,0), (10,1), (0,0),
110    (12,1), (11,1), (13,1), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
111    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
112    (0,0), (0,0), (0,0), (10,0), (10,0), (9,0), (0,0), (13,0), (13,0), (12,0),
113    (0,0), (11,1), (11,1), (12,1), (0,0), (0,0), (0,0), (0,0), (0,0), (13,0),
114    (13,0), (12,0), (0,0), (13,0), (13,0), (12,0), (0,0), (10,0), (10,0), (9,0),
115    (0,0), (0,0), (0,0), (0,0), (0,0), (11,1), (11,1), (12,1), (0,0), (10,0),
116    (10,0), (9,0), (0,0), (11,1), (11,1), (12,1), (0,0), (0,0), (0,0), (0,0),
117    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
118    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (10,1), (9,0), (10,1), (0,0),
119    (11,0), (12,0), (11,0), (0,0), (13,1), (12,1), (13,1), (0,0), (0,0), (0,0),
120    (0,0), (0,0), (11,0), (12,0), (11,0), (0,0), (11,0), (12,0), (11,0), (0,0),
121    (10,1), (9,0), (10,1), (0,0), (0,0), (0,0), (0,0), (0,0), (13,1), (12,1),
122    (13,1), (0,0), (10,1), (9,0), (10,1), (0,0), (13,1), (12,1), (13,1), (0,0),
123    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
124    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
125    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
126    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
127    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
128    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
129    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
130    (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
131];
132
133const fn packed_sign_contexts() -> [u16; 256] {
134    let mut values = [0; 256];
135    let mut index = 0;
136    while index < values.len() {
137        let (context, xor) = SIGN_CONTEXT_LOOKUP[index];
138        values[index] = context as u16 | ((xor as u16) << 8);
139        index += 1;
140    }
141    values
142}
143
144/// Packed sign context in the low byte and XOR bit in the high byte.
145pub const PACKED_SIGN_CONTEXT_LOOKUP: [u16; 256] = packed_sign_contexts();
146
147/// Zero-coding contexts for LL and LH sub-bands.
148#[rustfmt::skip]
149pub const ZERO_CTX_LL_LH_LOOKUP: [u8; 256] = [
150    0, 3, 1, 3, 5, 7, 6, 7, 1, 3, 2, 3, 6, 7, 6, 7, 5, 7, 6, 7, 8, 8, 8, 8, 6,
151    7, 6, 7, 8, 8, 8, 8, 1, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6, 7, 6, 7,
152    6, 7, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 3, 4, 3, 4, 7, 7, 7, 7, 3, 4, 3,
153    4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 3, 4, 3, 4,
154    7, 7, 7, 7, 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8,
155    8, 8, 8, 1, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6, 7, 6, 7, 6, 7, 8, 8,
156    8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 2, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6,
157    7, 6, 7, 6, 7, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 3, 4, 3, 4, 7, 7, 7, 7,
158    3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 3,
159    4, 3, 4, 7, 7, 7, 7, 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7,
160    7, 7, 8, 8, 8, 8,
161];
162
163/// Zero-coding contexts for HL sub-bands.
164#[rustfmt::skip]
165pub const ZERO_CTX_HL_LOOKUP: [u8; 256] = [
166    0, 5, 1, 6, 3, 7, 3, 7, 1, 6, 2, 6, 3, 7, 3, 7, 3, 7, 3, 7, 4, 7, 4, 7, 3,
167    7, 3, 7, 4, 7, 4, 7, 1, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3, 7, 3, 7,
168    3, 7, 4, 7, 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 5, 8, 6, 8, 7, 8, 7, 8, 6, 8, 6,
169    8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 6, 8, 6, 8,
170    7, 8, 7, 8, 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
171    8, 7, 8, 1, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3, 7, 3, 7, 3, 7, 4, 7,
172    4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 2, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3,
173    7, 3, 7, 3, 7, 4, 7, 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 6, 8, 6, 8, 7, 8, 7, 8,
174    6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 6,
175    8, 6, 8, 7, 8, 7, 8, 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
176    7, 8, 7, 8, 7, 8,
177];
178
179/// Zero-coding contexts for HH sub-bands.
180#[rustfmt::skip]
181pub const ZERO_CTX_HH_LOOKUP: [u8; 256] = [
182    0, 1, 3, 4, 1, 2, 4, 5, 3, 4, 6, 7, 4, 5, 7, 7, 1, 2, 4, 5, 2, 2, 5, 5, 4,
183    5, 7, 7, 5, 5, 7, 7, 3, 4, 6, 7, 4, 5, 7, 7, 6, 7, 8, 8, 7, 7, 8, 8, 4, 5,
184    7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 1, 2, 4, 5, 2, 2, 5, 5, 4, 5, 7,
185    7, 5, 5, 7, 7, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 7, 7, 5, 5, 7, 7, 4, 5, 7, 7,
186    5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 5, 5, 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7,
187    7, 8, 8, 3, 4, 6, 7, 4, 5, 7, 7, 6, 7, 8, 8, 7, 7, 8, 8, 4, 5, 7, 7, 5, 5,
188    7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 6, 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
189    8, 7, 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 5, 7, 7, 5, 5, 7, 7,
190    7, 7, 8, 8, 7, 7, 8, 8, 5, 5, 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 7,
191    7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 8, 8, 7, 7, 8, 8, 8, 8,
192    8, 8, 8, 8, 8, 8,
193];
194
195#[cfg(test)]
196mod tests {
197    use core::hint::black_box;
198
199    use super::*;
200
201    #[test]
202    fn device_tables_match_their_structured_sources_at_runtime() {
203        let build_qe: fn() -> [u32; 47] = mq_qe_values;
204        let build_transitions: fn() -> [u32; 47] = packed_mq_transitions;
205        let build_sign_contexts: fn() -> [u16; 256] = packed_sign_contexts;
206
207        let qe = black_box(build_qe)();
208        let transitions = black_box(build_transitions)();
209        let sign_contexts = black_box(build_sign_contexts)();
210
211        for (index, state) in MQ_STATES.iter().copied().enumerate() {
212            assert_eq!(qe[index], state.qe);
213            assert_eq!(
214                transitions[index],
215                u32::from(state.nmps)
216                    | (u32::from(state.nlps) << 8)
217                    | (u32::from(state.switch) << 16)
218            );
219        }
220        for (index, (context, xor)) in SIGN_CONTEXT_LOOKUP.iter().copied().enumerate() {
221            assert_eq!(
222                sign_contexts[index],
223                u16::from(context) | (u16::from(xor) << 8)
224            );
225        }
226    }
227}