1use alloc::vec::Vec;
6use j2k_types::encode_geometry::{
7 code_block_dimension, code_block_dimensions, CodeBlockGeometryError, EncodeCodeBlockDimensions,
8};
9
10use super::super::quantize;
11use crate::IrreversibleQuantizationSubbandScales;
12
13#[derive(Debug, Clone)]
15#[expect(
16 clippy::struct_excessive_bools,
17 reason = "the public options expose independent JPEG 2000 coding and marker switches"
18)]
19pub struct EncodeOptions {
20 pub num_decomposition_levels: u8,
22 pub reversible: bool,
24 pub code_block_width_exp: u8,
26 pub code_block_height_exp: u8,
28 pub guard_bits: u8,
30 pub use_ht_block_coding: bool,
32 pub progression_order: EncodeProgressionOrder,
34 pub write_tlm: bool,
36 pub write_plt: bool,
38 pub write_plm: bool,
40 pub write_ppm: bool,
42 pub write_ppt: bool,
44 pub write_sop: bool,
46 pub write_eph: bool,
48 pub use_mct: bool,
50 pub num_layers: u8,
52 pub quality_layer_byte_targets: Vec<u64>,
54 pub validate_high_throughput_codestream: bool,
56 pub irreversible_quantization_scale: f32,
61 pub irreversible_quantization_subband_scales: IrreversibleQuantizationSubbandScales,
64 pub component_sampling: Option<Vec<(u8, u8)>>,
70 pub roi_component_shifts: Vec<u8>,
76 pub tile_size: Option<(u32, u32)>,
78 pub tile_part_packet_limit: Option<u16>,
80 pub precinct_exponents: Vec<(u8, u8)>,
82}
83
84#[derive(Debug, Clone, Copy)]
86pub struct EncodeComponentPlane<'a> {
87 pub data: &'a [u8],
89 pub x_rsiz: u8,
91 pub y_rsiz: u8,
93}
94
95#[derive(Debug, Clone, Copy)]
97pub struct EncodeTypedComponentPlane<'a> {
98 pub data: &'a [u8],
100 pub x_rsiz: u8,
102 pub y_rsiz: u8,
104 pub bit_depth: u8,
106 pub signed: bool,
108}
109
110#[derive(Debug, Clone, Copy)]
118pub struct EncodeRoiRegion {
119 pub component: u16,
121 pub x: u32,
123 pub y: u32,
125 pub width: u32,
127 pub height: u32,
129 pub shift: u8,
131}
132
133impl Default for EncodeOptions {
134 fn default() -> Self {
135 Self {
136 num_decomposition_levels: 5,
137 reversible: true,
138 code_block_width_exp: 4,
139 code_block_height_exp: 4,
140 guard_bits: 1,
141 use_ht_block_coding: false,
142 progression_order: EncodeProgressionOrder::Lrcp,
143 write_tlm: false,
144 write_plt: false,
145 write_plm: false,
146 write_ppm: false,
147 write_ppt: false,
148 write_sop: false,
149 write_eph: false,
150 use_mct: true,
151 num_layers: 1,
152 quality_layer_byte_targets: Vec::new(),
153 validate_high_throughput_codestream: true,
154 irreversible_quantization_scale: 1.0,
155 irreversible_quantization_subband_scales:
156 IrreversibleQuantizationSubbandScales::default(),
157 component_sampling: None,
158 roi_component_shifts: Vec::new(),
159 tile_size: None,
160 tile_part_packet_limit: None,
161 precinct_exponents: Vec::new(),
162 }
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
168pub enum EncodeProgressionOrder {
169 #[default]
171 Lrcp,
172 Rlcp,
174 Rpcl,
176 Pcrl,
178 Cprl,
180}
181
182impl EncodeProgressionOrder {
183 pub(crate) const fn packetization_order(self) -> crate::J2kPacketizationProgressionOrder {
184 match self {
185 Self::Lrcp => crate::J2kPacketizationProgressionOrder::Lrcp,
186 Self::Rlcp => crate::J2kPacketizationProgressionOrder::Rlcp,
187 Self::Rpcl => crate::J2kPacketizationProgressionOrder::Rpcl,
188 Self::Pcrl => crate::J2kPacketizationProgressionOrder::Pcrl,
189 Self::Cprl => crate::J2kPacketizationProgressionOrder::Cprl,
190 }
191 }
192}
193
194fn validate_irreversible_quantization_scale(scale: f32) -> Result<(), &'static str> {
195 if scale.is_finite() && scale > 0.0 {
196 Ok(())
197 } else {
198 Err("irreversible quantization scale must be finite and greater than zero")
199 }
200}
201
202pub(super) fn validate_irreversible_quantization_profile(
203 options: &EncodeOptions,
204) -> Result<(), &'static str> {
205 validate_irreversible_quantization_scale(options.irreversible_quantization_scale)?;
206 if quantize::subband_scales_all_valid(options.irreversible_quantization_subband_scales) {
207 Ok(())
208 } else {
209 Err("irreversible quantization subband scales must be finite and greater than zero")
210 }
211}
212
213pub(super) type CodeBlockGeometry = EncodeCodeBlockDimensions;
216
217pub(super) fn validate_code_block_geometry(
220 options: &EncodeOptions,
221) -> Result<CodeBlockGeometry, &'static str> {
222 code_block_dimension(options.code_block_width_exp)
223 .map_err(|_| "code-block width exponent exceeds supported range")?;
224 code_block_dimension(options.code_block_height_exp)
225 .map_err(|_| "code-block height exponent exceeds supported range")?;
226 code_block_dimensions(options.code_block_width_exp, options.code_block_height_exp).map_err(
227 |error| match error {
228 CodeBlockGeometryError::AreaTooLarge => {
229 "code-block dimensions exceed JPEG 2000 Part 1 area limit"
230 }
231 CodeBlockGeometryError::DimensionTooSmall
232 | CodeBlockGeometryError::DimensionNotPowerOfTwo
233 | CodeBlockGeometryError::StoredExponentTooLarge => {
234 "code-block exponent exceeds supported range"
235 }
236 },
237 )
238}
239
240pub(super) fn validate_precinct_exponents_for_options(
241 options: &EncodeOptions,
242 num_decomposition_levels: u8,
243) -> Result<(), &'static str> {
244 validate_code_block_geometry(options)?;
245 if options.precinct_exponents.is_empty() {
246 return Ok(());
247 }
248
249 let expected = usize::from(num_decomposition_levels) + 1;
250 if options.precinct_exponents.len() != expected {
251 return Err("precinct exponent count must match resolution level count");
252 }
253 if options
254 .precinct_exponents
255 .iter()
256 .any(|&(horizontal_exponent, vertical_exponent)| {
257 horizontal_exponent > 15 || vertical_exponent > 15
258 })
259 {
260 return Err("precinct exponents must fit in COD marker nybbles");
261 }
262 let code_block_horizontal_exponent = options
263 .code_block_width_exp
264 .checked_add(2)
265 .ok_or("code-block width exponent exceeds supported range")?;
266 let code_block_vertical_exponent = options
267 .code_block_height_exp
268 .checked_add(2)
269 .ok_or("code-block height exponent exceeds supported range")?;
270 for (resolution, &(horizontal_exponent, vertical_exponent)) in
271 options.precinct_exponents.iter().enumerate()
272 {
273 let minimum_horizontal_exponent = if resolution == 0 {
274 code_block_horizontal_exponent
275 } else {
276 code_block_horizontal_exponent
277 .checked_add(1)
278 .ok_or("code-block width exponent exceeds supported range")?
279 };
280 let minimum_vertical_exponent = if resolution == 0 {
281 code_block_vertical_exponent
282 } else {
283 code_block_vertical_exponent
284 .checked_add(1)
285 .ok_or("code-block height exponent exceeds supported range")?
286 };
287 if horizontal_exponent < minimum_horizontal_exponent
288 || vertical_exponent < minimum_vertical_exponent
289 {
290 return Err("precinct exponents must not reduce encoder code-block dimensions");
291 }
292 }
293 Ok(())
294}