1use alloc::vec::Vec;
6
7use super::super::quantize;
8use crate::IrreversibleQuantizationSubbandScales;
9
10#[derive(Debug, Clone)]
12#[expect(
13 clippy::struct_excessive_bools,
14 reason = "the public options expose independent JPEG 2000 coding and marker switches"
15)]
16pub struct EncodeOptions {
17 pub num_decomposition_levels: u8,
19 pub reversible: bool,
21 pub code_block_width_exp: u8,
23 pub code_block_height_exp: u8,
25 pub guard_bits: u8,
27 pub use_ht_block_coding: bool,
29 pub progression_order: EncodeProgressionOrder,
31 pub write_tlm: bool,
33 pub write_plt: bool,
35 pub write_plm: bool,
37 pub write_ppm: bool,
39 pub write_ppt: bool,
41 pub write_sop: bool,
43 pub write_eph: bool,
45 pub use_mct: bool,
47 pub num_layers: u8,
49 pub quality_layer_byte_targets: Vec<u64>,
51 pub validate_high_throughput_codestream: bool,
53 pub irreversible_quantization_scale: f32,
58 pub irreversible_quantization_subband_scales: IrreversibleQuantizationSubbandScales,
61 pub component_sampling: Option<Vec<(u8, u8)>>,
67 pub roi_component_shifts: Vec<u8>,
73 pub tile_size: Option<(u32, u32)>,
75 pub tile_part_packet_limit: Option<u16>,
77 pub precinct_exponents: Vec<(u8, u8)>,
79}
80
81#[derive(Debug, Clone, Copy)]
83pub struct EncodeComponentPlane<'a> {
84 pub data: &'a [u8],
86 pub x_rsiz: u8,
88 pub y_rsiz: u8,
90}
91
92#[derive(Debug, Clone, Copy)]
94pub struct EncodeTypedComponentPlane<'a> {
95 pub data: &'a [u8],
97 pub x_rsiz: u8,
99 pub y_rsiz: u8,
101 pub bit_depth: u8,
103 pub signed: bool,
105}
106
107#[derive(Debug, Clone, Copy)]
115pub struct EncodeRoiRegion {
116 pub component: u16,
118 pub x: u32,
120 pub y: u32,
122 pub width: u32,
124 pub height: u32,
126 pub shift: u8,
128}
129
130impl Default for EncodeOptions {
131 fn default() -> Self {
132 Self {
133 num_decomposition_levels: 5,
134 reversible: true,
135 code_block_width_exp: 4,
136 code_block_height_exp: 4,
137 guard_bits: 1,
138 use_ht_block_coding: false,
139 progression_order: EncodeProgressionOrder::Lrcp,
140 write_tlm: false,
141 write_plt: false,
142 write_plm: false,
143 write_ppm: false,
144 write_ppt: false,
145 write_sop: false,
146 write_eph: false,
147 use_mct: true,
148 num_layers: 1,
149 quality_layer_byte_targets: Vec::new(),
150 validate_high_throughput_codestream: true,
151 irreversible_quantization_scale: 1.0,
152 irreversible_quantization_subband_scales:
153 IrreversibleQuantizationSubbandScales::default(),
154 component_sampling: None,
155 roi_component_shifts: Vec::new(),
156 tile_size: None,
157 tile_part_packet_limit: None,
158 precinct_exponents: Vec::new(),
159 }
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
165pub enum EncodeProgressionOrder {
166 #[default]
168 Lrcp,
169 Rlcp,
171 Rpcl,
173 Pcrl,
175 Cprl,
177}
178
179impl EncodeProgressionOrder {
180 pub(crate) const fn packetization_order(self) -> crate::J2kPacketizationProgressionOrder {
181 match self {
182 Self::Lrcp => crate::J2kPacketizationProgressionOrder::Lrcp,
183 Self::Rlcp => crate::J2kPacketizationProgressionOrder::Rlcp,
184 Self::Rpcl => crate::J2kPacketizationProgressionOrder::Rpcl,
185 Self::Pcrl => crate::J2kPacketizationProgressionOrder::Pcrl,
186 Self::Cprl => crate::J2kPacketizationProgressionOrder::Cprl,
187 }
188 }
189}
190
191fn validate_irreversible_quantization_scale(scale: f32) -> Result<(), &'static str> {
192 if scale.is_finite() && scale > 0.0 {
193 Ok(())
194 } else {
195 Err("irreversible quantization scale must be finite and greater than zero")
196 }
197}
198
199pub(super) fn validate_irreversible_quantization_profile(
200 options: &EncodeOptions,
201) -> Result<(), &'static str> {
202 validate_irreversible_quantization_scale(options.irreversible_quantization_scale)?;
203 if quantize::subband_scales_all_valid(options.irreversible_quantization_subband_scales) {
204 Ok(())
205 } else {
206 Err("irreversible quantization subband scales must be finite and greater than zero")
207 }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub(super) struct CodeBlockGeometry {
214 pub(super) width: u32,
215 pub(super) height: u32,
216}
217
218pub(super) fn validate_code_block_geometry(
221 options: &EncodeOptions,
222) -> Result<CodeBlockGeometry, &'static str> {
223 const MAX_STORED_EXPONENT: u8 = 8;
224 const MAX_COMBINED_ACTUAL_EXPONENT: u8 = 12;
225
226 if options.code_block_width_exp > MAX_STORED_EXPONENT {
227 return Err("code-block width exponent exceeds supported range");
228 }
229 if options.code_block_height_exp > MAX_STORED_EXPONENT {
230 return Err("code-block height exponent exceeds supported range");
231 }
232 let width_exponent = options
233 .code_block_width_exp
234 .checked_add(2)
235 .ok_or("code-block width exponent exceeds supported range")?;
236 let height_exponent = options
237 .code_block_height_exp
238 .checked_add(2)
239 .ok_or("code-block height exponent exceeds supported range")?;
240 let combined_exponent = width_exponent
241 .checked_add(height_exponent)
242 .ok_or("code-block combined exponent exceeds supported range")?;
243 if combined_exponent > MAX_COMBINED_ACTUAL_EXPONENT {
244 return Err("code-block dimensions exceed JPEG 2000 Part 1 area limit");
245 }
246 let width = 1_u32
247 .checked_shl(u32::from(width_exponent))
248 .ok_or("code-block width exponent exceeds supported range")?;
249 let height = 1_u32
250 .checked_shl(u32::from(height_exponent))
251 .ok_or("code-block height exponent exceeds supported range")?;
252 Ok(CodeBlockGeometry { width, height })
253}
254
255pub(super) fn validate_precinct_exponents_for_options(
256 options: &EncodeOptions,
257 num_decomposition_levels: u8,
258) -> Result<(), &'static str> {
259 validate_code_block_geometry(options)?;
260 if options.precinct_exponents.is_empty() {
261 return Ok(());
262 }
263
264 let expected = usize::from(num_decomposition_levels) + 1;
265 if options.precinct_exponents.len() != expected {
266 return Err("precinct exponent count must match resolution level count");
267 }
268 if options
269 .precinct_exponents
270 .iter()
271 .any(|&(horizontal_exponent, vertical_exponent)| {
272 horizontal_exponent > 15 || vertical_exponent > 15
273 })
274 {
275 return Err("precinct exponents must fit in COD marker nybbles");
276 }
277 let code_block_horizontal_exponent = options
278 .code_block_width_exp
279 .checked_add(2)
280 .ok_or("code-block width exponent exceeds supported range")?;
281 let code_block_vertical_exponent = options
282 .code_block_height_exp
283 .checked_add(2)
284 .ok_or("code-block height exponent exceeds supported range")?;
285 for (resolution, &(horizontal_exponent, vertical_exponent)) in
286 options.precinct_exponents.iter().enumerate()
287 {
288 let minimum_horizontal_exponent = if resolution == 0 {
289 code_block_horizontal_exponent
290 } else {
291 code_block_horizontal_exponent
292 .checked_add(1)
293 .ok_or("code-block width exponent exceeds supported range")?
294 };
295 let minimum_vertical_exponent = if resolution == 0 {
296 code_block_vertical_exponent
297 } else {
298 code_block_vertical_exponent
299 .checked_add(1)
300 .ok_or("code-block height exponent exceeds supported range")?
301 };
302 if horizontal_exponent < minimum_horizontal_exponent
303 || vertical_exponent < minimum_vertical_exponent
304 {
305 return Err("precinct exponents must not reduce encoder code-block dimensions");
306 }
307 }
308 Ok(())
309}