j2k_transcode/jpeg_to_htj2k/
options.rs1use std::vec::Vec;
4
5use crate::allocation::try_vec_from_slice;
6use j2k::IrreversibleQuantizationSubbandScales;
7use j2k::J2kProgressionOrder;
8
9use super::JpegToHtj2kError;
10
11pub const JPEG_TO_HTJ2K_LOSSY_97_QUANTIZATION_SCALE: f32 = 1.9;
18
19#[derive(Debug, Clone, PartialEq)]
21#[expect(
22 clippy::struct_excessive_bools,
23 reason = "stable public options expose independent codec switches rather than an implicit mode matrix"
24)]
25pub struct JpegToHtj2kEncodeOptions {
26 pub num_decomposition_levels: u8,
28 pub reversible: bool,
30 pub code_block_width_exp: u8,
32 pub code_block_height_exp: u8,
34 pub guard_bits: u8,
36 pub use_ht_block_coding: bool,
38 pub progression_order: J2kProgressionOrder,
40 pub write_tlm: bool,
42 pub write_plt: bool,
44 pub write_plm: bool,
46 pub write_ppm: bool,
48 pub write_ppt: bool,
50 pub write_sop: bool,
52 pub write_eph: bool,
54 pub use_mct: bool,
56 pub num_layers: u8,
58 pub quality_layer_byte_targets: Vec<u64>,
60 pub validate_high_throughput_codestream: bool,
62 pub irreversible_quantization_scale: f32,
64 pub irreversible_quantization_subband_scales: IrreversibleQuantizationSubbandScales,
66 pub component_sampling: Option<Vec<(u8, u8)>>,
68 pub tile_size: Option<(u32, u32)>,
70 pub tile_part_packet_limit: Option<u16>,
72 pub precinct_exponents: Vec<(u8, u8)>,
74}
75
76impl Default for JpegToHtj2kEncodeOptions {
77 fn default() -> Self {
78 Self {
79 num_decomposition_levels: 5,
80 reversible: true,
81 code_block_width_exp: 4,
82 code_block_height_exp: 4,
83 guard_bits: 1,
84 use_ht_block_coding: false,
85 progression_order: J2kProgressionOrder::Lrcp,
86 write_tlm: false,
87 write_plt: false,
88 write_plm: false,
89 write_ppm: false,
90 write_ppt: false,
91 write_sop: false,
92 write_eph: false,
93 use_mct: true,
94 num_layers: 1,
95 quality_layer_byte_targets: Vec::new(),
96 validate_high_throughput_codestream: true,
97 irreversible_quantization_scale: 1.0,
98 irreversible_quantization_subband_scales:
99 IrreversibleQuantizationSubbandScales::default(),
100 component_sampling: None,
101 tile_size: None,
102 tile_part_packet_limit: None,
103 precinct_exponents: Vec::new(),
104 }
105 }
106}
107
108impl JpegToHtj2kEncodeOptions {
109 pub(super) fn to_native(&self) -> Result<j2k_native::EncodeOptions, JpegToHtj2kError> {
110 Ok(j2k_native::EncodeOptions {
111 num_decomposition_levels: self.num_decomposition_levels,
112 reversible: self.reversible,
113 code_block_width_exp: self.code_block_width_exp,
114 code_block_height_exp: self.code_block_height_exp,
115 guard_bits: self.guard_bits,
116 use_ht_block_coding: self.use_ht_block_coding,
117 progression_order: native_progression_order(self.progression_order),
118 write_tlm: self.write_tlm,
119 write_plt: self.write_plt,
120 write_plm: self.write_plm,
121 write_ppm: self.write_ppm,
122 write_ppt: self.write_ppt,
123 write_sop: self.write_sop,
124 write_eph: self.write_eph,
125 use_mct: self.use_mct,
126 num_layers: self.num_layers,
127 quality_layer_byte_targets: try_vec_from_slice(&self.quality_layer_byte_targets)?,
128 validate_high_throughput_codestream: self.validate_high_throughput_codestream,
129 irreversible_quantization_scale: self.irreversible_quantization_scale,
130 irreversible_quantization_subband_scales: self.irreversible_quantization_subband_scales,
131 component_sampling: self
132 .component_sampling
133 .as_deref()
134 .map(try_vec_from_slice)
135 .transpose()?,
136 tile_size: self.tile_size,
137 tile_part_packet_limit: self.tile_part_packet_limit,
138 precinct_exponents: try_vec_from_slice(&self.precinct_exponents)?,
139 roi_component_shifts: Vec::new(),
140 })
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct JpegToHtj2kOptions {
147 pub encode_options: JpegToHtj2kEncodeOptions,
149 pub coefficient_path: JpegToHtj2kCoefficientPath,
151 pub validate_against_float_reference: bool,
155 pub validate_against_integer_reference: bool,
160}
161
162impl Default for JpegToHtj2kOptions {
163 fn default() -> Self {
164 Self::lossless_53()
165 }
166}
167
168impl JpegToHtj2kOptions {
169 #[must_use]
171 pub fn lossless_53() -> Self {
172 Self {
173 encode_options: transcode_encode_options(true),
174 coefficient_path: JpegToHtj2kCoefficientPath::IntegerDirect53,
175 validate_against_float_reference: false,
176 validate_against_integer_reference: false,
177 }
178 }
179
180 #[must_use]
182 pub fn lossy_97() -> Self {
183 let mut encode_options = transcode_encode_options(false);
184 encode_options.irreversible_quantization_scale = JPEG_TO_HTJ2K_LOSSY_97_QUANTIZATION_SCALE;
185 Self {
186 encode_options,
187 coefficient_path: JpegToHtj2kCoefficientPath::FloatDirectLinear97,
188 validate_against_float_reference: false,
189 validate_against_integer_reference: false,
190 }
191 }
192}
193
194fn transcode_encode_options(reversible: bool) -> JpegToHtj2kEncodeOptions {
195 JpegToHtj2kEncodeOptions {
196 num_decomposition_levels: 1,
197 reversible,
198 use_ht_block_coding: true,
199 use_mct: false,
200 validate_high_throughput_codestream: false,
201 ..JpegToHtj2kEncodeOptions::default()
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
207pub enum JpegToHtj2kCoefficientPath {
208 IntegerDirect53,
213 FloatDirectLinear53,
218 FloatDirectLinear97,
222}
223
224fn native_progression_order(
225 progression: J2kProgressionOrder,
226) -> j2k_native::EncodeProgressionOrder {
227 match progression {
228 J2kProgressionOrder::Lrcp => j2k_native::EncodeProgressionOrder::Lrcp,
229 J2kProgressionOrder::Rlcp => j2k_native::EncodeProgressionOrder::Rlcp,
230 J2kProgressionOrder::Rpcl => j2k_native::EncodeProgressionOrder::Rpcl,
231 J2kProgressionOrder::Pcrl => j2k_native::EncodeProgressionOrder::Pcrl,
232 J2kProgressionOrder::Cprl => j2k_native::EncodeProgressionOrder::Cprl,
233 }
234}