1use alloc::vec::Vec;
4
5use j2k_core::BackendKind;
6
7use crate::J2kEncodeDispatchReport;
8
9pub(super) use j2k_types::{
10 MAX_JPEG2000_PART1_COMPONENTS,
11 MAX_JPEG2000_PART1_SAMPLE_BIT_DEPTH as MAX_PART1_SAMPLE_BIT_DEPTH,
12};
13pub(super) const MAX_RAW_PIXEL_ENCODE_BIT_DEPTH: u8 = 24;
14pub(super) const MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES: u16 = 37;
15pub(super) const MAX_HTJ2K_ENCODE_BITPLANES: u16 = 31;
16
17macro_rules! define_encoded_j2k {
18 (
19 $(#[$attr:meta])*
20 pub struct $name:ident {
21 $($extra_fields:tt)*
22 }
23 ) => {
24 $(#[$attr])*
25 pub struct $name {
26 pub codestream: Vec<u8>,
28 pub backend: BackendKind,
30 pub dispatch_report: J2kEncodeDispatchReport,
36 pub width: u32,
38 pub height: u32,
40 pub components: u16,
42 pub bit_depth: u8,
44 pub signed: bool,
46 $($extra_fields)*
47 }
48 };
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
53pub enum EncodeBackendPreference {
54 #[default]
56 Auto,
57 CpuOnly,
59 RequireDevice,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
65pub enum J2kProgressionOrder {
66 #[default]
68 Lrcp,
69 Rlcp,
71 Rpcl,
73 Pcrl,
75 Cprl,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
81pub enum J2kBlockCodingMode {
82 #[default]
84 Classic,
85 HighThroughput,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
91pub enum ReversibleTransform {
92 #[default]
94 Rct53,
95 None53,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
101pub enum J2kEncodeValidation {
102 #[default]
105 CpuRoundTrip,
106 External,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
113#[expect(
114 clippy::struct_excessive_bools,
115 reason = "the public options type exposes independent compatibility switches"
116)]
117#[non_exhaustive]
118pub struct J2kLosslessEncodeOptions {
119 pub backend: EncodeBackendPreference,
121 pub block_coding_mode: J2kBlockCodingMode,
123 pub progression: J2kProgressionOrder,
125 pub max_decomposition_levels: Option<u8>,
129 pub tile_size: Option<(u32, u32)>,
131 pub tile_part_packet_limit: Option<u16>,
133 pub quality_layers: u8,
139 pub write_tlm: bool,
141 pub write_plt: bool,
143 pub write_plm: bool,
145 pub write_ppm: bool,
147 pub write_ppt: bool,
149 pub write_sop: bool,
151 pub write_eph: bool,
153 pub reversible_transform: ReversibleTransform,
155 pub validation: J2kEncodeValidation,
157}
158
159impl Default for J2kLosslessEncodeOptions {
160 fn default() -> Self {
161 Self {
162 backend: EncodeBackendPreference::Auto,
163 block_coding_mode: J2kBlockCodingMode::Classic,
164 progression: J2kProgressionOrder::Lrcp,
165 max_decomposition_levels: None,
166 tile_size: None,
167 tile_part_packet_limit: None,
168 quality_layers: 1,
169 write_tlm: false,
170 write_plt: false,
171 write_plm: false,
172 write_ppm: false,
173 write_ppt: false,
174 write_sop: false,
175 write_eph: false,
176 reversible_transform: ReversibleTransform::Rct53,
177 validation: J2kEncodeValidation::CpuRoundTrip,
178 }
179 }
180}
181
182impl J2kLosslessEncodeOptions {
183 pub const fn new(
185 backend: EncodeBackendPreference,
186 block_coding_mode: J2kBlockCodingMode,
187 progression: J2kProgressionOrder,
188 max_decomposition_levels: Option<u8>,
189 reversible_transform: ReversibleTransform,
190 validation: J2kEncodeValidation,
191 ) -> Self {
192 Self {
193 backend,
194 block_coding_mode,
195 progression,
196 max_decomposition_levels,
197 tile_size: None,
198 tile_part_packet_limit: None,
199 quality_layers: 1,
200 write_tlm: false,
201 write_plt: false,
202 write_plm: false,
203 write_ppm: false,
204 write_ppt: false,
205 write_sop: false,
206 write_eph: false,
207 reversible_transform,
208 validation,
209 }
210 }
211
212 #[must_use]
214 pub const fn with_backend(mut self, backend: EncodeBackendPreference) -> Self {
215 self.backend = backend;
216 self
217 }
218
219 #[must_use]
221 pub const fn with_accelerated_backend(self) -> Self {
222 self.with_backend(EncodeBackendPreference::Auto)
223 }
224
225 #[must_use]
227 pub const fn with_cpu_only_backend(self) -> Self {
228 self.with_backend(EncodeBackendPreference::CpuOnly)
229 }
230
231 #[must_use]
233 pub const fn with_strict_device_backend(self) -> Self {
234 self.with_backend(EncodeBackendPreference::RequireDevice)
235 }
236
237 #[must_use]
239 pub const fn with_block_coding_mode(mut self, block_coding_mode: J2kBlockCodingMode) -> Self {
240 self.block_coding_mode = block_coding_mode;
241 self
242 }
243
244 #[must_use]
246 pub const fn with_progression(mut self, progression: J2kProgressionOrder) -> Self {
247 self.progression = progression;
248 self
249 }
250
251 #[must_use]
253 pub const fn with_max_decomposition_levels(
254 mut self,
255 max_decomposition_levels: Option<u8>,
256 ) -> Self {
257 self.max_decomposition_levels = max_decomposition_levels;
258 self
259 }
260
261 #[must_use]
263 pub const fn with_tile_size(mut self, tile_size: Option<(u32, u32)>) -> Self {
264 self.tile_size = tile_size;
265 self
266 }
267
268 #[must_use]
270 pub const fn with_tile_part_packet_limit(
271 mut self,
272 tile_part_packet_limit: Option<u16>,
273 ) -> Self {
274 self.tile_part_packet_limit = tile_part_packet_limit;
275 self
276 }
277
278 #[must_use]
280 pub const fn with_quality_layers(mut self, quality_layers: u8) -> Self {
281 self.quality_layers = quality_layers;
282 self
283 }
284
285 #[must_use]
287 pub fn with_marker_segments(mut self, marker_segments: &[J2kMarkerSegment]) -> Self {
288 self.write_tlm = false;
289 self.write_plt = false;
290 self.write_plm = false;
291 self.write_ppm = false;
292 self.write_ppt = false;
293 self.write_sop = false;
294 self.write_eph = false;
295 for marker in marker_segments {
296 match marker {
297 J2kMarkerSegment::Sop => self.write_sop = true,
298 J2kMarkerSegment::Eph => self.write_eph = true,
299 J2kMarkerSegment::Tlm => self.write_tlm = true,
300 J2kMarkerSegment::Plt => self.write_plt = true,
301 J2kMarkerSegment::Plm => self.write_plm = true,
302 J2kMarkerSegment::Ppm => self.write_ppm = true,
303 J2kMarkerSegment::Ppt => self.write_ppt = true,
304 }
305 }
306 self
307 }
308
309 #[must_use]
311 pub const fn with_reversible_transform(
312 mut self,
313 reversible_transform: ReversibleTransform,
314 ) -> Self {
315 self.reversible_transform = reversible_transform;
316 self
317 }
318
319 #[must_use]
321 pub const fn with_validation(mut self, validation: J2kEncodeValidation) -> Self {
322 self.validation = validation;
323 self
324 }
325}
326
327#[derive(Debug, Clone, Copy, PartialEq)]
329pub enum J2kRateTarget {
330 BitsPerPixel(f64),
332 Bytes(u64),
334 PsnrDb(f64),
336}
337
338#[derive(Debug, Clone, Copy, PartialEq)]
340pub struct J2kQualityLayer {
341 pub target: J2kRateTarget,
343}
344
345impl J2kQualityLayer {
346 #[must_use]
348 pub const fn new(target: J2kRateTarget) -> Self {
349 Self { target }
350 }
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
355pub enum J2kMarkerSegment {
356 Sop,
358 Eph,
360 Tlm,
362 Plt,
364 Plm,
366 Ppm,
368 Ppt,
370}
371
372#[derive(Debug, Clone, PartialEq)]
374#[non_exhaustive]
375pub struct J2kLossyEncodeOptions {
376 pub backend: EncodeBackendPreference,
378 pub block_coding_mode: J2kBlockCodingMode,
380 pub progression: J2kProgressionOrder,
382 pub max_decomposition_levels: Option<u8>,
384 pub rate_target: Option<J2kRateTarget>,
386 pub quality_layers: Vec<J2kQualityLayer>,
388 pub tile_size: Option<(u32, u32)>,
390 pub tile_part_packet_limit: Option<u16>,
392 pub precinct_exponents: Vec<(u8, u8)>,
394 pub marker_segments: Vec<J2kMarkerSegment>,
396 pub psnr_tolerance_db: f64,
398 pub psnr_iteration_budget: u8,
400 pub validation: J2kEncodeValidation,
402}
403
404impl Default for J2kLossyEncodeOptions {
405 fn default() -> Self {
406 Self {
407 backend: EncodeBackendPreference::Auto,
408 block_coding_mode: J2kBlockCodingMode::Classic,
409 progression: J2kProgressionOrder::Lrcp,
410 max_decomposition_levels: None,
411 rate_target: None,
412 quality_layers: Vec::new(),
413 tile_size: None,
414 tile_part_packet_limit: None,
415 precinct_exponents: Vec::new(),
416 marker_segments: Vec::new(),
417 psnr_tolerance_db: 0.25,
418 psnr_iteration_budget: 8,
419 validation: J2kEncodeValidation::CpuRoundTrip,
420 }
421 }
422}
423
424impl J2kLossyEncodeOptions {
425 #[must_use]
427 pub fn with_backend(mut self, backend: EncodeBackendPreference) -> Self {
428 self.backend = backend;
429 self
430 }
431
432 #[must_use]
434 pub fn with_accelerated_backend(self) -> Self {
435 self.with_backend(EncodeBackendPreference::Auto)
436 }
437
438 #[must_use]
440 pub fn with_cpu_only_backend(self) -> Self {
441 self.with_backend(EncodeBackendPreference::CpuOnly)
442 }
443
444 #[must_use]
446 pub fn with_strict_device_backend(self) -> Self {
447 self.with_backend(EncodeBackendPreference::RequireDevice)
448 }
449
450 #[must_use]
452 pub fn with_block_coding_mode(mut self, block_coding_mode: J2kBlockCodingMode) -> Self {
453 self.block_coding_mode = block_coding_mode;
454 self
455 }
456
457 #[must_use]
459 pub fn with_progression(mut self, progression: J2kProgressionOrder) -> Self {
460 self.progression = progression;
461 self
462 }
463
464 #[must_use]
466 pub fn with_max_decomposition_levels(mut self, max_decomposition_levels: Option<u8>) -> Self {
467 self.max_decomposition_levels = max_decomposition_levels;
468 self
469 }
470
471 #[must_use]
473 pub fn with_rate_target(mut self, rate_target: Option<J2kRateTarget>) -> Self {
474 self.rate_target = rate_target;
475 self
476 }
477
478 #[must_use]
480 pub fn with_quality_layers(mut self, quality_layers: Vec<J2kQualityLayer>) -> Self {
481 self.quality_layers = quality_layers;
482 self
483 }
484
485 #[must_use]
487 pub fn with_tile_size(mut self, tile_size: Option<(u32, u32)>) -> Self {
488 self.tile_size = tile_size;
489 self
490 }
491
492 #[must_use]
494 pub fn with_tile_part_packet_limit(mut self, tile_part_packet_limit: Option<u16>) -> Self {
495 self.tile_part_packet_limit = tile_part_packet_limit;
496 self
497 }
498
499 #[must_use]
501 pub fn with_marker_segments(mut self, marker_segments: Vec<J2kMarkerSegment>) -> Self {
502 self.marker_segments = marker_segments;
503 self
504 }
505
506 #[must_use]
508 pub fn with_validation(mut self, validation: J2kEncodeValidation) -> Self {
509 self.validation = validation;
510 self
511 }
512}
513
514define_encoded_j2k! {
515 #[derive(Debug, Clone, PartialEq, Eq)]
517 pub struct EncodedJ2k {
518 }
519}
520
521#[derive(Debug, Clone, PartialEq)]
523pub struct J2kLossyEncodeReport {
524 pub target: Option<J2kRateTarget>,
526 pub quality_layers: u16,
528 pub quantization_scale: f32,
530 pub actual_bytes: u64,
532 pub actual_bits_per_pixel: f64,
534 pub psnr_db: Option<f64>,
536 pub ht_rate_granularity_bytes: Option<u64>,
538}
539
540define_encoded_j2k! {
541 #[derive(Debug, Clone, PartialEq)]
543 pub struct EncodedLossyJ2k {
544 pub report: J2kLossyEncodeReport,
546 }
547}