jxl_frame/
filter.rs

1use jxl_bitstream::Bitstream;
2use jxl_oxide_common::Bundle;
3
4use crate::{header::Encoding, Result};
5
6#[derive(Debug, Clone)]
7pub enum Gabor {
8    Disabled,
9    Enabled([[f32; 2]; 3]),
10}
11
12impl Default for Gabor {
13    fn default() -> Self {
14        Self::Enabled([[0.115169525, 0.061248592]; 3])
15    }
16}
17
18impl<Ctx> Bundle<Ctx> for Gabor {
19    type Error = crate::Error;
20
21    fn parse(bitstream: &mut Bitstream, _ctx: Ctx) -> Result<Self> {
22        let gab_enabled = bitstream.read_bool()?;
23        if !gab_enabled {
24            return Ok(Self::Disabled);
25        }
26
27        let custom = bitstream.read_bool()?;
28        if !custom {
29            return Ok(Self::default());
30        }
31
32        let mut weights = [[0.0f32; 2]; 3];
33        for chan_weight in &mut weights {
34            for weight in &mut *chan_weight {
35                *weight = bitstream.read_f16_as_f32()?;
36            }
37            if f32::abs(1.0 + (chan_weight[0] + chan_weight[1]) * 4.0) < f32::EPSILON {
38                return Err(jxl_bitstream::Error::ValidationFailed(
39                    "Gaborish weights lead to near 0 unnormalized kernel",
40                )
41                .into());
42            }
43        }
44        Ok(Self::Enabled(weights))
45    }
46}
47
48impl Gabor {
49    pub fn enabled(&self) -> bool {
50        matches!(self, Self::Enabled(_))
51    }
52}
53
54#[derive(Debug, Clone)]
55pub enum EdgePreservingFilter {
56    Disabled,
57    Enabled(EpfParams),
58}
59
60#[derive(Debug, Clone)]
61pub struct EpfParams {
62    pub iters: u32,
63    pub sharp_lut: [f32; 8],
64    pub channel_scale: [f32; 3],
65    pub sigma: EpfSigma,
66    pub sigma_for_modular: f32,
67}
68
69impl EdgePreservingFilter {
70    pub fn enabled(&self) -> bool {
71        matches!(self, Self::Enabled { .. })
72    }
73}
74
75impl Default for EdgePreservingFilter {
76    fn default() -> Self {
77        Self::Enabled(EpfParams::default())
78    }
79}
80
81const EPF_SHARP_LUT_DEFAULT: [f32; 8] = [
82    0.0,
83    1.0 / 7.0,
84    2.0 / 7.0,
85    3.0 / 7.0,
86    4.0 / 7.0,
87    5.0 / 7.0,
88    6.0 / 7.0,
89    1.0,
90];
91const EPF_CHANNEL_SCALE_DEFAULT: [f32; 3] = [40.0, 5.0, 3.5];
92
93impl Default for EpfParams {
94    fn default() -> Self {
95        Self {
96            iters: 2,
97            sharp_lut: EPF_SHARP_LUT_DEFAULT,
98            channel_scale: EPF_CHANNEL_SCALE_DEFAULT,
99            sigma: Default::default(),
100            sigma_for_modular: 1.0,
101        }
102    }
103}
104
105impl Bundle<Encoding> for EdgePreservingFilter {
106    type Error = crate::Error;
107
108    fn parse(bitstream: &mut Bitstream, encoding: Encoding) -> Result<Self> {
109        let iters = bitstream.read_bits(2)?;
110        if iters == 0 {
111            return Ok(Self::Disabled);
112        }
113
114        let sharp_custom = if encoding == Encoding::VarDct {
115            bitstream.read_bool()?
116        } else {
117            false
118        };
119        let sharp_lut = if sharp_custom {
120            let mut ret = [0.0; 8];
121            for out in &mut ret {
122                *out = bitstream.read_f16_as_f32()?;
123            }
124            ret
125        } else {
126            EPF_SHARP_LUT_DEFAULT
127        };
128
129        let weight_custom = bitstream.read_bool()?;
130        let channel_scale = if weight_custom {
131            let mut ret = [0.0; 3];
132            for out in &mut ret {
133                *out = bitstream.read_f16_as_f32()?;
134            }
135            bitstream.read_bits(32)?; // ignored
136            ret
137        } else {
138            EPF_CHANNEL_SCALE_DEFAULT
139        };
140
141        let sigma_custom = bitstream.read_bool()?;
142        let sigma = if sigma_custom {
143            EpfSigma::parse(bitstream, encoding)?
144        } else {
145            EpfSigma::default()
146        };
147
148        let sigma_for_modular = if encoding == Encoding::Modular {
149            let out = bitstream.read_f16_as_f32()?;
150            if out < f32::EPSILON {
151                tracing::warn!("EPF: sigma for modular is too small");
152            }
153            out
154        } else {
155            1.0
156        };
157
158        Ok(Self::Enabled(EpfParams {
159            iters,
160            sharp_lut,
161            channel_scale,
162            sigma,
163            sigma_for_modular,
164        }))
165    }
166}
167
168#[derive(Debug, Clone)]
169pub struct EpfSigma {
170    pub quant_mul: f32,
171    pub pass0_sigma_scale: f32,
172    pub pass2_sigma_scale: f32,
173    pub border_sad_mul: f32,
174}
175
176impl Default for EpfSigma {
177    fn default() -> Self {
178        Self {
179            quant_mul: 0.46,
180            pass0_sigma_scale: 0.9,
181            pass2_sigma_scale: 6.5,
182            border_sad_mul: 2.0 / 3.0,
183        }
184    }
185}
186
187impl Bundle<Encoding> for EpfSigma {
188    type Error = crate::Error;
189
190    fn parse(bitstream: &mut Bitstream, encoding: Encoding) -> Result<Self> {
191        Ok(Self {
192            quant_mul: if encoding == Encoding::VarDct {
193                bitstream.read_f16_as_f32()?
194            } else {
195                0.46
196            },
197            pass0_sigma_scale: bitstream.read_f16_as_f32()?,
198            pass2_sigma_scale: bitstream.read_f16_as_f32()?,
199            border_sad_mul: bitstream.read_f16_as_f32()?,
200        })
201    }
202}