Skip to main content

j2k_codec_math/
dwt.rs

1//! JPEG 2000 discrete wavelet transform constants.
2
3mod linearized53;
4pub use linearized53::{
5    linearized_dwt53_row, Dwt53Band, Dwt53LinearRow, Dwt53LinearTap, DWT53_MAX_HIGH_LINEAR_TAPS,
6    DWT53_MAX_LINEAR_TAPS,
7};
8
9/// Return the maximum number of DWT decomposition levels supported by an
10/// image geometry.
11///
12/// The shared encoder policy is `floor(log2(min(width, height)))`; a zero or
13/// unit-length axis supports no decomposition levels. Keeping this
14/// conservative geometry rule here ensures CPU and GPU paths apply the same
15/// ceiling.
16#[must_use]
17pub const fn max_decomposition_levels(width: u32, height: u32) -> u8 {
18    let mut minimum_dimension = if width < height { width } else { height };
19    let mut levels = 0_u8;
20    while minimum_dimension > 1 {
21        minimum_dimension >>= 1;
22        levels += 1;
23    }
24    levels
25}
26
27/// Forward irreversible 9/7 lifting step alpha, rounded for existing CPU/GPU paths.
28pub const DWT97_ALPHA_F32: f32 = -1.586_134_3;
29/// Forward irreversible 9/7 lifting step beta, rounded for existing CPU/GPU paths.
30pub const DWT97_BETA_F32: f32 = -0.052_980_117;
31/// Forward irreversible 9/7 lifting step gamma, rounded for existing CPU/GPU paths.
32pub const DWT97_GAMMA_F32: f32 = 0.882_911_1;
33/// Forward irreversible 9/7 lifting step delta, rounded for existing CPU/GPU paths.
34pub const DWT97_DELTA_F32: f32 = 0.443_506_87;
35/// Irreversible 9/7 scaling factor, rounded for existing CPU/GPU paths.
36pub const DWT97_KAPPA_F32: f32 = 1.230_174_1;
37/// Inverse irreversible 9/7 scaling factor, computed the same way as existing paths.
38pub const DWT97_INV_KAPPA_F32: f32 = 1.0 / DWT97_KAPPA_F32;
39
40/// Inverse 9/7 alpha step used by synthesis paths.
41pub const IDWT97_NEG_ALPHA_F32: f32 = -DWT97_ALPHA_F32;
42/// Inverse 9/7 beta step used by synthesis paths.
43pub const IDWT97_NEG_BETA_F32: f32 = -DWT97_BETA_F32;
44/// Inverse 9/7 gamma step used by synthesis paths.
45pub const IDWT97_NEG_GAMMA_F32: f32 = -DWT97_GAMMA_F32;
46/// Inverse 9/7 delta step used by synthesis paths.
47pub const IDWT97_NEG_DELTA_F32: f32 = -DWT97_DELTA_F32;
48
49/// Forward irreversible 9/7 lifting step alpha at f64 precision.
50pub const DWT97_ALPHA_F64: f64 = -1.586_134_342_059_924;
51/// Forward irreversible 9/7 lifting step beta at f64 precision.
52pub const DWT97_BETA_F64: f64 = -0.052_980_118_572_961;
53/// Forward irreversible 9/7 lifting step gamma at f64 precision.
54pub const DWT97_GAMMA_F64: f64 = 0.882_911_075_530_934;
55/// Forward irreversible 9/7 lifting step delta at f64 precision.
56pub const DWT97_DELTA_F64: f64 = 0.443_506_852_043_971;
57/// Irreversible 9/7 scaling factor at f64 precision.
58pub const DWT97_KAPPA_F64: f64 = 1.230_174_104_914_001;
59/// Inverse irreversible 9/7 scaling factor at f64 precision.
60pub const DWT97_INV_KAPPA_F64: f64 = 1.0 / DWT97_KAPPA_F64;
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    const MAX_LEVELS_FOR_U32_GEOMETRY: u8 = max_decomposition_levels(u32::MAX, u32::MAX);
67
68    #[test]
69    fn maximum_decomposition_levels_are_const_and_use_the_shorter_axis() {
70        assert_eq!(MAX_LEVELS_FOR_U32_GEOMETRY, 31);
71
72        for (width, height, expected) in [
73            (0, 0, 0),
74            (0, u32::MAX, 0),
75            (u32::MAX, 0, 0),
76            (1, u32::MAX, 0),
77            (u32::MAX, 1, 0),
78            (2, 8, 1),
79            (8, 2, 1),
80            (3, 9, 1),
81            (9, 3, 1),
82            (7, 9, 2),
83            (9, 7, 2),
84            (u32::MAX, u32::MAX, 31),
85        ] {
86            assert_eq!(max_decomposition_levels(width, height), expected);
87        }
88    }
89
90    #[test]
91    fn maximum_decomposition_levels_match_power_of_two_boundaries() {
92        for exponent in 1_u8..=31 {
93            let power = 1_u32 << exponent;
94            assert_eq!(max_decomposition_levels(power, power), exponent);
95            assert_eq!(max_decomposition_levels(power - 1, u32::MAX), exponent - 1);
96            assert_eq!(
97                max_decomposition_levels(power.saturating_add(1), u32::MAX),
98                exponent
99            );
100        }
101    }
102
103    #[test]
104    fn f32_constants_match_existing_backend_rounding() {
105        assert_eq!(DWT97_ALPHA_F32.to_bits(), (-1.586_134_3f32).to_bits());
106        assert_eq!(DWT97_BETA_F32.to_bits(), (-0.052_980_117f32).to_bits());
107        assert_eq!(DWT97_GAMMA_F32.to_bits(), 0.882_911_1f32.to_bits());
108        assert_eq!(DWT97_DELTA_F32.to_bits(), 0.443_506_87f32.to_bits());
109        assert_eq!(DWT97_KAPPA_F32.to_bits(), 1.230_174_1f32.to_bits());
110        assert_eq!(
111            DWT97_INV_KAPPA_F32.to_bits(),
112            (1.0f32 / 1.230_174_1f32).to_bits()
113        );
114    }
115
116    #[test]
117    fn inverse_constants_are_exact_negations_of_forward_steps() {
118        assert_eq!(IDWT97_NEG_ALPHA_F32.to_bits(), (-DWT97_ALPHA_F32).to_bits());
119        assert_eq!(IDWT97_NEG_BETA_F32.to_bits(), (-DWT97_BETA_F32).to_bits());
120        assert_eq!(IDWT97_NEG_GAMMA_F32.to_bits(), (-DWT97_GAMMA_F32).to_bits());
121        assert_eq!(IDWT97_NEG_DELTA_F32.to_bits(), (-DWT97_DELTA_F32).to_bits());
122    }
123}