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)?; 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}