1use alloc::vec;
9use alloc::vec::Vec;
10use core::cmp::Ordering;
11use core::ops::Range;
12
13#[cfg(feature = "parallel")]
14use rayon::prelude::*;
15
16use super::bitplane_encode;
17use super::build::SubBandType;
18use super::codestream::CodeBlockStyle;
19use super::codestream_write::{self, BlockCodingMode, EncodeComponentSampleInfo, EncodeParams};
20use super::fdwt::{self, DwtDecomposition};
21use super::forward_mct;
22use super::ht_block_encode;
23use super::packet_encode::{self, CodeBlockPacketData, ResolutionPacket, SubbandPrecinct};
24pub use super::quantize::irreversible_quantization_step_for_subband;
25use super::quantize::{self, QuantStepSize};
26use crate::math::{floor_f32, log2_f32};
27use crate::profile;
28use crate::{
29 CpuOnlyJ2kEncodeStageAccelerator, EncodedHtJ2kCodeBlock, EncodedJ2kCodeBlock,
30 IrreversibleQuantizationSubbandScales, J2kDeinterleaveToF32Job, J2kEncodeStageAccelerator,
31 J2kForwardDwt53Job, J2kForwardDwt53Level, J2kForwardDwt53Output, J2kForwardDwt97Job,
32 J2kForwardDwt97Level, J2kForwardDwt97Output, J2kForwardIctJob, J2kForwardRctJob,
33 J2kHtSubbandEncodeJob, J2kHtj2kTileEncodeJob, J2kPacketizationBlockCodingMode,
34 J2kPacketizationCodeBlock, J2kPacketizationEncodeJob, J2kPacketizationPacketDescriptor,
35 J2kPacketizationResolution, J2kPacketizationSubband, J2kQuantizeSubbandJob, J2kSubBandType,
36 J2kTier1CodeBlockEncodeJob, PrecomputedHtj2k53Component, PrecomputedHtj2k53Image,
37 PrecomputedHtj2k97Component, PrecomputedHtj2k97Image, PreencodedHtj2k97CodeBlock,
38 PreencodedHtj2k97CompactCodeBlock, PreencodedHtj2k97CompactComponent,
39 PreencodedHtj2k97CompactImage, PreencodedHtj2k97CompactResolution,
40 PreencodedHtj2k97CompactSubband, PreencodedHtj2k97Component, PreencodedHtj2k97Image,
41 PreencodedHtj2k97Resolution, PreencodedHtj2k97Subband, PrequantizedHtj2k97Component,
42 PrequantizedHtj2k97Image, PrequantizedHtj2k97Resolution, PrequantizedHtj2k97Subband,
43 MAX_J2K_SPEC_COMPONENTS,
44};
45use crate::{DecodeSettings, Image};
46
47const HT_CPU_PARALLEL_FALLBACK_MIN_JOBS: usize = 4;
48const MAX_RAW_PIXEL_ENCODE_BIT_DEPTH: u8 = 24;
49const MAX_PART1_SAMPLE_BIT_DEPTH: u8 = 38;
50const MAX_REVERSIBLE_NO_QUANT_EXPONENT: u16 = 31;
51const MAX_REVERSIBLE_NO_QUANT_GUARD_BITS: u8 = 7;
52const MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES: u16 =
53 MAX_REVERSIBLE_NO_QUANT_GUARD_BITS as u16 + MAX_REVERSIBLE_NO_QUANT_EXPONENT - 1;
54const MAX_CLASSIC_ROI_CODED_BITPLANES: u8 = 55;
58const MAX_HT_ROI_CODED_BITPLANES: u8 = 31;
59
60#[derive(Debug, Clone)]
62pub struct EncodeOptions {
63 pub num_decomposition_levels: u8,
65 pub reversible: bool,
67 pub code_block_width_exp: u8,
69 pub code_block_height_exp: u8,
71 pub guard_bits: u8,
73 pub use_ht_block_coding: bool,
75 pub progression_order: EncodeProgressionOrder,
77 pub write_tlm: bool,
79 pub write_plt: bool,
81 pub write_plm: bool,
83 pub write_ppm: bool,
85 pub write_ppt: bool,
87 pub write_sop: bool,
89 pub write_eph: bool,
91 pub use_mct: bool,
93 pub num_layers: u8,
95 pub quality_layer_byte_targets: Vec<u64>,
97 pub validate_high_throughput_codestream: bool,
99 pub irreversible_quantization_scale: f32,
104 pub irreversible_quantization_subband_scales: IrreversibleQuantizationSubbandScales,
107 pub component_sampling: Option<Vec<(u8, u8)>>,
113 pub roi_component_shifts: Vec<u8>,
119 pub tile_size: Option<(u32, u32)>,
121 pub tile_part_packet_limit: Option<u16>,
123 pub precinct_exponents: Vec<(u8, u8)>,
125}
126
127#[derive(Debug, Clone, Copy)]
129pub struct EncodeComponentPlane<'a> {
130 pub data: &'a [u8],
132 pub x_rsiz: u8,
134 pub y_rsiz: u8,
136}
137
138#[derive(Debug, Clone, Copy)]
140pub struct EncodeTypedComponentPlane<'a> {
141 pub data: &'a [u8],
143 pub x_rsiz: u8,
145 pub y_rsiz: u8,
147 pub bit_depth: u8,
149 pub signed: bool,
151}
152
153#[derive(Debug, Clone, Copy)]
161pub struct EncodeRoiRegion {
162 pub component: u16,
164 pub x: u32,
166 pub y: u32,
168 pub width: u32,
170 pub height: u32,
172 pub shift: u8,
174}
175
176impl Default for EncodeOptions {
177 fn default() -> Self {
178 Self {
179 num_decomposition_levels: 5,
180 reversible: true,
181 code_block_width_exp: 4,
182 code_block_height_exp: 4,
183 guard_bits: 1,
184 use_ht_block_coding: false,
185 progression_order: EncodeProgressionOrder::Lrcp,
186 write_tlm: false,
187 write_plt: false,
188 write_plm: false,
189 write_ppm: false,
190 write_ppt: false,
191 write_sop: false,
192 write_eph: false,
193 use_mct: true,
194 num_layers: 1,
195 quality_layer_byte_targets: Vec::new(),
196 validate_high_throughput_codestream: true,
197 irreversible_quantization_scale: 1.0,
198 irreversible_quantization_subband_scales:
199 IrreversibleQuantizationSubbandScales::default(),
200 component_sampling: None,
201 roi_component_shifts: Vec::new(),
202 tile_size: None,
203 tile_part_packet_limit: None,
204 precinct_exponents: Vec::new(),
205 }
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
211pub enum EncodeProgressionOrder {
212 #[default]
214 Lrcp,
215 Rlcp,
217 Rpcl,
219 Pcrl,
221 Cprl,
223}
224
225pub fn encode(
239 pixels: &[u8],
240 width: u32,
241 height: u32,
242 num_components: u16,
243 bit_depth: u8,
244 signed: bool,
245 options: &EncodeOptions,
246) -> Result<Vec<u8>, &'static str> {
247 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
248 encode_with_accelerator(
249 pixels,
250 width,
251 height,
252 num_components,
253 bit_depth,
254 signed,
255 options,
256 &mut accelerator,
257 )
258}
259
260pub fn encode_with_accelerator(
266 pixels: &[u8],
267 width: u32,
268 height: u32,
269 num_components: u16,
270 bit_depth: u8,
271 signed: bool,
272 options: &EncodeOptions,
273 accelerator: &mut impl J2kEncodeStageAccelerator,
274) -> Result<Vec<u8>, &'static str> {
275 encode_with_accelerator_and_component_sample_info(
276 pixels,
277 width,
278 height,
279 num_components,
280 bit_depth,
281 signed,
282 options,
283 &[],
284 accelerator,
285 )
286}
287
288fn encode_with_accelerator_and_component_sample_info(
289 pixels: &[u8],
290 width: u32,
291 height: u32,
292 num_components: u16,
293 bit_depth: u8,
294 signed: bool,
295 options: &EncodeOptions,
296 component_sample_info: &[EncodeComponentSampleInfo],
297 accelerator: &mut impl J2kEncodeStageAccelerator,
298) -> Result<Vec<u8>, &'static str> {
299 let block_coding_mode = block_coding_mode(options);
300 let codestream = encode_impl(
301 pixels,
302 width,
303 height,
304 num_components,
305 bit_depth,
306 signed,
307 options,
308 block_coding_mode,
309 &[],
310 component_sample_info,
311 accelerator,
312 )?;
313
314 if block_coding_mode == BlockCodingMode::HighThroughput
315 && options.validate_high_throughput_codestream
316 {
317 validate_htj2k_codestream(
318 &codestream,
319 pixels,
320 width,
321 height,
322 num_components,
323 bit_depth,
324 signed,
325 options.reversible,
326 )?;
327 }
328
329 Ok(codestream)
330}
331
332pub fn encode_with_roi_regions(
338 pixels: &[u8],
339 width: u32,
340 height: u32,
341 num_components: u16,
342 bit_depth: u8,
343 signed: bool,
344 options: &EncodeOptions,
345 roi_regions: &[EncodeRoiRegion],
346) -> Result<Vec<u8>, &'static str> {
347 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
348 encode_with_accelerator_and_roi_regions(
349 pixels,
350 width,
351 height,
352 num_components,
353 bit_depth,
354 signed,
355 options,
356 roi_regions,
357 &mut accelerator,
358 )
359}
360
361pub fn encode_with_accelerator_and_roi_regions(
363 pixels: &[u8],
364 width: u32,
365 height: u32,
366 num_components: u16,
367 bit_depth: u8,
368 signed: bool,
369 options: &EncodeOptions,
370 roi_regions: &[EncodeRoiRegion],
371 accelerator: &mut impl J2kEncodeStageAccelerator,
372) -> Result<Vec<u8>, &'static str> {
373 let block_coding_mode = block_coding_mode(options);
374 let codestream = encode_impl(
375 pixels,
376 width,
377 height,
378 num_components,
379 bit_depth,
380 signed,
381 options,
382 block_coding_mode,
383 roi_regions,
384 &[],
385 accelerator,
386 )?;
387
388 if block_coding_mode == BlockCodingMode::HighThroughput
389 && options.validate_high_throughput_codestream
390 {
391 validate_htj2k_codestream(
392 &codestream,
393 pixels,
394 width,
395 height,
396 num_components,
397 bit_depth,
398 signed,
399 options.reversible,
400 )?;
401 }
402
403 Ok(codestream)
404}
405
406pub fn encode_htj2k(
410 pixels: &[u8],
411 width: u32,
412 height: u32,
413 num_components: u16,
414 bit_depth: u8,
415 signed: bool,
416 options: &EncodeOptions,
417) -> Result<Vec<u8>, &'static str> {
418 let mut options = options.clone();
419 options.use_ht_block_coding = true;
420 encode(
421 pixels,
422 width,
423 height,
424 num_components,
425 bit_depth,
426 signed,
427 &options,
428 )
429}
430
431pub fn encode_component_planes_53(
438 planes: &[EncodeComponentPlane<'_>],
439 width: u32,
440 height: u32,
441 bit_depth: u8,
442 signed: bool,
443 options: &EncodeOptions,
444) -> Result<Vec<u8>, &'static str> {
445 let typed_planes = planes
446 .iter()
447 .map(|plane| EncodeTypedComponentPlane {
448 data: plane.data,
449 x_rsiz: plane.x_rsiz,
450 y_rsiz: plane.y_rsiz,
451 bit_depth,
452 signed,
453 })
454 .collect::<Vec<_>>();
455 encode_typed_component_planes_53(&typed_planes, width, height, options)
456}
457
458pub fn encode_typed_component_planes_53(
466 planes: &[EncodeTypedComponentPlane<'_>],
467 width: u32,
468 height: u32,
469 options: &EncodeOptions,
470) -> Result<Vec<u8>, &'static str> {
471 if width == 0 || height == 0 {
472 return Err("invalid dimensions");
473 }
474 if planes.is_empty() || planes.len() > usize::from(MAX_J2K_SPEC_COMPONENTS) {
475 return Err("unsupported component count");
476 }
477 if planes
478 .iter()
479 .any(|plane| plane.x_rsiz == 0 || plane.y_rsiz == 0)
480 {
481 return Err("component sampling factors must be non-zero");
482 }
483 if planes.iter().any(|plane| plane.bit_depth == 0) {
484 return Err("unsupported bit depth");
485 }
486 if planes
487 .iter()
488 .any(|plane| plane.bit_depth > MAX_PART1_SAMPLE_BIT_DEPTH)
489 {
490 return Err("unsupported bit depth");
491 }
492 if planes
493 .iter()
494 .any(|plane| plane.bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH)
495 {
496 return encode_typed_component_planes_53_i64(planes, width, height, options);
497 }
498
499 let max_levels = planes
500 .iter()
501 .map(|plane| {
502 let component_width = width.div_ceil(u32::from(plane.x_rsiz));
503 let component_height = height.div_ceil(u32::from(plane.y_rsiz));
504 max_decomposition_levels(component_width, component_height)
505 })
506 .min()
507 .unwrap_or(0);
508 let num_levels = options.num_decomposition_levels.min(max_levels);
509 let components = planes
510 .iter()
511 .map(|plane| {
512 let component_width = width.div_ceil(u32::from(plane.x_rsiz));
513 let component_height = height.div_ceil(u32::from(plane.y_rsiz));
514 let samples = component_plane_to_f32(
515 plane.data,
516 component_width,
517 component_height,
518 plane.bit_depth,
519 plane.signed,
520 )?;
521 let dwt = fdwt::forward_dwt(
522 &samples,
523 component_width,
524 component_height,
525 num_levels,
526 true,
527 );
528 Ok(PrecomputedHtj2k53Component {
529 x_rsiz: plane.x_rsiz,
530 y_rsiz: plane.y_rsiz,
531 dwt: forward_dwt53_output_from_decomposition(dwt),
532 })
533 })
534 .collect::<Result<Vec<_>, &'static str>>()?;
535 let max_bit_depth = planes
536 .iter()
537 .map(|plane| plane.bit_depth)
538 .max()
539 .ok_or("unsupported component count")?;
540 let component_sample_info = planes
541 .iter()
542 .map(|plane| EncodeComponentSampleInfo {
543 bit_depth: plane.bit_depth,
544 signed: plane.signed,
545 })
546 .collect::<Vec<_>>();
547 let image = PrecomputedHtj2k53Image {
548 width,
549 height,
550 bit_depth: max_bit_depth,
551 signed: planes.iter().all(|plane| plane.signed),
552 components,
553 };
554
555 if options.use_ht_block_coding {
556 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
557 encode_precomputed_53_with_component_sample_info_and_accelerator(
558 &image,
559 options,
560 false,
561 true,
562 &component_sample_info,
563 &mut accelerator,
564 )
565 } else {
566 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
567 encode_precomputed_53_with_component_sample_info_and_accelerator(
568 &image,
569 options,
570 false,
571 false,
572 &component_sample_info,
573 &mut accelerator,
574 )
575 }
576}
577
578fn encode_typed_component_planes_53_i64(
579 planes: &[EncodeTypedComponentPlane<'_>],
580 width: u32,
581 height: u32,
582 options: &EncodeOptions,
583) -> Result<Vec<u8>, &'static str> {
584 if options.num_layers == 0 || options.num_layers > 32 {
585 return Err("unsupported quality layer count");
586 }
587 if options.write_ppm && options.write_ppt {
588 return Err("PPM and PPT packet header markers are mutually exclusive");
589 }
590 if matches!(options.tile_part_packet_limit, Some(0)) {
591 return Err("tile-part packet limit must be non-zero");
592 }
593 if !options.quality_layer_byte_targets.is_empty()
594 && options.quality_layer_byte_targets.len() != usize::from(options.num_layers)
595 {
596 return Err("quality layer byte target count must match quality layer count");
597 }
598 if let Some((tile_width, tile_height)) = options.tile_size {
599 if tile_width == 0 || tile_height == 0 {
600 return Err("invalid tile dimensions");
601 }
602 }
603
604 let num_components = u16::try_from(planes.len()).map_err(|_| "unsupported component count")?;
605 if let Some((tile_width, tile_height)) = options.tile_size {
606 if tile_width < width || tile_height < height {
607 return encode_typed_component_planes_53_i64_multitile(
608 planes,
609 width,
610 height,
611 options,
612 tile_width,
613 tile_height,
614 num_components,
615 );
616 }
617 }
618 let max_bit_depth = planes
619 .iter()
620 .map(|plane| plane.bit_depth)
621 .max()
622 .ok_or("unsupported component count")?;
623 let num_levels = planes
624 .iter()
625 .map(|plane| {
626 let component_width = width.div_ceil(u32::from(plane.x_rsiz));
627 let component_height = height.div_ceil(u32::from(plane.y_rsiz));
628 max_decomposition_levels(component_width, component_height)
629 })
630 .min()
631 .unwrap_or(0)
632 .min(options.num_decomposition_levels);
633 let requested_guard_bits = options.guard_bits;
634 let guard_bits =
635 reversible_guard_bits_for_marker_limit(max_bit_depth, num_levels, requested_guard_bits)?;
636 let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
637 let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
638 max_bit_depth,
639 num_levels,
640 true,
641 guard_bits,
642 options.irreversible_quantization_scale,
643 options.irreversible_quantization_subband_scales,
644 );
645 if reversible_guard_delta != 0 {
646 adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
647 }
648 let component_sample_info = planes
649 .iter()
650 .map(|plane| EncodeComponentSampleInfo {
651 bit_depth: plane.bit_depth,
652 signed: plane.signed,
653 })
654 .collect::<Vec<_>>();
655 let mut component_step_sizes = component_step_sizes(
656 &component_sample_info,
657 num_levels,
658 true,
659 guard_bits,
660 options.irreversible_quantization_scale,
661 options.irreversible_quantization_subband_scales,
662 );
663 if reversible_guard_delta != 0 {
664 adjust_component_step_sizes_for_guard_delta(
665 &mut component_step_sizes,
666 reversible_guard_delta,
667 )?;
668 }
669 if step_sizes.iter().any(|step| step.exponent > 31)
670 || component_step_sizes
671 .iter()
672 .flatten()
673 .any(|step| step.exponent > 31)
674 {
675 return Err("25-38 bit typed component-plane encode exceeds the current no-quantization guard/exponent signaling limit");
676 }
677
678 let quant_params = step_sizes
679 .iter()
680 .map(|step| (step.exponent, step.mantissa))
681 .collect::<Vec<_>>();
682 let component_quantization_step_sizes = component_step_sizes
683 .iter()
684 .map(|steps| {
685 steps
686 .iter()
687 .map(|step| (step.exponent, step.mantissa))
688 .collect::<Vec<_>>()
689 })
690 .collect::<Vec<_>>();
691 let cb_width = 1u32 << (options.code_block_width_exp + 2);
692 let cb_height = 1u32 << (options.code_block_height_exp + 2);
693 let block_coding_mode = if options.use_ht_block_coding {
694 BlockCodingMode::HighThroughput
695 } else {
696 BlockCodingMode::Classic
697 };
698 let component_sampling = planes
699 .iter()
700 .map(|plane| (plane.x_rsiz, plane.y_rsiz))
701 .collect::<Vec<_>>();
702 let mut high_bit_options = options.clone();
703 high_bit_options.reversible = true;
704 high_bit_options.use_mct = false;
705 high_bit_options.component_sampling = Some(component_sampling.clone());
706 let precinct_exponents = precinct_exponents_for_options(&high_bit_options, num_levels)?;
707 let params = EncodeParams {
708 width,
709 height,
710 tile_width: options
711 .tile_size
712 .map_or(width, |(tile_width, _)| tile_width),
713 tile_height: options
714 .tile_size
715 .map_or(height, |(_, tile_height)| tile_height),
716 num_components,
717 bit_depth: max_bit_depth,
718 signed: planes.iter().all(|plane| plane.signed),
719 component_sample_info,
720 component_quantization_step_sizes,
721 num_decomposition_levels: num_levels,
722 reversible: true,
723 code_block_width_exp: options.code_block_width_exp,
724 code_block_height_exp: options.code_block_height_exp,
725 num_layers: options.num_layers,
726 use_mct: false,
727 guard_bits,
728 block_coding_mode,
729 progression_order: options.progression_order,
730 write_tlm: options.write_tlm,
731 write_plt: options.write_plt,
732 write_plm: options.write_plm,
733 write_ppm: options.write_ppm,
734 write_ppt: options.write_ppt,
735 write_sop: options.write_sop,
736 write_eph: options.write_eph,
737 terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
738 && options.num_layers > 1,
739 component_sampling,
740 roi_component_shifts: vec![0; usize::from(num_components)],
741 precinct_exponents,
742 };
743
744 let ht_target_coding_passes = ht_target_coding_passes_for_options(options);
745 let mut component_resolution_packets = Vec::with_capacity(planes.len());
746 for (component_idx, plane) in planes.iter().enumerate() {
747 let component_width = width.div_ceil(u32::from(plane.x_rsiz));
748 let component_height = height.div_ceil(u32::from(plane.y_rsiz));
749 let samples = typed_component_plane_to_i64(plane, component_width, component_height)?;
750 let decomp = fdwt::forward_dwt_i64(&samples, component_width, component_height, num_levels);
751 let steps = component_step_sizes
752 .get(component_idx)
753 .ok_or("component quantization step count mismatch")?;
754 let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
755 let mut packets = Vec::with_capacity(num_levels as usize + 1);
756
757 let ll_subband = prepare_subband_i64(
758 &decomp.ll,
759 decomp.ll_width,
760 decomp.ll_height,
761 steps
762 .first()
763 .ok_or("reversible quantization step missing")?,
764 guard_bits,
765 cb_width,
766 cb_height,
767 SubBandType::LowLow,
768 0,
769 &[],
770 1,
771 block_coding_mode,
772 ht_target_coding_passes,
773 )?;
774 packets.push(PreparedResolutionPacket {
775 component,
776 resolution: 0,
777 precinct: 0,
778 subbands: vec![ll_subband],
779 });
780
781 for (level_idx, level) in decomp.levels.iter().enumerate() {
782 let step_base = 1 + level_idx * 3;
783 let hl_subband = prepare_subband_i64(
784 &level.hl,
785 level.high_width,
786 level.low_height,
787 steps
788 .get(step_base)
789 .ok_or("reversible quantization step missing")?,
790 guard_bits,
791 cb_width,
792 cb_height,
793 SubBandType::HighLow,
794 0,
795 &[],
796 1,
797 block_coding_mode,
798 ht_target_coding_passes,
799 )?;
800 let lh_subband = prepare_subband_i64(
801 &level.lh,
802 level.low_width,
803 level.high_height,
804 steps
805 .get(step_base + 1)
806 .ok_or("reversible quantization step missing")?,
807 guard_bits,
808 cb_width,
809 cb_height,
810 SubBandType::LowHigh,
811 0,
812 &[],
813 1,
814 block_coding_mode,
815 ht_target_coding_passes,
816 )?;
817 let hh_subband = prepare_subband_i64(
818 &level.hh,
819 level.high_width,
820 level.high_height,
821 steps
822 .get(step_base + 2)
823 .ok_or("reversible quantization step missing")?,
824 guard_bits,
825 cb_width,
826 cb_height,
827 SubBandType::HighHigh,
828 0,
829 &[],
830 1,
831 block_coding_mode,
832 ht_target_coding_passes,
833 )?;
834 packets.push(PreparedResolutionPacket {
835 component,
836 resolution: u32::try_from(level_idx + 1)
837 .map_err(|_| "resolution index exceeds u32")?,
838 precinct: 0,
839 subbands: vec![hl_subband, lh_subband, hh_subband],
840 });
841 }
842 component_resolution_packets.push(packets);
843 }
844
845 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
846 encode_i64_component_resolution_packets(
847 component_resolution_packets,
848 width,
849 height,
850 num_components,
851 num_levels,
852 ¶ms,
853 &quant_params,
854 &high_bit_options,
855 &mut accelerator,
856 )
857}
858
859#[allow(clippy::too_many_arguments)]
860fn encode_typed_component_planes_53_i64_multitile(
861 planes: &[EncodeTypedComponentPlane<'_>],
862 width: u32,
863 height: u32,
864 options: &EncodeOptions,
865 tile_width: u32,
866 tile_height: u32,
867 num_components: u16,
868) -> Result<Vec<u8>, &'static str> {
869 let num_x_tiles = width.div_ceil(tile_width);
870 let num_y_tiles = height.div_ceil(tile_height);
871 let num_tiles = num_x_tiles
872 .checked_mul(num_y_tiles)
873 .ok_or("tile count overflow")?;
874 if num_tiles > u32::from(u16::MAX) + 1 {
875 return Err("multi-tile encode supports at most 65536 tiles");
876 }
877
878 let num_levels = min_sampled_tile_component_decomposition_levels(
879 planes,
880 width,
881 height,
882 tile_width,
883 tile_height,
884 )?
885 .min(options.num_decomposition_levels);
886 let max_bit_depth = planes
887 .iter()
888 .map(|plane| plane.bit_depth)
889 .max()
890 .ok_or("unsupported component count")?;
891 let requested_guard_bits = options.guard_bits;
892 let guard_bits =
893 reversible_guard_bits_for_marker_limit(max_bit_depth, num_levels, requested_guard_bits)?;
894 let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
895 let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
896 max_bit_depth,
897 num_levels,
898 true,
899 guard_bits,
900 options.irreversible_quantization_scale,
901 options.irreversible_quantization_subband_scales,
902 );
903 if reversible_guard_delta != 0 {
904 adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
905 }
906 let component_sample_info = planes
907 .iter()
908 .map(|plane| EncodeComponentSampleInfo {
909 bit_depth: plane.bit_depth,
910 signed: plane.signed,
911 })
912 .collect::<Vec<_>>();
913 let mut component_step_sizes = component_step_sizes(
914 &component_sample_info,
915 num_levels,
916 true,
917 guard_bits,
918 options.irreversible_quantization_scale,
919 options.irreversible_quantization_subband_scales,
920 );
921 if reversible_guard_delta != 0 {
922 adjust_component_step_sizes_for_guard_delta(
923 &mut component_step_sizes,
924 reversible_guard_delta,
925 )?;
926 }
927 if step_sizes.iter().any(|step| step.exponent > 31)
928 || component_step_sizes
929 .iter()
930 .flatten()
931 .any(|step| step.exponent > 31)
932 {
933 return Err("25-38 bit typed component-plane encode exceeds the current no-quantization guard/exponent signaling limit");
934 }
935
936 let quant_params = step_sizes
937 .iter()
938 .map(|step| (step.exponent, step.mantissa))
939 .collect::<Vec<_>>();
940 let component_quantization_step_sizes = component_step_sizes
941 .iter()
942 .map(|steps| {
943 steps
944 .iter()
945 .map(|step| (step.exponent, step.mantissa))
946 .collect::<Vec<_>>()
947 })
948 .collect::<Vec<_>>();
949 let component_sampling = planes
950 .iter()
951 .map(|plane| (plane.x_rsiz, plane.y_rsiz))
952 .collect::<Vec<_>>();
953 let mut high_bit_options = options.clone();
954 high_bit_options.num_decomposition_levels = num_levels;
955 high_bit_options.reversible = true;
956 high_bit_options.use_mct = false;
957 high_bit_options.component_sampling = Some(component_sampling.clone());
958 let precinct_exponents = precinct_exponents_for_options(&high_bit_options, num_levels)?;
959
960 let mut child_options = high_bit_options.clone();
961 child_options.tile_size = None;
962 child_options.write_tlm = false;
963 child_options.write_plt = false;
964 child_options.write_plm = false;
965 child_options.write_ppm = false;
966 child_options.write_ppt = false;
967
968 let block_coding_mode = if options.use_ht_block_coding {
969 BlockCodingMode::HighThroughput
970 } else {
971 BlockCodingMode::Classic
972 };
973 let params = EncodeParams {
974 width,
975 height,
976 tile_width,
977 tile_height,
978 num_components,
979 bit_depth: max_bit_depth,
980 signed: planes.iter().all(|plane| plane.signed),
981 component_sample_info,
982 component_quantization_step_sizes,
983 num_decomposition_levels: num_levels,
984 reversible: true,
985 code_block_width_exp: options.code_block_width_exp,
986 code_block_height_exp: options.code_block_height_exp,
987 num_layers: options.num_layers,
988 use_mct: false,
989 guard_bits,
990 block_coding_mode,
991 progression_order: options.progression_order,
992 write_tlm: options.write_tlm,
993 write_plt: options.write_plt,
994 write_plm: options.write_plm,
995 write_ppm: options.write_ppm,
996 write_ppt: options.write_ppt,
997 write_sop: options.write_sop,
998 write_eph: options.write_eph,
999 terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
1000 && options.num_layers > 1,
1001 component_sampling,
1002 roi_component_shifts: vec![0; usize::from(num_components)],
1003 precinct_exponents,
1004 };
1005
1006 let mut tile_bodies = Vec::with_capacity(num_tiles as usize);
1007 for tile_y in 0..num_y_tiles {
1008 for tile_x in 0..num_x_tiles {
1009 let tile_index = tile_y
1010 .checked_mul(num_x_tiles)
1011 .and_then(|base| base.checked_add(tile_x))
1012 .ok_or("tile index overflow")?;
1013 let tile_index = u16::try_from(tile_index).map_err(|_| "tile index exceeds u16")?;
1014 let x0 = tile_x * tile_width;
1015 let y0 = tile_y * tile_height;
1016 let actual_width = (width - x0).min(tile_width);
1017 let actual_height = (height - y0).min(tile_height);
1018 let tile_plane_data = planes
1019 .iter()
1020 .map(|plane| {
1021 let x_rsiz = u32::from(plane.x_rsiz);
1022 let y_rsiz = u32::from(plane.y_rsiz);
1023 let component_image_width = width.div_ceil(x_rsiz);
1024 let component_image_height = height.div_ceil(y_rsiz);
1025 let (component_x0, component_tile_width) = sampled_tile_component_axis(
1026 x0,
1027 actual_width,
1028 x_rsiz,
1029 component_image_width,
1030 )?;
1031 let (component_y0, component_tile_height) = sampled_tile_component_axis(
1032 y0,
1033 actual_height,
1034 y_rsiz,
1035 component_image_height,
1036 )?;
1037 let data = extract_component_plane_tile(
1038 plane.data,
1039 component_image_width,
1040 component_x0,
1041 component_y0,
1042 component_tile_width,
1043 component_tile_height,
1044 plane.bit_depth,
1045 )?;
1046 Ok((data, component_tile_width, component_tile_height))
1047 })
1048 .collect::<Result<Vec<_>, &'static str>>()?;
1049 let tile_planes = planes
1050 .iter()
1051 .zip(tile_plane_data.iter())
1052 .map(|(plane, (data, _, _))| EncodeTypedComponentPlane {
1053 data,
1054 x_rsiz: plane.x_rsiz,
1055 y_rsiz: plane.y_rsiz,
1056 bit_depth: plane.bit_depth,
1057 signed: plane.signed,
1058 })
1059 .collect::<Vec<_>>();
1060 let component_dimensions = tile_planes
1061 .iter()
1062 .zip(tile_plane_data.iter())
1063 .map(|(_, (_, component_width, component_height))| {
1064 (*component_width, *component_height)
1065 })
1066 .collect::<Vec<_>>();
1067 let component_resolution_packets = prepare_typed_component_planes_i64_packets(
1068 &tile_planes,
1069 &component_dimensions,
1070 &component_step_sizes,
1071 guard_bits,
1072 num_levels,
1073 1u32 << (options.code_block_width_exp + 2),
1074 1u32 << (options.code_block_height_exp + 2),
1075 block_coding_mode,
1076 ht_target_coding_passes_for_options(options),
1077 )?;
1078 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1079 let packetized_tile = packetize_i64_component_resolution_packets(
1080 component_resolution_packets,
1081 actual_width,
1082 actual_height,
1083 num_components,
1084 num_levels,
1085 ¶ms,
1086 &child_options,
1087 &mut accelerator,
1088 )?;
1089 tile_bodies.extend(split_packetized_tile_into_tile_parts(
1090 tile_index,
1091 &packetized_tile.data,
1092 &packetized_tile.packet_lengths,
1093 &packetized_tile.packet_headers,
1094 options.tile_part_packet_limit,
1095 )?);
1096 }
1097 }
1098
1099 let tile_packet_headers = tile_bodies
1100 .iter()
1101 .map(|tile| tile.packet_headers.as_slice())
1102 .collect::<Vec<_>>();
1103 validate_packet_header_marker_payloads(
1104 params.write_ppm,
1105 params.write_ppt,
1106 &tile_packet_headers,
1107 )?;
1108 let tile_parts = tile_bodies
1109 .iter()
1110 .map(|tile| codestream_write::TilePartData {
1111 tile_index: tile.tile_index,
1112 tile_part_index: tile.tile_part_index,
1113 num_tile_parts: tile.num_tile_parts,
1114 data: &tile.data,
1115 packet_lengths: &tile.packet_lengths,
1116 packet_headers: &tile.packet_headers,
1117 })
1118 .collect::<Vec<_>>();
1119
1120 Ok(codestream_write::write_codestream_tiles(
1121 ¶ms,
1122 &tile_parts,
1123 &quant_params,
1124 ))
1125}
1126
1127fn min_sampled_tile_component_decomposition_levels(
1128 planes: &[EncodeTypedComponentPlane<'_>],
1129 width: u32,
1130 height: u32,
1131 tile_width: u32,
1132 tile_height: u32,
1133) -> Result<u8, &'static str> {
1134 let num_x_tiles = width.div_ceil(tile_width);
1135 let num_y_tiles = height.div_ceil(tile_height);
1136 let mut levels: Option<u8> = None;
1137 for tile_y in 0..num_y_tiles {
1138 for tile_x in 0..num_x_tiles {
1139 let x0 = tile_x * tile_width;
1140 let y0 = tile_y * tile_height;
1141 let actual_width = (width - x0).min(tile_width);
1142 let actual_height = (height - y0).min(tile_height);
1143 for plane in planes {
1144 let x_rsiz = u32::from(plane.x_rsiz);
1145 let y_rsiz = u32::from(plane.y_rsiz);
1146 let component_image_width = width.div_ceil(x_rsiz);
1147 let component_image_height = height.div_ceil(y_rsiz);
1148 let (_, component_tile_width) =
1149 sampled_tile_component_axis(x0, actual_width, x_rsiz, component_image_width)?;
1150 let (_, component_tile_height) =
1151 sampled_tile_component_axis(y0, actual_height, y_rsiz, component_image_height)?;
1152 let component_levels =
1153 max_decomposition_levels(component_tile_width, component_tile_height);
1154 levels = Some(levels.map_or(component_levels, |min| min.min(component_levels)));
1155 }
1156 }
1157 }
1158 Ok(levels.unwrap_or(0))
1159}
1160
1161fn sampled_tile_component_axis(
1162 tile_origin: u32,
1163 tile_extent: u32,
1164 sampling: u32,
1165 component_extent: u32,
1166) -> Result<(u32, u32), &'static str> {
1167 let tile_end = tile_origin
1168 .checked_add(tile_extent)
1169 .ok_or("tile component bounds overflow")?;
1170 let start = tile_origin.div_ceil(sampling).min(component_extent);
1171 let end = tile_end.div_ceil(sampling).min(component_extent);
1172 Ok((start, end.saturating_sub(start)))
1173}
1174
1175#[allow(clippy::too_many_arguments)]
1176fn prepare_typed_component_planes_i64_packets(
1177 planes: &[EncodeTypedComponentPlane<'_>],
1178 component_dimensions: &[(u32, u32)],
1179 component_step_sizes: &[Vec<QuantStepSize>],
1180 guard_bits: u8,
1181 num_levels: u8,
1182 cb_width: u32,
1183 cb_height: u32,
1184 block_coding_mode: BlockCodingMode,
1185 ht_target_coding_passes: u8,
1186) -> Result<Vec<Vec<PreparedResolutionPacket>>, &'static str> {
1187 if component_dimensions.len() != planes.len() {
1188 return Err("component dimensions count does not match component count");
1189 }
1190 let mut component_resolution_packets = Vec::with_capacity(planes.len());
1191 for (component_idx, (plane, &(component_width, component_height))) in
1192 planes.iter().zip(component_dimensions).enumerate()
1193 {
1194 let samples = typed_component_plane_to_i64(plane, component_width, component_height)?;
1195 let decomp = fdwt::forward_dwt_i64(&samples, component_width, component_height, num_levels);
1196 let steps = component_step_sizes
1197 .get(component_idx)
1198 .ok_or("component quantization step count mismatch")?;
1199 let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
1200 let mut packets = Vec::with_capacity(num_levels as usize + 1);
1201
1202 let ll_subband = prepare_subband_i64(
1203 &decomp.ll,
1204 decomp.ll_width,
1205 decomp.ll_height,
1206 steps
1207 .first()
1208 .ok_or("reversible quantization step missing")?,
1209 guard_bits,
1210 cb_width,
1211 cb_height,
1212 SubBandType::LowLow,
1213 0,
1214 &[],
1215 1,
1216 block_coding_mode,
1217 ht_target_coding_passes,
1218 )?;
1219 packets.push(PreparedResolutionPacket {
1220 component,
1221 resolution: 0,
1222 precinct: 0,
1223 subbands: vec![ll_subband],
1224 });
1225
1226 for (level_idx, level) in decomp.levels.iter().enumerate() {
1227 let step_base = 1 + level_idx * 3;
1228 let hl_subband = prepare_subband_i64(
1229 &level.hl,
1230 level.high_width,
1231 level.low_height,
1232 steps
1233 .get(step_base)
1234 .ok_or("reversible quantization step missing")?,
1235 guard_bits,
1236 cb_width,
1237 cb_height,
1238 SubBandType::HighLow,
1239 0,
1240 &[],
1241 1,
1242 block_coding_mode,
1243 ht_target_coding_passes,
1244 )?;
1245 let lh_subband = prepare_subband_i64(
1246 &level.lh,
1247 level.low_width,
1248 level.high_height,
1249 steps
1250 .get(step_base + 1)
1251 .ok_or("reversible quantization step missing")?,
1252 guard_bits,
1253 cb_width,
1254 cb_height,
1255 SubBandType::LowHigh,
1256 0,
1257 &[],
1258 1,
1259 block_coding_mode,
1260 ht_target_coding_passes,
1261 )?;
1262 let hh_subband = prepare_subband_i64(
1263 &level.hh,
1264 level.high_width,
1265 level.high_height,
1266 steps
1267 .get(step_base + 2)
1268 .ok_or("reversible quantization step missing")?,
1269 guard_bits,
1270 cb_width,
1271 cb_height,
1272 SubBandType::HighHigh,
1273 0,
1274 &[],
1275 1,
1276 block_coding_mode,
1277 ht_target_coding_passes,
1278 )?;
1279 packets.push(PreparedResolutionPacket {
1280 component,
1281 resolution: u32::try_from(level_idx + 1)
1282 .map_err(|_| "resolution index exceeds u32")?,
1283 precinct: 0,
1284 subbands: vec![hl_subband, lh_subband, hh_subband],
1285 });
1286 }
1287 component_resolution_packets.push(packets);
1288 }
1289
1290 Ok(component_resolution_packets)
1291}
1292
1293pub fn encode_precomputed_j2k_53(
1301 image: &PrecomputedHtj2k53Image,
1302 options: &EncodeOptions,
1303) -> Result<Vec<u8>, &'static str> {
1304 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1305 encode_precomputed_j2k_53_with_mct_and_accelerator(image, options, false, &mut accelerator)
1306}
1307
1308pub fn encode_precomputed_j2k_53_with_accelerator(
1312 image: &PrecomputedHtj2k53Image,
1313 options: &EncodeOptions,
1314 accelerator: &mut impl J2kEncodeStageAccelerator,
1315) -> Result<Vec<u8>, &'static str> {
1316 encode_precomputed_j2k_53_with_mct_and_accelerator(image, options, false, accelerator)
1317}
1318
1319pub fn encode_precomputed_j2k_53_with_mct(
1323 image: &PrecomputedHtj2k53Image,
1324 options: &EncodeOptions,
1325 use_mct: bool,
1326) -> Result<Vec<u8>, &'static str> {
1327 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1328 encode_precomputed_j2k_53_with_mct_and_accelerator(image, options, use_mct, &mut accelerator)
1329}
1330
1331pub fn encode_precomputed_j2k_53_with_mct_and_accelerator(
1335 image: &PrecomputedHtj2k53Image,
1336 options: &EncodeOptions,
1337 use_mct: bool,
1338 accelerator: &mut impl J2kEncodeStageAccelerator,
1339) -> Result<Vec<u8>, &'static str> {
1340 encode_precomputed_53_with_mct_and_accelerator(image, options, use_mct, false, accelerator)
1341}
1342
1343pub fn encode_precomputed_htj2k_53(
1353 image: &PrecomputedHtj2k53Image,
1354 options: &EncodeOptions,
1355) -> Result<Vec<u8>, &'static str> {
1356 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1357 encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, false, &mut accelerator)
1358}
1359
1360pub fn encode_precomputed_htj2k_53_with_accelerator(
1363 image: &PrecomputedHtj2k53Image,
1364 options: &EncodeOptions,
1365 accelerator: &mut impl J2kEncodeStageAccelerator,
1366) -> Result<Vec<u8>, &'static str> {
1367 encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, false, accelerator)
1368}
1369
1370pub fn encode_precomputed_htj2k_53_with_mct(
1376 image: &PrecomputedHtj2k53Image,
1377 options: &EncodeOptions,
1378 use_mct: bool,
1379) -> Result<Vec<u8>, &'static str> {
1380 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1381 encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, use_mct, &mut accelerator)
1382}
1383
1384pub fn encode_precomputed_htj2k_53_with_mct_and_accelerator(
1388 image: &PrecomputedHtj2k53Image,
1389 options: &EncodeOptions,
1390 use_mct: bool,
1391 accelerator: &mut impl J2kEncodeStageAccelerator,
1392) -> Result<Vec<u8>, &'static str> {
1393 encode_precomputed_53_with_mct_and_accelerator(image, options, use_mct, true, accelerator)
1394}
1395
1396fn encode_precomputed_53_with_mct_and_accelerator(
1397 image: &PrecomputedHtj2k53Image,
1398 options: &EncodeOptions,
1399 use_mct: bool,
1400 use_ht_block_coding: bool,
1401 accelerator: &mut impl J2kEncodeStageAccelerator,
1402) -> Result<Vec<u8>, &'static str> {
1403 encode_precomputed_53_with_component_sample_info_and_accelerator(
1404 image,
1405 options,
1406 use_mct,
1407 use_ht_block_coding,
1408 &[],
1409 accelerator,
1410 )
1411}
1412
1413fn encode_precomputed_53_with_component_sample_info_and_accelerator(
1414 image: &PrecomputedHtj2k53Image,
1415 options: &EncodeOptions,
1416 use_mct: bool,
1417 use_ht_block_coding: bool,
1418 component_sample_info: &[EncodeComponentSampleInfo],
1419 accelerator: &mut impl J2kEncodeStageAccelerator,
1420) -> Result<Vec<u8>, &'static str> {
1421 if image.width == 0 || image.height == 0 {
1422 return Err("invalid dimensions");
1423 }
1424 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1425 {
1426 return Err("unsupported component count");
1427 }
1428 if image.bit_depth == 0 || image.bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH {
1429 return Err("unsupported bit depth");
1430 }
1431 validate_component_sample_info(component_sample_info, image.components.len())?;
1432 if image
1433 .components
1434 .iter()
1435 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1436 {
1437 return Err("component sampling factors must be non-zero");
1438 }
1439 validate_precomputed_dwt_geometry(image)?;
1440
1441 let num_components =
1442 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1443 let num_levels = precomputed_level_count(&image.components)?;
1444 let mut precomputed_options = options.clone();
1445 precomputed_options.num_decomposition_levels = num_levels;
1446 precomputed_options.reversible = true;
1447 precomputed_options.use_ht_block_coding = use_ht_block_coding;
1448 precomputed_options.use_mct = use_mct;
1449 precomputed_options.validate_high_throughput_codestream = false;
1450 precomputed_options.component_sampling = Some(
1451 image
1452 .components
1453 .iter()
1454 .map(|component| (component.x_rsiz, component.y_rsiz))
1455 .collect(),
1456 );
1457
1458 let dummy_pixels =
1459 zero_pixel_buffer(image.width, image.height, num_components, image.bit_depth)?;
1460 let mut precomputed_accelerator = PrecomputedDwtAccelerator {
1461 outputs: image
1462 .components
1463 .iter()
1464 .map(|component| component.dwt.clone())
1465 .collect(),
1466 encode_accelerator: accelerator,
1467 };
1468
1469 encode_with_accelerator_and_component_sample_info(
1470 &dummy_pixels,
1471 image.width,
1472 image.height,
1473 num_components,
1474 image.bit_depth,
1475 image.signed,
1476 &precomputed_options,
1477 component_sample_info,
1478 &mut precomputed_accelerator,
1479 )
1480}
1481
1482pub fn encode_precomputed_htj2k_97(
1492 image: &PrecomputedHtj2k97Image,
1493 options: &EncodeOptions,
1494) -> Result<Vec<u8>, &'static str> {
1495 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1496 encode_precomputed_htj2k_97_with_accelerator(image, options, &mut accelerator)
1497}
1498
1499pub fn encode_precomputed_htj2k_97_with_accelerator(
1502 image: &PrecomputedHtj2k97Image,
1503 options: &EncodeOptions,
1504 accelerator: &mut impl J2kEncodeStageAccelerator,
1505) -> Result<Vec<u8>, &'static str> {
1506 if image.width == 0 || image.height == 0 {
1507 return Err("invalid dimensions");
1508 }
1509 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1510 {
1511 return Err("unsupported component count");
1512 }
1513 if image.bit_depth == 0 || image.bit_depth > 16 {
1514 return Err("unsupported bit depth");
1515 }
1516 validate_irreversible_quantization_profile(options)?;
1517 if image
1518 .components
1519 .iter()
1520 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1521 {
1522 return Err("component sampling factors must be non-zero");
1523 }
1524 validate_precomputed_dwt97_geometry(image)?;
1525
1526 let num_components =
1527 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1528 let num_levels = precomputed_97_level_count(&image.components)?;
1529 let mut precomputed_options = options.clone();
1530 precomputed_options.num_decomposition_levels = num_levels;
1531 precomputed_options.reversible = false;
1532 precomputed_options.use_ht_block_coding = true;
1533 precomputed_options.use_mct = false;
1534 precomputed_options.validate_high_throughput_codestream = false;
1535 precomputed_options.component_sampling = Some(
1536 image
1537 .components
1538 .iter()
1539 .map(|component| (component.x_rsiz, component.y_rsiz))
1540 .collect(),
1541 );
1542
1543 let dummy_pixels =
1544 zero_pixel_buffer(image.width, image.height, num_components, image.bit_depth)?;
1545 let mut precomputed_accelerator = PrecomputedDwt97Accelerator {
1546 outputs: image
1547 .components
1548 .iter()
1549 .map(|component| component.dwt.clone())
1550 .collect(),
1551 encode_accelerator: accelerator,
1552 };
1553
1554 encode_with_accelerator(
1555 &dummy_pixels,
1556 image.width,
1557 image.height,
1558 num_components,
1559 image.bit_depth,
1560 image.signed,
1561 &precomputed_options,
1562 &mut precomputed_accelerator,
1563 )
1564}
1565
1566pub fn encode_precomputed_htj2k_97_batch_with_accelerator(
1569 images: &[PrecomputedHtj2k97Image],
1570 options: &EncodeOptions,
1571 accelerator: &mut impl J2kEncodeStageAccelerator,
1572) -> Result<Vec<Vec<u8>>, &'static str> {
1573 if images.is_empty() {
1574 return Ok(Vec::new());
1575 }
1576 if options.num_layers != 1 {
1577 return Err("batch precomputed 9/7 encode currently supports one quality layer");
1578 }
1579
1580 let mut prepared_images = prepare_precomputed_htj2k97_images_for_batch(images, options)?;
1581 let mut all_packets = Vec::new();
1582 for prepared in &mut prepared_images {
1583 prepared.packet_count = prepared.prepared_packets.len();
1584 all_packets.append(&mut prepared.prepared_packets);
1585 }
1586
1587 let mut encoded_packets =
1588 encode_prepared_resolution_packets(all_packets, accelerator)?.into_iter();
1589 let mut codestreams = Vec::with_capacity(prepared_images.len());
1590 for prepared in prepared_images {
1591 let mut resolution_packets = Vec::with_capacity(prepared.packet_count);
1592 for _ in 0..prepared.packet_count {
1593 resolution_packets.push(
1594 encoded_packets
1595 .next()
1596 .ok_or("encoded packet count mismatch")?,
1597 );
1598 }
1599 let scalar_packet_descriptors = scalar_packet_descriptors(&prepared.packet_descriptors);
1600 let packetized_tile =
1601 packet_encode::form_tile_bitstream_with_descriptors_lengths_and_markers(
1602 &mut resolution_packets,
1603 &scalar_packet_descriptors,
1604 packet_encode::PacketMarkerOptions {
1605 write_sop: prepared.params.write_sop,
1606 write_eph: prepared.params.write_eph,
1607 separate_packet_headers: prepared.params.write_ppm || prepared.params.write_ppt,
1608 },
1609 )?;
1610 codestreams.push(write_single_tile_packetized_codestream(
1611 &prepared.params,
1612 &packetized_tile,
1613 &prepared.quant_params,
1614 options.tile_part_packet_limit,
1615 )?);
1616 }
1617 if encoded_packets.next().is_some() {
1618 return Err("encoded packet count mismatch");
1619 }
1620
1621 Ok(codestreams)
1622}
1623
1624#[cfg(feature = "parallel")]
1625fn prepare_precomputed_htj2k97_images_for_batch(
1626 images: &[PrecomputedHtj2k97Image],
1627 options: &EncodeOptions,
1628) -> Result<Vec<PreparedPrecomputedHtj2k97Image>, &'static str> {
1629 images
1630 .par_iter()
1631 .map(|image| prepare_precomputed_htj2k97_image_for_batch(image, options))
1632 .collect()
1633}
1634
1635#[cfg(not(feature = "parallel"))]
1636fn prepare_precomputed_htj2k97_images_for_batch(
1637 images: &[PrecomputedHtj2k97Image],
1638 options: &EncodeOptions,
1639) -> Result<Vec<PreparedPrecomputedHtj2k97Image>, &'static str> {
1640 images
1641 .iter()
1642 .map(|image| prepare_precomputed_htj2k97_image_for_batch(image, options))
1643 .collect()
1644}
1645
1646pub fn encode_prequantized_htj2k_97(
1649 image: &PrequantizedHtj2k97Image,
1650 options: &EncodeOptions,
1651) -> Result<Vec<u8>, &'static str> {
1652 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1653 encode_prequantized_htj2k_97_with_accelerator(image, options, &mut accelerator)
1654}
1655
1656pub fn encode_prequantized_htj2k_97_with_accelerator(
1659 image: &PrequantizedHtj2k97Image,
1660 options: &EncodeOptions,
1661 accelerator: &mut impl J2kEncodeStageAccelerator,
1662) -> Result<Vec<u8>, &'static str> {
1663 if image.width == 0 || image.height == 0 {
1664 return Err("invalid dimensions");
1665 }
1666 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1667 {
1668 return Err("unsupported component count");
1669 }
1670 if image.bit_depth == 0 || image.bit_depth > 16 {
1671 return Err("unsupported bit depth");
1672 }
1673 validate_irreversible_quantization_profile(options)?;
1674 if image
1675 .components
1676 .iter()
1677 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1678 {
1679 return Err("component sampling factors must be non-zero");
1680 }
1681
1682 let num_components =
1683 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1684 let num_levels = prequantized_97_level_count(&image.components)?;
1685 let guard_bits = options.guard_bits.max(2);
1686 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
1687 image.bit_depth,
1688 num_levels,
1689 false,
1690 guard_bits,
1691 options.irreversible_quantization_scale,
1692 options.irreversible_quantization_subband_scales,
1693 );
1694 validate_prequantized_htj2k97_image(image, guard_bits, &step_sizes)?;
1695
1696 let mut prequantized_options = options.clone();
1697 prequantized_options.num_decomposition_levels = num_levels;
1698 prequantized_options.reversible = false;
1699 prequantized_options.use_ht_block_coding = true;
1700 prequantized_options.use_mct = false;
1701 prequantized_options.validate_high_throughput_codestream = false;
1702 prequantized_options.component_sampling = Some(
1703 image
1704 .components
1705 .iter()
1706 .map(|component| (component.x_rsiz, component.y_rsiz))
1707 .collect(),
1708 );
1709
1710 let component_resolution_packets = image
1711 .components
1712 .iter()
1713 .enumerate()
1714 .map(|(component_idx, component)| {
1715 prepared_resolution_packets_from_prequantized_component(component_idx, component)
1716 })
1717 .collect::<Result<Vec<_>, &'static str>>()?;
1718 let prepared_resolution_packets =
1719 ordered_prepared_resolution_packets(component_resolution_packets, &prequantized_options)?;
1720 let packet_descriptors = packet_descriptors_for_order(
1721 &prepared_resolution_packets,
1722 1,
1723 prequantized_options.progression_order,
1724 )?;
1725 let mut resolution_packets =
1726 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
1727 let packetized_tile = packetize_resolution_packets_with_options(
1728 &mut resolution_packets,
1729 &packet_descriptors,
1730 1,
1731 num_components,
1732 prequantized_options.progression_order,
1733 packet_encode::PacketMarkerOptions {
1734 write_sop: prequantized_options.write_sop,
1735 write_eph: prequantized_options.write_eph,
1736 separate_packet_headers: prequantized_options.write_ppm
1737 || prequantized_options.write_ppt,
1738 },
1739 true,
1740 prequantized_options.write_plt
1741 || prequantized_options.write_plm
1742 || prequantized_options.write_ppm
1743 || prequantized_options.write_ppt
1744 || prequantized_options.write_sop
1745 || prequantized_options.write_eph
1746 || prequantized_options.tile_part_packet_limit.is_some(),
1747 accelerator,
1748 )?;
1749
1750 let quant_params: Vec<(u16, u16)> = step_sizes
1751 .iter()
1752 .map(|s| (s.exponent, s.mantissa))
1753 .collect();
1754 let params = EncodeParams {
1755 width: image.width,
1756 height: image.height,
1757 tile_width: image.width,
1758 tile_height: image.height,
1759 num_components,
1760 bit_depth: image.bit_depth,
1761 signed: image.signed,
1762 component_sample_info: Vec::new(),
1763 component_quantization_step_sizes: Vec::new(),
1764 num_decomposition_levels: num_levels,
1765 reversible: false,
1766 code_block_width_exp: prequantized_options.code_block_width_exp,
1767 code_block_height_exp: prequantized_options.code_block_height_exp,
1768 num_layers: 1,
1769 use_mct: false,
1770 guard_bits,
1771 block_coding_mode: BlockCodingMode::HighThroughput,
1772 progression_order: prequantized_options.progression_order,
1773 write_tlm: prequantized_options.write_tlm,
1774 write_plt: prequantized_options.write_plt,
1775 write_plm: prequantized_options.write_plm,
1776 write_ppm: prequantized_options.write_ppm,
1777 write_ppt: prequantized_options.write_ppt,
1778 write_sop: prequantized_options.write_sop,
1779 write_eph: prequantized_options.write_eph,
1780 terminate_coding_passes: false,
1781 component_sampling: prequantized_options
1782 .component_sampling
1783 .clone()
1784 .ok_or("component sampling missing")?,
1785 roi_component_shifts: vec![0; usize::from(num_components)],
1786 precinct_exponents: precinct_exponents_for_options(&prequantized_options, num_levels)?,
1787 };
1788
1789 write_single_tile_packetized_codestream(
1790 ¶ms,
1791 &packetized_tile,
1792 &quant_params,
1793 prequantized_options.tile_part_packet_limit,
1794 )
1795}
1796
1797pub fn encode_preencoded_htj2k_97(
1800 image: &PreencodedHtj2k97Image,
1801 options: &EncodeOptions,
1802) -> Result<Vec<u8>, &'static str> {
1803 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1804 encode_preencoded_htj2k_97_with_accelerator(image, options, &mut accelerator)
1805}
1806
1807pub fn encode_preencoded_htj2k_97_with_accelerator(
1810 image: &PreencodedHtj2k97Image,
1811 options: &EncodeOptions,
1812 accelerator: &mut impl J2kEncodeStageAccelerator,
1813) -> Result<Vec<u8>, &'static str> {
1814 if image.width == 0 || image.height == 0 {
1815 return Err("invalid dimensions");
1816 }
1817 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1818 {
1819 return Err("unsupported component count");
1820 }
1821 if image.bit_depth == 0 || image.bit_depth > 16 {
1822 return Err("unsupported bit depth");
1823 }
1824 validate_irreversible_quantization_profile(options)?;
1825 if image
1826 .components
1827 .iter()
1828 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1829 {
1830 return Err("component sampling factors must be non-zero");
1831 }
1832
1833 let num_components =
1834 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1835 let num_levels = preencoded_97_level_count(&image.components)?;
1836 let guard_bits = options.guard_bits.max(2);
1837 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
1838 image.bit_depth,
1839 num_levels,
1840 false,
1841 guard_bits,
1842 options.irreversible_quantization_scale,
1843 options.irreversible_quantization_subband_scales,
1844 );
1845 validate_preencoded_htj2k97_image(image, guard_bits, &step_sizes)?;
1846
1847 let mut preencoded_options = options.clone();
1848 preencoded_options.num_decomposition_levels = num_levels;
1849 preencoded_options.reversible = false;
1850 preencoded_options.use_ht_block_coding = true;
1851 preencoded_options.use_mct = false;
1852 preencoded_options.validate_high_throughput_codestream = false;
1853 preencoded_options.component_sampling = Some(
1854 image
1855 .components
1856 .iter()
1857 .map(|component| (component.x_rsiz, component.y_rsiz))
1858 .collect(),
1859 );
1860
1861 let component_resolution_packets = image
1862 .components
1863 .iter()
1864 .enumerate()
1865 .map(|(component_idx, component)| {
1866 prepared_resolution_packets_from_preencoded_component(component_idx, component)
1867 })
1868 .collect::<Result<Vec<_>, &'static str>>()?;
1869 let prepared_resolution_packets =
1870 ordered_prepared_resolution_packets(component_resolution_packets, &preencoded_options)?;
1871 let packet_descriptors = packet_descriptors_for_order(
1872 &prepared_resolution_packets,
1873 1,
1874 preencoded_options.progression_order,
1875 )?;
1876 let mut resolution_packets =
1877 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
1878 let packetized_tile = packetize_resolution_packets_with_options(
1879 &mut resolution_packets,
1880 &packet_descriptors,
1881 1,
1882 num_components,
1883 preencoded_options.progression_order,
1884 packet_encode::PacketMarkerOptions {
1885 write_sop: preencoded_options.write_sop,
1886 write_eph: preencoded_options.write_eph,
1887 separate_packet_headers: preencoded_options.write_ppm || preencoded_options.write_ppt,
1888 },
1889 true,
1890 preencoded_options.write_plt
1891 || preencoded_options.write_plm
1892 || preencoded_options.write_ppm
1893 || preencoded_options.write_ppt
1894 || preencoded_options.write_sop
1895 || preencoded_options.write_eph
1896 || preencoded_options.tile_part_packet_limit.is_some(),
1897 accelerator,
1898 )?;
1899
1900 let quant_params: Vec<(u16, u16)> = step_sizes
1901 .iter()
1902 .map(|s| (s.exponent, s.mantissa))
1903 .collect();
1904 let params = EncodeParams {
1905 width: image.width,
1906 height: image.height,
1907 tile_width: image.width,
1908 tile_height: image.height,
1909 num_components,
1910 bit_depth: image.bit_depth,
1911 signed: image.signed,
1912 component_sample_info: Vec::new(),
1913 component_quantization_step_sizes: Vec::new(),
1914 num_decomposition_levels: num_levels,
1915 reversible: false,
1916 code_block_width_exp: preencoded_options.code_block_width_exp,
1917 code_block_height_exp: preencoded_options.code_block_height_exp,
1918 num_layers: 1,
1919 use_mct: false,
1920 guard_bits,
1921 block_coding_mode: BlockCodingMode::HighThroughput,
1922 progression_order: preencoded_options.progression_order,
1923 write_tlm: preencoded_options.write_tlm,
1924 write_plt: preencoded_options.write_plt,
1925 write_plm: preencoded_options.write_plm,
1926 write_ppm: preencoded_options.write_ppm,
1927 write_ppt: preencoded_options.write_ppt,
1928 write_sop: preencoded_options.write_sop,
1929 write_eph: preencoded_options.write_eph,
1930 terminate_coding_passes: false,
1931 component_sampling: preencoded_options
1932 .component_sampling
1933 .clone()
1934 .ok_or("component sampling missing")?,
1935 roi_component_shifts: vec![0; usize::from(num_components)],
1936 precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
1937 };
1938
1939 write_single_tile_packetized_codestream(
1940 ¶ms,
1941 &packetized_tile,
1942 &quant_params,
1943 preencoded_options.tile_part_packet_limit,
1944 )
1945}
1946
1947pub fn encode_preencoded_htj2k_97_owned_with_accelerator(
1951 image: PreencodedHtj2k97Image,
1952 options: &EncodeOptions,
1953 accelerator: &mut impl J2kEncodeStageAccelerator,
1954) -> Result<Vec<u8>, &'static str> {
1955 if image.width == 0 || image.height == 0 {
1956 return Err("invalid dimensions");
1957 }
1958 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1959 {
1960 return Err("unsupported component count");
1961 }
1962 if image.bit_depth == 0 || image.bit_depth > 16 {
1963 return Err("unsupported bit depth");
1964 }
1965 validate_irreversible_quantization_profile(options)?;
1966 if image
1967 .components
1968 .iter()
1969 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1970 {
1971 return Err("component sampling factors must be non-zero");
1972 }
1973
1974 let width = image.width;
1975 let height = image.height;
1976 let bit_depth = image.bit_depth;
1977 let signed = image.signed;
1978 let num_components =
1979 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1980 let num_levels = preencoded_97_level_count(&image.components)?;
1981 let guard_bits = options.guard_bits.max(2);
1982 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
1983 bit_depth,
1984 num_levels,
1985 false,
1986 guard_bits,
1987 options.irreversible_quantization_scale,
1988 options.irreversible_quantization_subband_scales,
1989 );
1990 validate_preencoded_htj2k97_image(&image, guard_bits, &step_sizes)?;
1991
1992 let component_sampling = image
1993 .components
1994 .iter()
1995 .map(|component| (component.x_rsiz, component.y_rsiz))
1996 .collect::<Vec<_>>();
1997 let mut preencoded_options = options.clone();
1998 preencoded_options.num_decomposition_levels = num_levels;
1999 preencoded_options.reversible = false;
2000 preencoded_options.use_ht_block_coding = true;
2001 preencoded_options.use_mct = false;
2002 preencoded_options.validate_high_throughput_codestream = false;
2003 preencoded_options.component_sampling = Some(component_sampling);
2004
2005 let component_resolution_packets = image
2006 .components
2007 .into_iter()
2008 .enumerate()
2009 .map(|(component_idx, component)| {
2010 prepared_resolution_packets_from_preencoded_component_owned(component_idx, component)
2011 })
2012 .collect::<Result<Vec<_>, &'static str>>()?;
2013 let prepared_resolution_packets =
2014 ordered_prepared_resolution_packets(component_resolution_packets, &preencoded_options)?;
2015 let packet_descriptors = packet_descriptors_for_order(
2016 &prepared_resolution_packets,
2017 1,
2018 preencoded_options.progression_order,
2019 )?;
2020 let mut resolution_packets =
2021 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
2022 let packetized_tile = packetize_resolution_packets_with_options(
2023 &mut resolution_packets,
2024 &packet_descriptors,
2025 1,
2026 num_components,
2027 preencoded_options.progression_order,
2028 packet_encode::PacketMarkerOptions {
2029 write_sop: preencoded_options.write_sop,
2030 write_eph: preencoded_options.write_eph,
2031 separate_packet_headers: preencoded_options.write_ppm || preencoded_options.write_ppt,
2032 },
2033 true,
2034 preencoded_options.write_plt
2035 || preencoded_options.write_plm
2036 || preencoded_options.write_ppm
2037 || preencoded_options.write_ppt
2038 || preencoded_options.write_sop
2039 || preencoded_options.write_eph
2040 || preencoded_options.tile_part_packet_limit.is_some(),
2041 accelerator,
2042 )?;
2043
2044 let quant_params: Vec<(u16, u16)> = step_sizes
2045 .iter()
2046 .map(|s| (s.exponent, s.mantissa))
2047 .collect();
2048 let params = EncodeParams {
2049 width,
2050 height,
2051 tile_width: width,
2052 tile_height: height,
2053 num_components,
2054 bit_depth,
2055 signed,
2056 component_sample_info: Vec::new(),
2057 component_quantization_step_sizes: Vec::new(),
2058 num_decomposition_levels: num_levels,
2059 reversible: false,
2060 code_block_width_exp: preencoded_options.code_block_width_exp,
2061 code_block_height_exp: preencoded_options.code_block_height_exp,
2062 num_layers: 1,
2063 use_mct: false,
2064 guard_bits,
2065 block_coding_mode: BlockCodingMode::HighThroughput,
2066 progression_order: preencoded_options.progression_order,
2067 write_tlm: preencoded_options.write_tlm,
2068 write_plt: preencoded_options.write_plt,
2069 write_plm: preencoded_options.write_plm,
2070 write_ppm: preencoded_options.write_ppm,
2071 write_ppt: preencoded_options.write_ppt,
2072 write_sop: preencoded_options.write_sop,
2073 write_eph: preencoded_options.write_eph,
2074 terminate_coding_passes: false,
2075 component_sampling: preencoded_options
2076 .component_sampling
2077 .clone()
2078 .ok_or("component sampling missing")?,
2079 roi_component_shifts: vec![0; usize::from(num_components)],
2080 precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
2081 };
2082
2083 write_single_tile_packetized_codestream(
2084 ¶ms,
2085 &packetized_tile,
2086 &quant_params,
2087 preencoded_options.tile_part_packet_limit,
2088 )
2089}
2090
2091pub fn encode_preencoded_htj2k_97_compact_owned_with_accelerator(
2095 image: PreencodedHtj2k97CompactImage,
2096 options: &EncodeOptions,
2097 accelerator: &mut impl J2kEncodeStageAccelerator,
2098) -> Result<Vec<u8>, &'static str> {
2099 if image.width == 0 || image.height == 0 {
2100 return Err("invalid dimensions");
2101 }
2102 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
2103 {
2104 return Err("unsupported component count");
2105 }
2106 if image.bit_depth == 0 || image.bit_depth > 16 {
2107 return Err("unsupported bit depth");
2108 }
2109 if options.write_plt
2110 || options.write_plm
2111 || options.write_sop
2112 || options.write_eph
2113 || options.tile_part_packet_limit.is_some()
2114 {
2115 return Err(
2116 "compact preencoded HTJ2K encode does not support packet marker or tile-part options",
2117 );
2118 }
2119 validate_irreversible_quantization_profile(options)?;
2120 if image
2121 .components
2122 .iter()
2123 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
2124 {
2125 return Err("component sampling factors must be non-zero");
2126 }
2127
2128 let width = image.width;
2129 let height = image.height;
2130 let bit_depth = image.bit_depth;
2131 let signed = image.signed;
2132 let num_components =
2133 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
2134 let num_levels = preencoded_compact_97_level_count(&image.components)?;
2135 let guard_bits = options.guard_bits.max(2);
2136 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
2137 bit_depth,
2138 num_levels,
2139 false,
2140 guard_bits,
2141 options.irreversible_quantization_scale,
2142 options.irreversible_quantization_subband_scales,
2143 );
2144 validate_preencoded_compact_htj2k97_image(&image, guard_bits, &step_sizes)?;
2145
2146 let component_sampling = image
2147 .components
2148 .iter()
2149 .map(|component| (component.x_rsiz, component.y_rsiz))
2150 .collect::<Vec<_>>();
2151 let mut preencoded_options = options.clone();
2152 preencoded_options.num_decomposition_levels = num_levels;
2153 preencoded_options.reversible = false;
2154 preencoded_options.use_ht_block_coding = true;
2155 preencoded_options.use_mct = false;
2156 preencoded_options.validate_high_throughput_codestream = false;
2157 preencoded_options.component_sampling = Some(component_sampling);
2158
2159 let PreencodedHtj2k97CompactImage {
2160 payload,
2161 components,
2162 ..
2163 } = image;
2164 let component_resolution_packets = components
2165 .iter()
2166 .enumerate()
2167 .map(|(component_idx, component)| {
2168 prepared_resolution_packets_from_preencoded_compact_component(
2169 component_idx,
2170 component,
2171 &payload,
2172 )
2173 })
2174 .collect::<Result<Vec<_>, &'static str>>()?;
2175 let prepared_resolution_packets = ordered_prepared_compact_resolution_packets(
2176 component_resolution_packets,
2177 &preencoded_options,
2178 )?;
2179 let packet_descriptors = packet_descriptors_for_compact_order(
2180 &prepared_resolution_packets,
2181 1,
2182 preencoded_options.progression_order,
2183 )?;
2184 let packetization_resolutions =
2185 public_packetization_resolutions_from_compact(&prepared_resolution_packets);
2186 let packetization_job = J2kPacketizationEncodeJob {
2187 resolution_count: packetization_resolutions.len() as u32,
2188 num_layers: 1,
2189 num_components,
2190 code_block_count: count_compact_code_blocks(&prepared_resolution_packets)?,
2191 progression_order: public_packetization_progression_order(
2192 preencoded_options.progression_order,
2193 ),
2194 packet_descriptors: &packet_descriptors,
2195 resolutions: &packetization_resolutions,
2196 };
2197 let tile_data = accelerator
2198 .encode_packetization(packetization_job)?
2199 .map_or_else(
2200 || crate::encode_j2k_packetization_scalar(packetization_job),
2201 Ok,
2202 )?;
2203
2204 let quant_params: Vec<(u16, u16)> = step_sizes
2205 .iter()
2206 .map(|s| (s.exponent, s.mantissa))
2207 .collect();
2208 let params = EncodeParams {
2209 width,
2210 height,
2211 tile_width: width,
2212 tile_height: height,
2213 num_components,
2214 bit_depth,
2215 signed,
2216 component_sample_info: Vec::new(),
2217 component_quantization_step_sizes: Vec::new(),
2218 num_decomposition_levels: num_levels,
2219 reversible: false,
2220 code_block_width_exp: preencoded_options.code_block_width_exp,
2221 code_block_height_exp: preencoded_options.code_block_height_exp,
2222 num_layers: 1,
2223 use_mct: false,
2224 guard_bits,
2225 block_coding_mode: BlockCodingMode::HighThroughput,
2226 progression_order: preencoded_options.progression_order,
2227 write_tlm: preencoded_options.write_tlm,
2228 write_plt: preencoded_options.write_plt,
2229 write_plm: preencoded_options.write_plm,
2230 write_ppm: preencoded_options.write_ppm,
2231 write_ppt: preencoded_options.write_ppt,
2232 write_sop: preencoded_options.write_sop,
2233 write_eph: preencoded_options.write_eph,
2234 terminate_coding_passes: false,
2235 component_sampling: preencoded_options
2236 .component_sampling
2237 .clone()
2238 .ok_or("component sampling missing")?,
2239 roi_component_shifts: vec![0; usize::from(num_components)],
2240 precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
2241 };
2242
2243 Ok(codestream_write::write_codestream(
2244 ¶ms,
2245 &tile_data,
2246 &quant_params,
2247 ))
2248}
2249
2250fn validate_precomputed_dwt_geometry(image: &PrecomputedHtj2k53Image) -> Result<(), &'static str> {
2251 for component in &image.components {
2252 let component_width = image.width.div_ceil(u32::from(component.x_rsiz));
2253 let component_height = image.height.div_ceil(u32::from(component.y_rsiz));
2254 validate_precomputed_component_dwt_geometry(
2255 &component.dwt,
2256 component_width,
2257 component_height,
2258 )?;
2259 }
2260
2261 Ok(())
2262}
2263
2264fn validate_precomputed_dwt97_geometry(
2265 image: &PrecomputedHtj2k97Image,
2266) -> Result<(), &'static str> {
2267 for component in &image.components {
2268 let component_width = image.width.div_ceil(u32::from(component.x_rsiz));
2269 let component_height = image.height.div_ceil(u32::from(component.y_rsiz));
2270 validate_precomputed_component_dwt_geometry(
2271 &component.dwt,
2272 component_width,
2273 component_height,
2274 )?;
2275 }
2276
2277 Ok(())
2278}
2279
2280fn validate_precomputed_component_dwt_geometry(
2281 dwt: &impl PrecomputedDwtGeometryView,
2282 component_width: u32,
2283 component_height: u32,
2284) -> Result<(), &'static str> {
2285 if let Some(highest_level) = dwt.last_level_geometry() {
2286 if highest_level.width != component_width || highest_level.height != component_height {
2287 return Err("precomputed DWT component dimensions mismatch");
2288 }
2289 }
2290
2291 let mut expected_width = component_width;
2292 let mut expected_height = component_height;
2293 for level_index in (0..dwt.level_count()).rev() {
2294 let level = dwt.level_geometry(level_index);
2295 let low_width = expected_width.div_ceil(2);
2296 let low_height = expected_height.div_ceil(2);
2297 let high_width = expected_width / 2;
2298 let high_height = expected_height / 2;
2299
2300 if level.width != expected_width
2301 || level.height != expected_height
2302 || level.low_width != low_width
2303 || level.low_height != low_height
2304 || level.high_width != high_width
2305 || level.high_height != high_height
2306 {
2307 return Err("precomputed DWT recursive geometry mismatch");
2308 }
2309 validate_band_len(level.hl_len, high_width, low_height)?;
2310 validate_band_len(level.lh_len, low_width, high_height)?;
2311 validate_band_len(level.hh_len, high_width, high_height)?;
2312
2313 expected_width = low_width;
2314 expected_height = low_height;
2315 }
2316
2317 if dwt.ll_width() != expected_width || dwt.ll_height() != expected_height {
2318 return Err("precomputed DWT component dimensions mismatch");
2319 }
2320 validate_band_len(dwt.ll_len(), expected_width, expected_height)
2321}
2322
2323#[derive(Debug, Clone, Copy)]
2324struct PrecomputedDwtLevelGeometry {
2325 width: u32,
2326 height: u32,
2327 low_width: u32,
2328 low_height: u32,
2329 high_width: u32,
2330 high_height: u32,
2331 hl_len: usize,
2332 lh_len: usize,
2333 hh_len: usize,
2334}
2335
2336trait PrecomputedDwtGeometryView {
2337 fn ll_len(&self) -> usize;
2338 fn ll_width(&self) -> u32;
2339 fn ll_height(&self) -> u32;
2340 fn level_count(&self) -> usize;
2341 fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry;
2342
2343 fn last_level_geometry(&self) -> Option<PrecomputedDwtLevelGeometry> {
2344 self.level_count()
2345 .checked_sub(1)
2346 .map(|index| self.level_geometry(index))
2347 }
2348}
2349
2350impl PrecomputedDwtGeometryView for J2kForwardDwt53Output {
2351 fn ll_len(&self) -> usize {
2352 self.ll.len()
2353 }
2354
2355 fn ll_width(&self) -> u32 {
2356 self.ll_width
2357 }
2358
2359 fn ll_height(&self) -> u32 {
2360 self.ll_height
2361 }
2362
2363 fn level_count(&self) -> usize {
2364 self.levels.len()
2365 }
2366
2367 fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry {
2368 let level = &self.levels[index];
2369 PrecomputedDwtLevelGeometry {
2370 width: level.width,
2371 height: level.height,
2372 low_width: level.low_width,
2373 low_height: level.low_height,
2374 high_width: level.high_width,
2375 high_height: level.high_height,
2376 hl_len: level.hl.len(),
2377 lh_len: level.lh.len(),
2378 hh_len: level.hh.len(),
2379 }
2380 }
2381}
2382
2383impl PrecomputedDwtGeometryView for J2kForwardDwt97Output {
2384 fn ll_len(&self) -> usize {
2385 self.ll.len()
2386 }
2387
2388 fn ll_width(&self) -> u32 {
2389 self.ll_width
2390 }
2391
2392 fn ll_height(&self) -> u32 {
2393 self.ll_height
2394 }
2395
2396 fn level_count(&self) -> usize {
2397 self.levels.len()
2398 }
2399
2400 fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry {
2401 let level = &self.levels[index];
2402 PrecomputedDwtLevelGeometry {
2403 width: level.width,
2404 height: level.height,
2405 low_width: level.low_width,
2406 low_height: level.low_height,
2407 high_width: level.high_width,
2408 high_height: level.high_height,
2409 hl_len: level.hl.len(),
2410 lh_len: level.lh.len(),
2411 hh_len: level.hh.len(),
2412 }
2413 }
2414}
2415
2416fn precomputed_level_count(components: &[PrecomputedHtj2k53Component]) -> Result<u8, &'static str> {
2417 let first = components
2418 .first()
2419 .ok_or("unsupported component count")?
2420 .dwt
2421 .levels
2422 .len();
2423 if components
2424 .iter()
2425 .any(|component| component.dwt.levels.len() != first)
2426 {
2427 return Err("precomputed components must use the same decomposition level count");
2428 }
2429 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2430}
2431
2432fn precomputed_97_level_count(
2433 components: &[PrecomputedHtj2k97Component],
2434) -> Result<u8, &'static str> {
2435 let first = components
2436 .first()
2437 .ok_or("unsupported component count")?
2438 .dwt
2439 .levels
2440 .len();
2441 if components
2442 .iter()
2443 .any(|component| component.dwt.levels.len() != first)
2444 {
2445 return Err("precomputed components must use the same decomposition level count");
2446 }
2447 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2448}
2449
2450fn prequantized_97_level_count(
2451 components: &[PrequantizedHtj2k97Component],
2452) -> Result<u8, &'static str> {
2453 let first = components
2454 .first()
2455 .ok_or("unsupported component count")?
2456 .resolutions
2457 .len()
2458 .checked_sub(1)
2459 .ok_or("prequantized components must contain at least one decomposition level")?;
2460 if components
2461 .iter()
2462 .any(|component| component.resolutions.len() != first + 1)
2463 {
2464 return Err("prequantized components must use the same decomposition level count");
2465 }
2466 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2467}
2468
2469fn preencoded_97_level_count(
2470 components: &[PreencodedHtj2k97Component],
2471) -> Result<u8, &'static str> {
2472 let first = components
2473 .first()
2474 .ok_or("unsupported component count")?
2475 .resolutions
2476 .len()
2477 .checked_sub(1)
2478 .ok_or("preencoded components must contain at least one decomposition level")?;
2479 if components
2480 .iter()
2481 .any(|component| component.resolutions.len() != first + 1)
2482 {
2483 return Err("preencoded components must use the same decomposition level count");
2484 }
2485 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2486}
2487
2488fn preencoded_compact_97_level_count(
2489 components: &[PreencodedHtj2k97CompactComponent],
2490) -> Result<u8, &'static str> {
2491 let first = components
2492 .first()
2493 .ok_or("unsupported component count")?
2494 .resolutions
2495 .len()
2496 .checked_sub(1)
2497 .ok_or("preencoded components must contain at least one decomposition level")?;
2498 if components
2499 .iter()
2500 .any(|component| component.resolutions.len() != first + 1)
2501 {
2502 return Err("preencoded components must use the same decomposition level count");
2503 }
2504 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2505}
2506
2507fn validate_prequantized_htj2k97_image(
2508 image: &PrequantizedHtj2k97Image,
2509 guard_bits: u8,
2510 step_sizes: &[QuantStepSize],
2511) -> Result<(), &'static str> {
2512 for component in &image.components {
2513 if component.resolutions.is_empty() {
2514 return Err("prequantized components must contain at least one resolution");
2515 }
2516 validate_prequantized_resolution(
2517 &component.resolutions[0],
2518 &[J2kSubBandType::LowLow],
2519 guard_bits,
2520 &step_sizes[0..1],
2521 )?;
2522 for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
2523 let step_base = 1 + (level_index - 1) * 3;
2524 validate_prequantized_resolution(
2525 resolution,
2526 &[
2527 J2kSubBandType::HighLow,
2528 J2kSubBandType::LowHigh,
2529 J2kSubBandType::HighHigh,
2530 ],
2531 guard_bits,
2532 &step_sizes[step_base..step_base + 3],
2533 )?;
2534 }
2535 }
2536
2537 Ok(())
2538}
2539
2540fn validate_preencoded_htj2k97_image(
2541 image: &PreencodedHtj2k97Image,
2542 guard_bits: u8,
2543 step_sizes: &[QuantStepSize],
2544) -> Result<(), &'static str> {
2545 for component in &image.components {
2546 if component.resolutions.is_empty() {
2547 return Err("preencoded components must contain at least one resolution");
2548 }
2549 validate_preencoded_resolution(
2550 &component.resolutions[0],
2551 &[J2kSubBandType::LowLow],
2552 guard_bits,
2553 &step_sizes[0..1],
2554 )?;
2555 for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
2556 let step_base = 1 + (level_index - 1) * 3;
2557 validate_preencoded_resolution(
2558 resolution,
2559 &[
2560 J2kSubBandType::HighLow,
2561 J2kSubBandType::LowHigh,
2562 J2kSubBandType::HighHigh,
2563 ],
2564 guard_bits,
2565 &step_sizes[step_base..step_base + 3],
2566 )?;
2567 }
2568 }
2569
2570 Ok(())
2571}
2572
2573fn validate_preencoded_compact_htj2k97_image(
2574 image: &PreencodedHtj2k97CompactImage,
2575 guard_bits: u8,
2576 step_sizes: &[QuantStepSize],
2577) -> Result<(), &'static str> {
2578 for component in &image.components {
2579 if component.resolutions.is_empty() {
2580 return Err("preencoded components must contain at least one resolution");
2581 }
2582 validate_preencoded_compact_resolution(
2583 &component.resolutions[0],
2584 &[J2kSubBandType::LowLow],
2585 guard_bits,
2586 &step_sizes[0..1],
2587 image.payload.len(),
2588 )?;
2589 for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
2590 let step_base = 1 + (level_index - 1) * 3;
2591 validate_preencoded_compact_resolution(
2592 resolution,
2593 &[
2594 J2kSubBandType::HighLow,
2595 J2kSubBandType::LowHigh,
2596 J2kSubBandType::HighHigh,
2597 ],
2598 guard_bits,
2599 &step_sizes[step_base..step_base + 3],
2600 image.payload.len(),
2601 )?;
2602 }
2603 }
2604
2605 Ok(())
2606}
2607
2608fn validate_prequantized_resolution(
2609 resolution: &PrequantizedHtj2k97Resolution,
2610 expected_subbands: &[J2kSubBandType],
2611 guard_bits: u8,
2612 step_sizes: &[QuantStepSize],
2613) -> Result<(), &'static str> {
2614 if resolution.subbands.len() != expected_subbands.len() {
2615 return Err("prequantized resolution subband count mismatch");
2616 }
2617 for ((subband, expected_subband), step_size) in resolution
2618 .subbands
2619 .iter()
2620 .zip(expected_subbands)
2621 .zip(step_sizes)
2622 {
2623 if subband.sub_band_type != *expected_subband {
2624 return Err("prequantized resolution subband order mismatch");
2625 }
2626 let expected_blocks = subband
2627 .num_cbs_x
2628 .checked_mul(subband.num_cbs_y)
2629 .ok_or("prequantized code-block count overflow")?;
2630 if expected_blocks == 0 {
2631 if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
2632 return Err("empty prequantized subbands must not contain code-block data");
2633 }
2634 continue;
2635 }
2636 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
2637 let expected_total_bitplanes = guard_bits
2638 .saturating_add(step_size.exponent as u8)
2639 .saturating_sub(1);
2640 if subband.total_bitplanes != expected_total_bitplanes {
2641 return Err("prequantized subband bitplane count mismatch");
2642 }
2643 if usize::try_from(expected_blocks).map_err(|_| "prequantized code-block count overflow")?
2644 != subband.code_blocks.len()
2645 {
2646 return Err("prequantized code-block count mismatch");
2647 }
2648 for block in &subband.code_blocks {
2649 if block.width == 0 || block.height == 0 {
2650 return Err("prequantized code-block dimensions must be non-zero");
2651 }
2652 validate_band_len(block.coefficients.len(), block.width, block.height)?;
2653 }
2654 }
2655
2656 Ok(())
2657}
2658
2659fn validate_preencoded_resolution(
2660 resolution: &PreencodedHtj2k97Resolution,
2661 expected_subbands: &[J2kSubBandType],
2662 guard_bits: u8,
2663 step_sizes: &[QuantStepSize],
2664) -> Result<(), &'static str> {
2665 if resolution.subbands.len() != expected_subbands.len() {
2666 return Err("preencoded resolution subband count mismatch");
2667 }
2668 for ((subband, expected_subband), step_size) in resolution
2669 .subbands
2670 .iter()
2671 .zip(expected_subbands)
2672 .zip(step_sizes)
2673 {
2674 if subband.sub_band_type != *expected_subband {
2675 return Err("preencoded resolution subband order mismatch");
2676 }
2677 let expected_blocks = subband
2678 .num_cbs_x
2679 .checked_mul(subband.num_cbs_y)
2680 .ok_or("preencoded code-block count overflow")?;
2681 if expected_blocks == 0 {
2682 if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
2683 return Err("empty preencoded subbands must not contain code-block data");
2684 }
2685 continue;
2686 }
2687 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
2688 let expected_total_bitplanes = guard_bits
2689 .saturating_add(step_size.exponent as u8)
2690 .saturating_sub(1);
2691 if subband.total_bitplanes != expected_total_bitplanes {
2692 return Err("preencoded subband bitplane count mismatch");
2693 }
2694 if usize::try_from(expected_blocks).map_err(|_| "preencoded code-block count overflow")?
2695 != subband.code_blocks.len()
2696 {
2697 return Err("preencoded code-block count mismatch");
2698 }
2699 for block in &subband.code_blocks {
2700 if block.width == 0 || block.height == 0 {
2701 return Err("preencoded code-block dimensions must be non-zero");
2702 }
2703 validate_preencoded_code_block_payload(&block.encoded, subband.total_bitplanes)?;
2704 }
2705 }
2706
2707 Ok(())
2708}
2709
2710fn validate_preencoded_compact_resolution(
2711 resolution: &PreencodedHtj2k97CompactResolution,
2712 expected_subbands: &[J2kSubBandType],
2713 guard_bits: u8,
2714 step_sizes: &[QuantStepSize],
2715 payload_len: usize,
2716) -> Result<(), &'static str> {
2717 if resolution.subbands.len() != expected_subbands.len() {
2718 return Err("preencoded resolution subband count mismatch");
2719 }
2720 for ((subband, expected_subband), step_size) in resolution
2721 .subbands
2722 .iter()
2723 .zip(expected_subbands)
2724 .zip(step_sizes)
2725 {
2726 if subband.sub_band_type != *expected_subband {
2727 return Err("preencoded resolution subband order mismatch");
2728 }
2729 let expected_blocks = subband
2730 .num_cbs_x
2731 .checked_mul(subband.num_cbs_y)
2732 .ok_or("preencoded code-block count overflow")?;
2733 if expected_blocks == 0 {
2734 if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
2735 return Err("empty preencoded subbands must not contain code-block data");
2736 }
2737 continue;
2738 }
2739 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
2740 let expected_total_bitplanes = guard_bits
2741 .saturating_add(step_size.exponent as u8)
2742 .saturating_sub(1);
2743 if subband.total_bitplanes != expected_total_bitplanes {
2744 return Err("preencoded subband bitplane count mismatch");
2745 }
2746 if usize::try_from(expected_blocks).map_err(|_| "preencoded code-block count overflow")?
2747 != subband.code_blocks.len()
2748 {
2749 return Err("preencoded code-block count mismatch");
2750 }
2751 for block in &subband.code_blocks {
2752 if block.width == 0 || block.height == 0 {
2753 return Err("preencoded code-block dimensions must be non-zero");
2754 }
2755 validate_preencoded_compact_code_block_payload(
2756 block,
2757 payload_len,
2758 subband.total_bitplanes,
2759 )?;
2760 }
2761 }
2762
2763 Ok(())
2764}
2765
2766fn validate_preencoded_code_block_payload(
2767 block: &EncodedHtJ2kCodeBlock,
2768 total_bitplanes: u8,
2769) -> Result<(), &'static str> {
2770 let data_len = u32::try_from(block.data.len()).map_err(|_| "HTJ2K payload too large")?;
2771 if block.num_coding_passes == 0 {
2772 if data_len != 0 || block.cleanup_length != 0 || block.refinement_length != 0 {
2773 return Err("empty HTJ2K code-block payload metadata mismatch");
2774 }
2775 if block.num_zero_bitplanes != total_bitplanes {
2776 return Err("empty HTJ2K code-block zero-bitplane count mismatch");
2777 }
2778 return Ok(());
2779 }
2780 if block.num_coding_passes > 164 {
2781 return Err("HTJ2K code-block coding pass count out of range");
2782 }
2783 if block.num_zero_bitplanes >= total_bitplanes {
2784 return Err("HTJ2K code-block zero-bitplane count out of range");
2785 }
2786 let segment_len = block
2787 .cleanup_length
2788 .checked_add(block.refinement_length)
2789 .ok_or("HTJ2K payload segment length overflow")?;
2790 if segment_len != data_len {
2791 return Err("HTJ2K payload segment length mismatch");
2792 }
2793 Ok(())
2794}
2795
2796fn validate_preencoded_compact_code_block_payload(
2797 block: &PreencodedHtj2k97CompactCodeBlock,
2798 payload_len: usize,
2799 total_bitplanes: u8,
2800) -> Result<(), &'static str> {
2801 if block.payload_range.start > block.payload_range.end || block.payload_range.end > payload_len
2802 {
2803 return Err("HTJ2K payload range out of bounds");
2804 }
2805 let data_len = u32::try_from(block.payload_range.end - block.payload_range.start)
2806 .map_err(|_| "HTJ2K payload too large")?;
2807 if block.num_coding_passes == 0 {
2808 if data_len != 0 || block.cleanup_length != 0 || block.refinement_length != 0 {
2809 return Err("empty HTJ2K code-block payload metadata mismatch");
2810 }
2811 if block.num_zero_bitplanes != total_bitplanes {
2812 return Err("empty HTJ2K code-block zero-bitplane count mismatch");
2813 }
2814 return Ok(());
2815 }
2816 if block.num_coding_passes > 164 {
2817 return Err("HTJ2K code-block coding pass count out of range");
2818 }
2819 if block.num_zero_bitplanes >= total_bitplanes {
2820 return Err("HTJ2K code-block zero-bitplane count out of range");
2821 }
2822 let segment_len = block
2823 .cleanup_length
2824 .checked_add(block.refinement_length)
2825 .ok_or("HTJ2K payload segment length overflow")?;
2826 if segment_len != data_len {
2827 return Err("HTJ2K payload segment length mismatch");
2828 }
2829 Ok(())
2830}
2831
2832fn prepared_resolution_packets_from_prequantized_component(
2833 component_idx: usize,
2834 component: &PrequantizedHtj2k97Component,
2835) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
2836 let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2837 component
2838 .resolutions
2839 .iter()
2840 .enumerate()
2841 .map(|(resolution_idx, resolution)| {
2842 Ok(PreparedResolutionPacket {
2843 component: component_idx,
2844 resolution: u32::try_from(resolution_idx)
2845 .map_err(|_| "resolution index exceeds u32")?,
2846 precinct: 0,
2847 subbands: resolution
2848 .subbands
2849 .iter()
2850 .map(prepared_subband_from_prequantized)
2851 .collect::<Result<Vec<_>, &'static str>>()?,
2852 })
2853 })
2854 .collect()
2855}
2856
2857fn prepared_subband_from_prequantized(
2858 subband: &PrequantizedHtj2k97Subband,
2859) -> Result<PreparedEncodeSubband, &'static str> {
2860 Ok(PreparedEncodeSubband {
2861 code_blocks: subband
2862 .code_blocks
2863 .iter()
2864 .map(|block| PreparedEncodeCodeBlock {
2865 coefficients: block.coefficients.iter().copied().map(i64::from).collect(),
2866 width: block.width,
2867 height: block.height,
2868 })
2869 .collect(),
2870 preencoded_ht_code_blocks: None,
2871 num_cbs_x: subband.num_cbs_x,
2872 num_cbs_y: subband.num_cbs_y,
2873 code_block_width: subband
2874 .code_blocks
2875 .iter()
2876 .map(|block| block.width)
2877 .max()
2878 .unwrap_or(0),
2879 code_block_height: subband
2880 .code_blocks
2881 .iter()
2882 .map(|block| block.height)
2883 .max()
2884 .unwrap_or(0),
2885 width: precomputed_subband_width(
2886 subband.num_cbs_x,
2887 subband.code_blocks.iter().map(|block| block.width),
2888 ),
2889 height: precomputed_subband_height(
2890 subband.num_cbs_x,
2891 subband.num_cbs_y,
2892 subband.code_blocks.iter().map(|block| block.height),
2893 ),
2894 sub_band_type: internal_sub_band_type(subband.sub_band_type),
2895 total_bitplanes: subband.total_bitplanes,
2896 block_coding_mode: BlockCodingMode::HighThroughput,
2897 ht_target_coding_passes: 1,
2898 })
2899}
2900
2901fn precomputed_subband_width(width_in_blocks: u32, widths: impl Iterator<Item = u32>) -> u32 {
2902 if width_in_blocks == 0 {
2903 return 0;
2904 }
2905
2906 widths.take(width_in_blocks as usize).sum()
2907}
2908
2909fn precomputed_subband_height(
2910 width_in_blocks: u32,
2911 height_in_blocks: u32,
2912 heights: impl Iterator<Item = u32>,
2913) -> u32 {
2914 if width_in_blocks == 0 || height_in_blocks == 0 {
2915 return 0;
2916 }
2917
2918 heights
2919 .step_by(width_in_blocks as usize)
2920 .take(height_in_blocks as usize)
2921 .sum()
2922}
2923
2924fn prepared_resolution_packets_from_preencoded_component(
2925 component_idx: usize,
2926 component: &PreencodedHtj2k97Component,
2927) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
2928 let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2929 component
2930 .resolutions
2931 .iter()
2932 .enumerate()
2933 .map(|(resolution_idx, resolution)| {
2934 Ok(PreparedResolutionPacket {
2935 component: component_idx,
2936 resolution: u32::try_from(resolution_idx)
2937 .map_err(|_| "resolution index exceeds u32")?,
2938 precinct: 0,
2939 subbands: resolution
2940 .subbands
2941 .iter()
2942 .map(prepared_subband_from_preencoded)
2943 .collect::<Result<Vec<_>, &'static str>>()?,
2944 })
2945 })
2946 .collect()
2947}
2948
2949fn prepared_resolution_packets_from_preencoded_component_owned(
2950 component_idx: usize,
2951 component: PreencodedHtj2k97Component,
2952) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
2953 let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2954 component
2955 .resolutions
2956 .into_iter()
2957 .enumerate()
2958 .map(|(resolution_idx, resolution)| {
2959 Ok(PreparedResolutionPacket {
2960 component: component_idx,
2961 resolution: u32::try_from(resolution_idx)
2962 .map_err(|_| "resolution index exceeds u32")?,
2963 precinct: 0,
2964 subbands: resolution
2965 .subbands
2966 .into_iter()
2967 .map(prepared_subband_from_preencoded_owned)
2968 .collect::<Result<Vec<_>, &'static str>>()?,
2969 })
2970 })
2971 .collect()
2972}
2973
2974fn prepared_resolution_packets_from_preencoded_compact_component<'a>(
2975 component_idx: usize,
2976 component: &'a PreencodedHtj2k97CompactComponent,
2977 payload: &'a [u8],
2978) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
2979 let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2980 component
2981 .resolutions
2982 .iter()
2983 .enumerate()
2984 .map(|(resolution_idx, resolution)| {
2985 Ok(PreparedCompactResolutionPacket {
2986 component: component_idx,
2987 resolution: u32::try_from(resolution_idx)
2988 .map_err(|_| "resolution index exceeds u32")?,
2989 precinct: 0,
2990 subbands: resolution
2991 .subbands
2992 .iter()
2993 .map(|subband| prepared_subband_from_preencoded_compact(subband, payload))
2994 .collect::<Result<Vec<_>, &'static str>>()?,
2995 })
2996 })
2997 .collect()
2998}
2999
3000fn prepared_subband_from_preencoded(
3001 subband: &PreencodedHtj2k97Subband,
3002) -> Result<PreparedEncodeSubband, &'static str> {
3003 Ok(PreparedEncodeSubband {
3004 code_blocks: subband
3005 .code_blocks
3006 .iter()
3007 .map(|block| PreparedEncodeCodeBlock {
3008 coefficients: Vec::new(),
3009 width: block.width,
3010 height: block.height,
3011 })
3012 .collect(),
3013 preencoded_ht_code_blocks: Some(
3014 subband
3015 .code_blocks
3016 .iter()
3017 .map(|block| block.encoded.clone())
3018 .collect(),
3019 ),
3020 num_cbs_x: subband.num_cbs_x,
3021 num_cbs_y: subband.num_cbs_y,
3022 code_block_width: subband
3023 .code_blocks
3024 .iter()
3025 .map(|block| block.width)
3026 .max()
3027 .unwrap_or(0),
3028 code_block_height: subband
3029 .code_blocks
3030 .iter()
3031 .map(|block| block.height)
3032 .max()
3033 .unwrap_or(0),
3034 width: precomputed_subband_width(
3035 subband.num_cbs_x,
3036 subband.code_blocks.iter().map(|block| block.width),
3037 ),
3038 height: precomputed_subband_height(
3039 subband.num_cbs_x,
3040 subband.num_cbs_y,
3041 subband.code_blocks.iter().map(|block| block.height),
3042 ),
3043 sub_band_type: internal_sub_band_type(subband.sub_band_type),
3044 total_bitplanes: subband.total_bitplanes,
3045 block_coding_mode: BlockCodingMode::HighThroughput,
3046 ht_target_coding_passes: 1,
3047 })
3048}
3049
3050fn prepared_subband_from_preencoded_owned(
3051 subband: PreencodedHtj2k97Subband,
3052) -> Result<PreparedEncodeSubband, &'static str> {
3053 let code_block_width = subband
3054 .code_blocks
3055 .iter()
3056 .map(|block| block.width)
3057 .max()
3058 .unwrap_or(0);
3059 let code_block_height = subband
3060 .code_blocks
3061 .iter()
3062 .map(|block| block.height)
3063 .max()
3064 .unwrap_or(0);
3065 let width = precomputed_subband_width(
3066 subband.num_cbs_x,
3067 subband.code_blocks.iter().map(|block| block.width),
3068 );
3069 let height = precomputed_subband_height(
3070 subband.num_cbs_x,
3071 subband.num_cbs_y,
3072 subband.code_blocks.iter().map(|block| block.height),
3073 );
3074 let code_blocks = subband
3075 .code_blocks
3076 .into_iter()
3077 .map(|block| {
3078 let PreencodedHtj2k97CodeBlock {
3079 width,
3080 height,
3081 encoded,
3082 } = block;
3083 (
3084 PreparedEncodeCodeBlock {
3085 coefficients: Vec::new(),
3086 width,
3087 height,
3088 },
3089 encoded,
3090 )
3091 })
3092 .collect::<Vec<_>>();
3093 let (code_blocks, preencoded_ht_code_blocks): (Vec<_>, Vec<_>) =
3094 code_blocks.into_iter().unzip();
3095
3096 Ok(PreparedEncodeSubband {
3097 code_blocks,
3098 preencoded_ht_code_blocks: Some(preencoded_ht_code_blocks),
3099 num_cbs_x: subband.num_cbs_x,
3100 num_cbs_y: subband.num_cbs_y,
3101 code_block_width,
3102 code_block_height,
3103 width,
3104 height,
3105 sub_band_type: internal_sub_band_type(subband.sub_band_type),
3106 total_bitplanes: subband.total_bitplanes,
3107 block_coding_mode: BlockCodingMode::HighThroughput,
3108 ht_target_coding_passes: 1,
3109 })
3110}
3111
3112fn prepared_subband_from_preencoded_compact<'a>(
3113 subband: &'a PreencodedHtj2k97CompactSubband,
3114 payload: &'a [u8],
3115) -> Result<PreparedCompactSubband<'a>, &'static str> {
3116 let code_blocks = subband
3117 .code_blocks
3118 .iter()
3119 .map(|block| {
3120 Ok(PreparedCompactCodeBlock {
3121 data: compact_payload_slice(payload, &block.payload_range)?,
3122 cleanup_length: block.cleanup_length,
3123 refinement_length: block.refinement_length,
3124 num_coding_passes: block.num_coding_passes,
3125 num_zero_bitplanes: block.num_zero_bitplanes,
3126 })
3127 })
3128 .collect::<Result<Vec<_>, &'static str>>()?;
3129
3130 Ok(PreparedCompactSubband {
3131 code_blocks,
3132 num_cbs_x: subband.num_cbs_x,
3133 num_cbs_y: subband.num_cbs_y,
3134 })
3135}
3136
3137fn compact_payload_slice<'a>(
3138 payload: &'a [u8],
3139 range: &Range<usize>,
3140) -> Result<&'a [u8], &'static str> {
3141 if range.start > range.end || range.end > payload.len() {
3142 return Err("HTJ2K payload range out of bounds");
3143 }
3144 Ok(&payload[range.clone()])
3145}
3146
3147fn zero_pixel_buffer(
3148 width: u32,
3149 height: u32,
3150 num_components: u16,
3151 bit_depth: u8,
3152) -> Result<Vec<u8>, &'static str> {
3153 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
3154 let len = width as usize;
3155 let len = len
3156 .checked_mul(height as usize)
3157 .and_then(|value| value.checked_mul(usize::from(num_components)))
3158 .and_then(|value| value.checked_mul(bytes_per_sample))
3159 .ok_or("pixel buffer dimensions overflow")?;
3160 Ok(vec![0; len])
3161}
3162
3163struct PrecomputedDwtAccelerator<'a, A: J2kEncodeStageAccelerator> {
3164 outputs: Vec<J2kForwardDwt53Output>,
3165 encode_accelerator: &'a mut A,
3166}
3167
3168struct PrecomputedDwt97Accelerator<'a, A: J2kEncodeStageAccelerator> {
3169 outputs: Vec<J2kForwardDwt97Output>,
3170 encode_accelerator: &'a mut A,
3171}
3172
3173impl<A: J2kEncodeStageAccelerator> J2kEncodeStageAccelerator for PrecomputedDwtAccelerator<'_, A> {
3174 fn dispatch_report(&self) -> crate::J2kEncodeDispatchReport {
3175 self.encode_accelerator.dispatch_report()
3176 }
3177
3178 fn encode_forward_dwt53(
3179 &mut self,
3180 _job: J2kForwardDwt53Job<'_>,
3181 ) -> Result<Option<J2kForwardDwt53Output>, &'static str> {
3182 if self.outputs.is_empty() {
3183 return Err("precomputed DWT output exhausted");
3184 }
3185
3186 Ok(Some(self.outputs.remove(0)))
3187 }
3188
3189 fn encode_quantize_subband(
3190 &mut self,
3191 job: J2kQuantizeSubbandJob<'_>,
3192 ) -> Result<Option<Vec<i32>>, &'static str> {
3193 self.encode_accelerator.encode_quantize_subband(job)
3194 }
3195
3196 fn encode_tier1_code_block(
3197 &mut self,
3198 job: J2kTier1CodeBlockEncodeJob<'_>,
3199 ) -> Result<Option<EncodedJ2kCodeBlock>, &'static str> {
3200 self.encode_accelerator.encode_tier1_code_block(job)
3201 }
3202
3203 fn encode_tier1_code_blocks(
3204 &mut self,
3205 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
3206 ) -> Result<Option<Vec<EncodedJ2kCodeBlock>>, &'static str> {
3207 self.encode_accelerator.encode_tier1_code_blocks(jobs)
3208 }
3209
3210 fn encode_ht_code_block(
3211 &mut self,
3212 job: crate::J2kHtCodeBlockEncodeJob<'_>,
3213 ) -> Result<Option<EncodedHtJ2kCodeBlock>, &'static str> {
3214 self.encode_accelerator.encode_ht_code_block(job)
3215 }
3216
3217 fn encode_ht_code_blocks(
3218 &mut self,
3219 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
3220 ) -> Result<Option<Vec<EncodedHtJ2kCodeBlock>>, &'static str> {
3221 self.encode_accelerator.encode_ht_code_blocks(jobs)
3222 }
3223
3224 fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
3225 self.encode_accelerator
3226 .prefer_parallel_cpu_code_block_fallback()
3227 }
3228
3229 fn prefer_parallel_cpu_tile_encode(&self) -> bool {
3230 self.encode_accelerator.prefer_parallel_cpu_tile_encode()
3231 }
3232
3233 fn encode_packetization(
3234 &mut self,
3235 job: J2kPacketizationEncodeJob<'_>,
3236 ) -> Result<Option<Vec<u8>>, &'static str> {
3237 self.encode_accelerator.encode_packetization(job)
3238 }
3239}
3240
3241impl<A: J2kEncodeStageAccelerator> J2kEncodeStageAccelerator
3242 for PrecomputedDwt97Accelerator<'_, A>
3243{
3244 fn dispatch_report(&self) -> crate::J2kEncodeDispatchReport {
3245 self.encode_accelerator.dispatch_report()
3246 }
3247
3248 fn encode_forward_dwt97(
3249 &mut self,
3250 _job: J2kForwardDwt97Job<'_>,
3251 ) -> Result<Option<J2kForwardDwt97Output>, &'static str> {
3252 if self.outputs.is_empty() {
3253 return Err("precomputed DWT output exhausted");
3254 }
3255
3256 Ok(Some(self.outputs.remove(0)))
3257 }
3258
3259 fn encode_quantize_subband(
3260 &mut self,
3261 job: J2kQuantizeSubbandJob<'_>,
3262 ) -> Result<Option<Vec<i32>>, &'static str> {
3263 self.encode_accelerator.encode_quantize_subband(job)
3264 }
3265
3266 fn encode_tier1_code_block(
3267 &mut self,
3268 job: J2kTier1CodeBlockEncodeJob<'_>,
3269 ) -> Result<Option<EncodedJ2kCodeBlock>, &'static str> {
3270 self.encode_accelerator.encode_tier1_code_block(job)
3271 }
3272
3273 fn encode_tier1_code_blocks(
3274 &mut self,
3275 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
3276 ) -> Result<Option<Vec<EncodedJ2kCodeBlock>>, &'static str> {
3277 self.encode_accelerator.encode_tier1_code_blocks(jobs)
3278 }
3279
3280 fn encode_ht_code_block(
3281 &mut self,
3282 job: crate::J2kHtCodeBlockEncodeJob<'_>,
3283 ) -> Result<Option<EncodedHtJ2kCodeBlock>, &'static str> {
3284 self.encode_accelerator.encode_ht_code_block(job)
3285 }
3286
3287 fn encode_ht_code_blocks(
3288 &mut self,
3289 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
3290 ) -> Result<Option<Vec<EncodedHtJ2kCodeBlock>>, &'static str> {
3291 self.encode_accelerator.encode_ht_code_blocks(jobs)
3292 }
3293
3294 fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
3295 self.encode_accelerator
3296 .prefer_parallel_cpu_code_block_fallback()
3297 }
3298
3299 fn prefer_parallel_cpu_tile_encode(&self) -> bool {
3300 self.encode_accelerator.prefer_parallel_cpu_tile_encode()
3301 }
3302
3303 fn encode_packetization(
3304 &mut self,
3305 job: J2kPacketizationEncodeJob<'_>,
3306 ) -> Result<Option<Vec<u8>>, &'static str> {
3307 self.encode_accelerator.encode_packetization(job)
3308 }
3309}
3310
3311fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
3312 if options.use_ht_block_coding {
3313 BlockCodingMode::HighThroughput
3314 } else {
3315 BlockCodingMode::Classic
3316 }
3317}
3318
3319fn ht_target_coding_passes_for_options(options: &EncodeOptions) -> u8 {
3320 if options.use_ht_block_coding && options.num_layers > 1 {
3321 options.num_layers.min(3)
3322 } else {
3323 1
3324 }
3325}
3326
3327fn validate_irreversible_quantization_scale(scale: f32) -> Result<(), &'static str> {
3328 if scale.is_finite() && scale > 0.0 {
3329 Ok(())
3330 } else {
3331 Err("irreversible quantization scale must be finite and greater than zero")
3332 }
3333}
3334
3335fn validate_irreversible_quantization_profile(options: &EncodeOptions) -> Result<(), &'static str> {
3336 validate_irreversible_quantization_scale(options.irreversible_quantization_scale)?;
3337 if quantize::subband_scales_all_valid(options.irreversible_quantization_subband_scales) {
3338 Ok(())
3339 } else {
3340 Err("irreversible quantization subband scales must be finite and greater than zero")
3341 }
3342}
3343
3344fn validate_htj2k_codestream(
3345 codestream: &[u8],
3346 pixels: &[u8],
3347 width: u32,
3348 height: u32,
3349 num_components: u16,
3350 bit_depth: u8,
3351 signed: bool,
3352 reversible: bool,
3353) -> Result<(), &'static str> {
3354 let image = Image::new(codestream, &DecodeSettings::default())
3355 .map_err(|_| "generated HTJ2K codestream failed self-validation")?;
3356 let decoded = image
3357 .decode_native()
3358 .map_err(|_| "generated HTJ2K codestream failed self-validation")?;
3359
3360 if decoded.width != width
3361 || decoded.height != height
3362 || decoded.bit_depth != bit_depth
3363 || decoded.num_components != num_components
3364 {
3365 return Err("generated HTJ2K codestream failed self-validation");
3366 }
3367
3368 if reversible && !native_samples_equal(pixels, &decoded.data, bit_depth, signed) {
3369 return Err("generated HTJ2K codestream did not roundtrip");
3370 }
3371
3372 Ok(())
3373}
3374
3375fn native_samples_equal(expected: &[u8], actual: &[u8], bit_depth: u8, signed: bool) -> bool {
3376 if expected.len() != actual.len() {
3377 return false;
3378 }
3379
3380 let Ok(bytes_per_sample) = raw_pixel_bytes_per_sample(bit_depth) else {
3381 return false;
3382 };
3383 let sample_count = expected.len() / bytes_per_sample;
3384 (0..sample_count).all(|sample_index| {
3385 decode_native_sample(expected, sample_index, bit_depth, signed)
3386 == decode_native_sample(actual, sample_index, bit_depth, signed)
3387 })
3388}
3389
3390fn decode_native_sample(bytes: &[u8], sample_index: usize, bit_depth: u8, signed: bool) -> i64 {
3391 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth).unwrap_or(1);
3392 let byte_offset = sample_index * bytes_per_sample;
3393 let raw = read_le_sample_value(
3394 &bytes[byte_offset..byte_offset + bytes_per_sample],
3395 bit_depth,
3396 );
3397
3398 if signed {
3399 sign_extend_sample(raw, bit_depth)
3400 } else {
3401 raw as i64
3402 }
3403}
3404
3405fn raw_pixel_bytes_per_sample(bit_depth: u8) -> Result<usize, &'static str> {
3406 if bit_depth == 0 || bit_depth > MAX_PART1_SAMPLE_BIT_DEPTH {
3407 return Err("unsupported bit depth");
3408 }
3409 Ok(usize::from(bit_depth).div_ceil(8).max(1))
3410}
3411
3412fn read_le_sample_value(bytes: &[u8], bit_depth: u8) -> u64 {
3413 let mut raw = 0_u64;
3414 for (shift, byte) in bytes.iter().enumerate() {
3415 raw |= u64::from(*byte) << (shift * 8);
3416 }
3417 let mask = (1_u64 << bit_depth) - 1;
3418 raw & mask
3419}
3420
3421fn sign_extend_sample(raw: u64, bit_depth: u8) -> i64 {
3422 let shift = 64 - u32::from(bit_depth);
3423 ((raw << shift) as i64) >> shift
3424}
3425
3426fn encode_impl(
3427 pixels: &[u8],
3428 width: u32,
3429 height: u32,
3430 num_components: u16,
3431 bit_depth: u8,
3432 signed: bool,
3433 options: &EncodeOptions,
3434 block_coding_mode: BlockCodingMode,
3435 roi_regions: &[EncodeRoiRegion],
3436 component_sample_info: &[EncodeComponentSampleInfo],
3437 accelerator: &mut impl J2kEncodeStageAccelerator,
3438) -> Result<Vec<u8>, &'static str> {
3439 if width == 0 || height == 0 {
3440 return Err("invalid dimensions");
3441 }
3442 if num_components == 0 || num_components > MAX_J2K_SPEC_COMPONENTS {
3443 return Err("unsupported component count");
3444 }
3445 if bit_depth == 0 || bit_depth > MAX_PART1_SAMPLE_BIT_DEPTH {
3446 return Err("unsupported bit depth");
3447 }
3448 if options.num_layers == 0 || options.num_layers > 32 {
3449 return Err("unsupported quality layer count");
3450 }
3451 if options.write_ppm && options.write_ppt {
3452 return Err("PPM and PPT packet header markers are mutually exclusive");
3453 }
3454 if matches!(options.tile_part_packet_limit, Some(0)) {
3455 return Err("tile-part packet limit must be non-zero");
3456 }
3457 if !options.quality_layer_byte_targets.is_empty()
3458 && options.quality_layer_byte_targets.len() != usize::from(options.num_layers)
3459 {
3460 return Err("quality layer byte target count must match quality layer count");
3461 }
3462 if !options.reversible {
3463 validate_irreversible_quantization_profile(options)?;
3464 }
3465 validate_component_sample_info(component_sample_info, usize::from(num_components))?;
3466
3467 let num_pixels = (width as usize)
3468 .checked_mul(height as usize)
3469 .ok_or("image dimensions overflow")?;
3470 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
3471 let expected_len = num_pixels
3472 .checked_mul(num_components as usize)
3473 .and_then(|len| len.checked_mul(bytes_per_sample))
3474 .ok_or("image dimensions overflow")?;
3475 if pixels.len() < expected_len {
3476 return Err("pixel data too short");
3477 }
3478 let component_sampling = component_sampling_for_options(options, num_components)?;
3479 let high_bit_exact = bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH;
3480 if high_bit_exact && options.reversible {
3481 validate_reversible_i64_encode_options(
3482 options,
3483 block_coding_mode,
3484 component_sample_info,
3485 &component_sampling,
3486 )?;
3487 }
3488 if let Some((tile_width, tile_height)) = options.tile_size {
3489 if tile_width == 0 || tile_height == 0 {
3490 return Err("invalid tile dimensions");
3491 }
3492 if component_sampling
3493 .iter()
3494 .any(|sampling| *sampling != (1, 1))
3495 {
3496 return Err("multi-tile encode with component sampling is not implemented");
3497 }
3498 if tile_width < width || tile_height < height {
3499 return encode_multitile_impl(
3500 pixels,
3501 width,
3502 height,
3503 num_components,
3504 bit_depth,
3505 signed,
3506 options,
3507 block_coding_mode,
3508 roi_regions,
3509 component_sample_info,
3510 accelerator,
3511 tile_width,
3512 tile_height,
3513 );
3514 }
3515 }
3516
3517 let profile_enabled = profile::profile_stages_enabled();
3518 let total_start = profile::profile_now(profile_enabled);
3519
3520 let use_mct = options.use_mct && matches!(num_components, 3 | 4);
3521 let num_levels = options.num_decomposition_levels.min(
3522 max_decomposition_levels(width, height),
3524 );
3525 let requested_guard_bits = if options.reversible {
3526 if use_mct {
3527 options.guard_bits.max(2)
3528 } else {
3529 options.guard_bits
3530 }
3531 } else {
3532 options.guard_bits.max(2)
3533 };
3534 let guard_bits = if high_bit_exact && options.reversible {
3535 reversible_guard_bits_for_marker_limit(bit_depth, num_levels, requested_guard_bits)?
3536 } else {
3537 requested_guard_bits
3538 };
3539 let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
3540 let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
3541 bit_depth,
3542 num_levels,
3543 options.reversible,
3544 guard_bits,
3545 options.irreversible_quantization_scale,
3546 options.irreversible_quantization_subband_scales,
3547 );
3548 if options.reversible && reversible_guard_delta != 0 {
3549 adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
3550 }
3551 let quant_params: Vec<(u16, u16)> = step_sizes
3552 .iter()
3553 .map(|s| (s.exponent, s.mantissa))
3554 .collect();
3555 let mut component_step_sizes = component_step_sizes(
3556 component_sample_info,
3557 num_levels,
3558 options.reversible,
3559 guard_bits,
3560 options.irreversible_quantization_scale,
3561 options.irreversible_quantization_subband_scales,
3562 );
3563 if options.reversible && reversible_guard_delta != 0 {
3564 adjust_component_step_sizes_for_guard_delta(
3565 &mut component_step_sizes,
3566 reversible_guard_delta,
3567 )?;
3568 }
3569 let component_quantization_step_sizes = component_step_sizes
3570 .iter()
3571 .map(|steps| {
3572 steps
3573 .iter()
3574 .map(|step| (step.exponent, step.mantissa))
3575 .collect::<Vec<_>>()
3576 })
3577 .collect::<Vec<_>>();
3578 let cb_width = 1u32 << (options.code_block_width_exp + 2);
3579 let cb_height = 1u32 << (options.code_block_height_exp + 2);
3580 let ht_target_coding_passes = ht_target_coding_passes_for_options(options);
3581 let precinct_exponents = precinct_exponents_for_options(options, num_levels)?;
3582 let max_base_bitplanes =
3583 max_total_bitplanes_for_components(&step_sizes, &component_step_sizes, guard_bits)?;
3584 let roi_plans = roi_encode_plans_for_options(
3585 options,
3586 roi_regions,
3587 num_components,
3588 width,
3589 height,
3590 &component_sampling,
3591 max_base_bitplanes,
3592 block_coding_mode,
3593 )?;
3594 let roi_component_shifts: Vec<u8> = roi_plans.iter().map(|plan| plan.shift).collect();
3595 let params = EncodeParams {
3596 width,
3597 height,
3598 tile_width: options
3599 .tile_size
3600 .map_or(width, |(tile_width, _)| tile_width),
3601 tile_height: options
3602 .tile_size
3603 .map_or(height, |(_, tile_height)| tile_height),
3604 num_components,
3605 bit_depth,
3606 signed,
3607 component_sample_info: component_sample_info.to_vec(),
3608 component_quantization_step_sizes,
3609 num_decomposition_levels: num_levels,
3610 reversible: options.reversible,
3611 code_block_width_exp: options.code_block_width_exp,
3612 code_block_height_exp: options.code_block_height_exp,
3613 num_layers: options.num_layers,
3614 use_mct,
3615 guard_bits,
3616 block_coding_mode,
3617 progression_order: options.progression_order,
3618 write_tlm: options.write_tlm,
3619 write_plt: options.write_plt,
3620 write_plm: options.write_plm,
3621 write_ppm: options.write_ppm,
3622 write_ppt: options.write_ppt,
3623 write_sop: options.write_sop,
3624 write_eph: options.write_eph,
3625 terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
3626 && options.num_layers > 1,
3627 component_sampling,
3628 roi_component_shifts: roi_component_shifts.clone(),
3629 precinct_exponents,
3630 };
3631
3632 if high_bit_exact && options.reversible {
3633 return encode_reversible_i64_single_tile_codestream(
3634 pixels,
3635 width,
3636 height,
3637 num_pixels,
3638 num_components,
3639 bit_depth,
3640 signed,
3641 options,
3642 ¶ms,
3643 &quant_params,
3644 &step_sizes,
3645 &roi_plans,
3646 use_mct,
3647 guard_bits,
3648 num_levels,
3649 cb_width,
3650 cb_height,
3651 ht_target_coding_passes,
3652 accelerator,
3653 );
3654 }
3655
3656 let stage_start = profile::profile_now(profile_enabled);
3657 if block_coding_mode == BlockCodingMode::HighThroughput
3658 && component_sample_info.is_empty()
3659 && roi_component_shifts.iter().all(|shift| *shift == 0)
3660 && roi_regions.is_empty()
3661 && !(params.write_plt
3662 || params.write_plm
3663 || params.write_sop
3664 || params.write_eph
3665 || options.tile_part_packet_limit.is_some())
3666 {
3667 if let Some(tile_data) = accelerator.encode_htj2k_tile(J2kHtj2kTileEncodeJob {
3668 pixels,
3669 width,
3670 height,
3671 num_components,
3672 bit_depth,
3673 signed,
3674 num_decomposition_levels: num_levels,
3675 reversible: options.reversible,
3676 use_mct,
3677 guard_bits,
3678 code_block_width: cb_width,
3679 code_block_height: cb_height,
3680 progression_order: public_packetization_progression_order(options.progression_order),
3681 component_sampling: ¶ms.component_sampling,
3682 quantization_steps: &quant_params,
3683 })? {
3684 let tile_body_us = profile::elapsed_us(stage_start);
3685 let stage_start = profile::profile_now(profile_enabled);
3686 let codestream = codestream_write::write_codestream(¶ms, &tile_data, &quant_params);
3687 let codestream_us = profile::elapsed_us(stage_start);
3688 if profile_enabled {
3689 profile::emit_profile_row(
3690 "encode",
3691 "accelerated",
3692 &[
3693 ("tile_body_us", tile_body_us),
3694 ("codestream_us", codestream_us),
3695 ("total_us", profile::elapsed_us(total_start)),
3696 ],
3697 );
3698 }
3699 return Ok(codestream);
3700 }
3701 }
3702
3703 let stage_start = profile::profile_now(profile_enabled);
3705 let mut components = match accelerator.encode_deinterleave(J2kDeinterleaveToF32Job {
3706 pixels,
3707 num_pixels,
3708 num_components,
3709 bit_depth,
3710 signed,
3711 })? {
3712 Some(components) => {
3713 validate_deinterleaved_components(components, num_components, num_pixels)?
3714 }
3715 None => deinterleave_to_f32(pixels, num_pixels, num_components, bit_depth, signed),
3716 };
3717 let deinterleave_us = profile::elapsed_us(stage_start);
3718
3719 let stage_start = profile::profile_now(profile_enabled);
3721 if use_mct {
3722 if options.reversible {
3723 if !try_encode_forward_rct(&mut components, accelerator)? {
3724 forward_mct::forward_rct(&mut components);
3725 }
3726 } else if !try_encode_forward_ict(&mut components, accelerator)? {
3727 forward_mct::forward_ict(&mut components);
3728 }
3729 }
3730 let mct_us = profile::elapsed_us(stage_start);
3731
3732 let stage_start = profile::profile_now(profile_enabled);
3734 let decompositions: Vec<DwtDecomposition> = components
3735 .iter()
3736 .map(|comp| {
3737 encode_forward_dwt(
3738 comp,
3739 width,
3740 height,
3741 num_levels,
3742 options.reversible,
3743 accelerator,
3744 )
3745 })
3746 .collect::<Result<Vec<_>, &'static str>>()?;
3747 validate_component_sampling_dwt_geometry(
3748 &decompositions,
3749 width,
3750 height,
3751 ¶ms.component_sampling,
3752 )?;
3753 let dwt_us = profile::elapsed_us(stage_start);
3754
3755 let mut component_resolution_packets: Vec<Vec<PreparedResolutionPacket>> =
3757 Vec::with_capacity(num_components as usize);
3758
3759 let stage_start = profile::profile_now(profile_enabled);
3760 for (component_idx, decomp) in decompositions
3761 .iter()
3762 .take(num_components as usize)
3763 .enumerate()
3764 {
3765 let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
3766 let component_bit_depth = component_sample_info
3767 .get(component_idx)
3768 .map_or(bit_depth, |info| info.bit_depth);
3769 let component_steps = component_step_sizes
3770 .get(component_idx)
3771 .map_or(step_sizes.as_slice(), Vec::as_slice);
3772 let roi_shift = roi_component_shifts
3773 .get(component_idx)
3774 .copied()
3775 .unwrap_or(0);
3776 let roi_plan = roi_plans
3777 .get(component_idx)
3778 .ok_or("ROI plan count does not match component count")?;
3779 let mut packets = Vec::with_capacity(num_levels as usize + 1);
3780
3781 let ll_roi_scale = roi_subband_scale(num_levels, None)?;
3783 let ll_subband = prepare_subband(
3784 &decomp.ll,
3785 decomp.ll_width,
3786 decomp.ll_height,
3787 &component_steps[0],
3788 component_bit_depth,
3789 guard_bits,
3790 options.reversible,
3791 block_coding_mode,
3792 cb_width,
3793 cb_height,
3794 SubBandType::LowLow,
3795 roi_shift,
3796 &roi_plan.regions,
3797 ll_roi_scale,
3798 ht_target_coding_passes,
3799 accelerator,
3800 )?;
3801 packets.push(PreparedResolutionPacket {
3802 component,
3803 resolution: 0,
3804 precinct: 0,
3805 subbands: vec![ll_subband],
3806 });
3807
3808 for (level_idx, level) in decomp.levels.iter().enumerate() {
3810 let step_base = 1 + level_idx * 3;
3811 let level_roi_scale = roi_subband_scale(num_levels, Some(level_idx))?;
3812
3813 let hl_subband = prepare_subband(
3815 &level.hl,
3816 level.high_width,
3817 level.low_height,
3818 &component_steps[step_base],
3819 component_bit_depth,
3820 guard_bits,
3821 options.reversible,
3822 block_coding_mode,
3823 cb_width,
3824 cb_height,
3825 SubBandType::HighLow,
3826 roi_shift,
3827 &roi_plan.regions,
3828 level_roi_scale,
3829 ht_target_coding_passes,
3830 accelerator,
3831 )?;
3832
3833 let lh_subband = prepare_subband(
3835 &level.lh,
3836 level.low_width,
3837 level.high_height,
3838 &component_steps[step_base + 1],
3839 component_bit_depth,
3840 guard_bits,
3841 options.reversible,
3842 block_coding_mode,
3843 cb_width,
3844 cb_height,
3845 SubBandType::LowHigh,
3846 roi_shift,
3847 &roi_plan.regions,
3848 level_roi_scale,
3849 ht_target_coding_passes,
3850 accelerator,
3851 )?;
3852
3853 let hh_subband = prepare_subband(
3855 &level.hh,
3856 level.high_width,
3857 level.high_height,
3858 &component_steps[step_base + 2],
3859 component_bit_depth,
3860 guard_bits,
3861 options.reversible,
3862 block_coding_mode,
3863 cb_width,
3864 cb_height,
3865 SubBandType::HighHigh,
3866 roi_shift,
3867 &roi_plan.regions,
3868 level_roi_scale,
3869 ht_target_coding_passes,
3870 accelerator,
3871 )?;
3872
3873 packets.push(PreparedResolutionPacket {
3874 component,
3875 resolution: u32::try_from(level_idx + 1)
3876 .map_err(|_| "resolution index exceeds u32")?,
3877 precinct: 0,
3878 subbands: vec![hl_subband, lh_subband, hh_subband],
3879 });
3880 }
3881
3882 component_resolution_packets.push(packets);
3883 }
3884 let subband_prepare_us = profile::elapsed_us(stage_start);
3885
3886 let component_resolution_packets = split_component_resolution_packets_by_precinct(
3887 component_resolution_packets,
3888 width,
3889 height,
3890 num_levels,
3891 ¶ms.precinct_exponents,
3892 )?;
3893 let prepared_resolution_packets =
3894 ordered_prepared_resolution_packets(component_resolution_packets, options)?;
3895 let stage_start = profile::profile_now(profile_enabled);
3896 let (resolution_packets, packet_descriptors, allow_packetization_accelerator) =
3897 if options.num_layers > 1 {
3898 let (resolution_packets, packet_descriptors) =
3899 encode_prepared_resolution_packets_layered(
3900 prepared_resolution_packets,
3901 options.num_layers,
3902 options.progression_order,
3903 &options.quality_layer_byte_targets,
3904 accelerator,
3905 )?;
3906 (resolution_packets, packet_descriptors, false)
3907 } else {
3908 let packet_descriptors = packet_descriptors_for_order(
3909 &prepared_resolution_packets,
3910 1,
3911 options.progression_order,
3912 )?;
3913 let resolution_packets =
3914 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
3915 (resolution_packets, packet_descriptors, true)
3916 };
3917 let block_encode_us = profile::elapsed_us(stage_start);
3918
3919 let stage_start = profile::profile_now(profile_enabled);
3921 let mut resolution_packets = resolution_packets;
3922 let packetized_tile = packetize_resolution_packets_with_options(
3923 &mut resolution_packets,
3924 &packet_descriptors,
3925 options.num_layers,
3926 num_components,
3927 options.progression_order,
3928 packet_encode::PacketMarkerOptions {
3929 write_sop: params.write_sop,
3930 write_eph: params.write_eph,
3931 separate_packet_headers: params.write_ppm || params.write_ppt,
3932 },
3933 allow_packetization_accelerator,
3934 packetization_requires_scalar(¶ms, options.tile_part_packet_limit),
3935 accelerator,
3936 )?;
3937 let packetize_us = profile::elapsed_us(stage_start);
3938
3939 let stage_start = profile::profile_now(profile_enabled);
3941 let codestream = write_single_tile_packetized_codestream(
3942 ¶ms,
3943 &packetized_tile,
3944 &quant_params,
3945 options.tile_part_packet_limit,
3946 )?;
3947 let codestream_us = profile::elapsed_us(stage_start);
3948
3949 if profile_enabled {
3950 profile::emit_profile_row(
3951 "encode",
3952 "cpu",
3953 &[
3954 ("deinterleave_us", deinterleave_us),
3955 ("mct_us", mct_us),
3956 ("dwt_us", dwt_us),
3957 ("subband_prepare_us", subband_prepare_us),
3958 ("block_encode_us", block_encode_us),
3959 ("packetize_us", packetize_us),
3960 ("codestream_us", codestream_us),
3961 ("total_us", profile::elapsed_us(total_start)),
3962 ],
3963 );
3964 }
3965
3966 Ok(codestream)
3967}
3968
3969fn validate_reversible_i64_encode_options(
3970 options: &EncodeOptions,
3971 block_coding_mode: BlockCodingMode,
3972 component_sample_info: &[EncodeComponentSampleInfo],
3973 component_sampling: &[(u8, u8)],
3974) -> Result<(), &'static str> {
3975 if !options.reversible {
3976 return Err("25-38 bit encode currently requires reversible 5/3 coding");
3977 }
3978 if !matches!(
3979 block_coding_mode,
3980 BlockCodingMode::Classic | BlockCodingMode::HighThroughput
3981 ) {
3982 return Err("25-38 bit encode requires classic J2K or HTJ2K block coding");
3983 }
3984 if !component_sample_info.is_empty() {
3985 return Err("25-38 bit encode currently requires uniform raw-pixel component metadata");
3986 }
3987 if component_sampling
3988 .iter()
3989 .any(|sampling| *sampling != (1, 1))
3990 {
3991 return Err("25-38 bit encode currently requires full-resolution components");
3992 }
3993 Ok(())
3994}
3995
3996#[allow(clippy::too_many_arguments)]
3997fn encode_reversible_i64_single_tile_codestream(
3998 pixels: &[u8],
3999 width: u32,
4000 height: u32,
4001 num_pixels: usize,
4002 num_components: u16,
4003 bit_depth: u8,
4004 signed: bool,
4005 options: &EncodeOptions,
4006 params: &EncodeParams,
4007 quant_params: &[(u16, u16)],
4008 step_sizes: &[QuantStepSize],
4009 roi_plans: &[ComponentRoiEncodePlan],
4010 use_mct: bool,
4011 guard_bits: u8,
4012 num_levels: u8,
4013 cb_width: u32,
4014 cb_height: u32,
4015 ht_target_coding_passes: u8,
4016 accelerator: &mut impl J2kEncodeStageAccelerator,
4017) -> Result<Vec<u8>, &'static str> {
4018 let max_reversible_gain = if num_levels == 0 { 0 } else { 2 };
4019 if u16::from(bit_depth) + max_reversible_gain > MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES {
4020 return Err("25-38 bit reversible encode exceeds the current no-quantization guard/exponent signaling limit");
4021 }
4022
4023 let mut components = deinterleave_to_i64(pixels, num_pixels, num_components, bit_depth, signed);
4024 if use_mct {
4025 forward_rct_i64(&mut components);
4026 }
4027
4028 let decompositions = components
4029 .iter()
4030 .map(|component| fdwt::forward_dwt_i64(component, width, height, num_levels))
4031 .collect::<Vec<_>>();
4032
4033 let mut component_resolution_packets = Vec::with_capacity(num_components as usize);
4034 for (component_idx, decomp) in decompositions
4035 .iter()
4036 .take(num_components as usize)
4037 .enumerate()
4038 {
4039 let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
4040 let roi_shift = params
4041 .roi_component_shifts
4042 .get(component_idx)
4043 .copied()
4044 .unwrap_or(0);
4045 let roi_plan = roi_plans
4046 .get(component_idx)
4047 .ok_or("ROI plan count does not match component count")?;
4048 let mut packets = Vec::with_capacity(num_levels as usize + 1);
4049
4050 let ll_roi_scale = roi_subband_scale(num_levels, None)?;
4051 let ll_subband = prepare_subband_i64(
4052 &decomp.ll,
4053 decomp.ll_width,
4054 decomp.ll_height,
4055 step_sizes
4056 .first()
4057 .ok_or("reversible quantization step missing")?,
4058 guard_bits,
4059 cb_width,
4060 cb_height,
4061 SubBandType::LowLow,
4062 roi_shift,
4063 &roi_plan.regions,
4064 ll_roi_scale,
4065 params.block_coding_mode,
4066 ht_target_coding_passes,
4067 )?;
4068 packets.push(PreparedResolutionPacket {
4069 component,
4070 resolution: 0,
4071 precinct: 0,
4072 subbands: vec![ll_subband],
4073 });
4074
4075 for (level_idx, level) in decomp.levels.iter().enumerate() {
4076 let step_base = 1 + level_idx * 3;
4077 let level_roi_scale = roi_subband_scale(num_levels, Some(level_idx))?;
4078 let hl_subband = prepare_subband_i64(
4079 &level.hl,
4080 level.high_width,
4081 level.low_height,
4082 step_sizes
4083 .get(step_base)
4084 .ok_or("reversible quantization step missing")?,
4085 guard_bits,
4086 cb_width,
4087 cb_height,
4088 SubBandType::HighLow,
4089 roi_shift,
4090 &roi_plan.regions,
4091 level_roi_scale,
4092 params.block_coding_mode,
4093 ht_target_coding_passes,
4094 )?;
4095 let lh_subband = prepare_subband_i64(
4096 &level.lh,
4097 level.low_width,
4098 level.high_height,
4099 step_sizes
4100 .get(step_base + 1)
4101 .ok_or("reversible quantization step missing")?,
4102 guard_bits,
4103 cb_width,
4104 cb_height,
4105 SubBandType::LowHigh,
4106 roi_shift,
4107 &roi_plan.regions,
4108 level_roi_scale,
4109 params.block_coding_mode,
4110 ht_target_coding_passes,
4111 )?;
4112 let hh_subband = prepare_subband_i64(
4113 &level.hh,
4114 level.high_width,
4115 level.high_height,
4116 step_sizes
4117 .get(step_base + 2)
4118 .ok_or("reversible quantization step missing")?,
4119 guard_bits,
4120 cb_width,
4121 cb_height,
4122 SubBandType::HighHigh,
4123 roi_shift,
4124 &roi_plan.regions,
4125 level_roi_scale,
4126 params.block_coding_mode,
4127 ht_target_coding_passes,
4128 )?;
4129
4130 packets.push(PreparedResolutionPacket {
4131 component,
4132 resolution: u32::try_from(level_idx + 1)
4133 .map_err(|_| "resolution index exceeds u32")?,
4134 precinct: 0,
4135 subbands: vec![hl_subband, lh_subband, hh_subband],
4136 });
4137 }
4138 component_resolution_packets.push(packets);
4139 }
4140
4141 encode_i64_component_resolution_packets(
4142 component_resolution_packets,
4143 width,
4144 height,
4145 num_components,
4146 num_levels,
4147 params,
4148 quant_params,
4149 options,
4150 accelerator,
4151 )
4152}
4153
4154#[allow(clippy::too_many_arguments)]
4155fn encode_i64_component_resolution_packets(
4156 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
4157 width: u32,
4158 height: u32,
4159 num_components: u16,
4160 num_levels: u8,
4161 params: &EncodeParams,
4162 quant_params: &[(u16, u16)],
4163 options: &EncodeOptions,
4164 accelerator: &mut impl J2kEncodeStageAccelerator,
4165) -> Result<Vec<u8>, &'static str> {
4166 let packetized_tile = packetize_i64_component_resolution_packets(
4167 component_resolution_packets,
4168 width,
4169 height,
4170 num_components,
4171 num_levels,
4172 params,
4173 options,
4174 accelerator,
4175 )?;
4176
4177 write_single_tile_packetized_codestream(
4178 params,
4179 &packetized_tile,
4180 quant_params,
4181 options.tile_part_packet_limit,
4182 )
4183}
4184
4185#[allow(clippy::too_many_arguments)]
4186fn packetize_i64_component_resolution_packets(
4187 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
4188 width: u32,
4189 height: u32,
4190 num_components: u16,
4191 num_levels: u8,
4192 params: &EncodeParams,
4193 options: &EncodeOptions,
4194 accelerator: &mut impl J2kEncodeStageAccelerator,
4195) -> Result<packet_encode::PacketizedTileData, &'static str> {
4196 let component_resolution_packets = split_component_resolution_packets_by_precinct(
4197 component_resolution_packets,
4198 width,
4199 height,
4200 num_levels,
4201 ¶ms.precinct_exponents,
4202 )?;
4203 let prepared_resolution_packets =
4204 ordered_prepared_resolution_packets(component_resolution_packets, options)?;
4205 let (resolution_packets, packet_descriptors, allow_packetization_accelerator) =
4206 if options.num_layers > 1 {
4207 let (resolution_packets, packet_descriptors) =
4208 encode_prepared_resolution_packets_layered(
4209 prepared_resolution_packets,
4210 options.num_layers,
4211 options.progression_order,
4212 &options.quality_layer_byte_targets,
4213 accelerator,
4214 )?;
4215 (resolution_packets, packet_descriptors, false)
4216 } else {
4217 let packet_descriptors = packet_descriptors_for_order(
4218 &prepared_resolution_packets,
4219 1,
4220 options.progression_order,
4221 )?;
4222 let resolution_packets =
4223 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
4224 (resolution_packets, packet_descriptors, true)
4225 };
4226
4227 let mut resolution_packets = resolution_packets;
4228 let packetized_tile = packetize_resolution_packets_with_options(
4229 &mut resolution_packets,
4230 &packet_descriptors,
4231 options.num_layers,
4232 num_components,
4233 options.progression_order,
4234 packet_encode::PacketMarkerOptions {
4235 write_sop: params.write_sop,
4236 write_eph: params.write_eph,
4237 separate_packet_headers: params.write_ppm || params.write_ppt,
4238 },
4239 allow_packetization_accelerator,
4240 packetization_requires_scalar(params, options.tile_part_packet_limit),
4241 accelerator,
4242 )?;
4243 Ok(packetized_tile)
4244}
4245
4246fn deinterleave_to_i64(
4247 pixels: &[u8],
4248 num_pixels: usize,
4249 num_components: u16,
4250 bit_depth: u8,
4251 signed: bool,
4252) -> Vec<Vec<i64>> {
4253 let nc = num_components as usize;
4254 let mut components = vec![vec![0_i64; num_pixels]; nc];
4255 let unsigned_offset = if signed {
4256 0
4257 } else {
4258 1_i64 << (u32::from(bit_depth) - 1)
4259 };
4260 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth).unwrap_or(5);
4261 for (i, pixel) in pixels
4262 .chunks_exact(nc * bytes_per_sample)
4263 .take(num_pixels)
4264 .enumerate()
4265 {
4266 for (component_idx, component) in components.iter_mut().enumerate().take(nc) {
4267 let offset = component_idx * bytes_per_sample;
4268 let raw = read_le_sample_value(&pixel[offset..offset + bytes_per_sample], bit_depth);
4269 component[i] = if signed {
4270 sign_extend_sample(raw, bit_depth)
4271 } else {
4272 i64::try_from(raw).unwrap_or(i64::MAX) - unsigned_offset
4273 };
4274 }
4275 }
4276 components
4277}
4278
4279fn typed_component_plane_to_i64(
4280 plane: &EncodeTypedComponentPlane<'_>,
4281 width: u32,
4282 height: u32,
4283) -> Result<Vec<i64>, &'static str> {
4284 let bytes_per_sample = raw_pixel_bytes_per_sample(plane.bit_depth)?;
4285 let sample_count = (width as usize)
4286 .checked_mul(height as usize)
4287 .ok_or("image dimensions overflow")?;
4288 let expected_len = sample_count
4289 .checked_mul(bytes_per_sample)
4290 .ok_or("image dimensions overflow")?;
4291 if plane.data.len() != expected_len {
4292 return Err("component plane data length mismatch");
4293 }
4294 let unsigned_offset = if plane.signed {
4295 0
4296 } else {
4297 1_i64 << (u32::from(plane.bit_depth) - 1)
4298 };
4299 Ok(plane
4300 .data
4301 .chunks_exact(bytes_per_sample)
4302 .map(|sample| {
4303 let raw = read_le_sample_value(sample, plane.bit_depth);
4304 if plane.signed {
4305 sign_extend_sample(raw, plane.bit_depth)
4306 } else {
4307 i64::try_from(raw).unwrap_or(i64::MAX) - unsigned_offset
4308 }
4309 })
4310 .collect())
4311}
4312
4313fn forward_rct_i64(components: &mut [Vec<i64>]) {
4314 debug_assert!(components.len() >= 3);
4315 let (r_components, rest) = components.split_at_mut(1);
4316 let (g_components, b_components) = rest.split_at_mut(1);
4317 let r_components = &mut r_components[0];
4318 let g_components = &mut g_components[0];
4319 let b_components = &mut b_components[0];
4320
4321 for ((r, g), b) in r_components
4322 .iter_mut()
4323 .zip(g_components.iter_mut())
4324 .zip(b_components.iter_mut())
4325 {
4326 let r0 = *r;
4327 let g0 = *g;
4328 let b0 = *b;
4329 *r = (r0 + 2 * g0 + b0).div_euclid(4);
4330 *g = b0 - g0;
4331 *b = r0 - g0;
4332 }
4333}
4334
4335struct EncodedTilePart {
4336 tile_index: u16,
4337 tile_part_index: u8,
4338 num_tile_parts: u8,
4339 data: Vec<u8>,
4340 packet_lengths: Vec<u32>,
4341 packet_headers: Vec<Vec<u8>>,
4342}
4343
4344fn split_packetized_tile_into_tile_parts(
4345 tile_index: u16,
4346 data: &[u8],
4347 packet_lengths: &[u32],
4348 packet_headers: &[Vec<u8>],
4349 packet_limit: Option<u16>,
4350) -> Result<Vec<EncodedTilePart>, &'static str> {
4351 if !packet_headers.is_empty() && packet_headers.len() != packet_lengths.len() {
4352 return Err("packet header count does not match packet length count");
4353 }
4354 let Some(packet_limit) = packet_limit else {
4355 return Ok(vec![EncodedTilePart {
4356 tile_index,
4357 tile_part_index: 0,
4358 num_tile_parts: 1,
4359 data: data.to_vec(),
4360 packet_lengths: packet_lengths.to_vec(),
4361 packet_headers: packet_headers.to_vec(),
4362 }]);
4363 };
4364 if packet_limit == 0 {
4365 return Err("tile-part packet limit must be non-zero");
4366 }
4367 if packet_lengths.is_empty() {
4368 return Ok(vec![EncodedTilePart {
4369 tile_index,
4370 tile_part_index: 0,
4371 num_tile_parts: 1,
4372 data: data.to_vec(),
4373 packet_lengths: Vec::new(),
4374 packet_headers: Vec::new(),
4375 }]);
4376 }
4377
4378 let expected_len = packet_lengths.iter().try_fold(0usize, |acc, &len| {
4379 acc.checked_add(usize::try_from(len).map_err(|_| "packet length exceeds usize")?)
4380 .ok_or("packet length sum overflow")
4381 })?;
4382 if expected_len != data.len() {
4383 return Err("packet lengths do not match tile data length");
4384 }
4385
4386 let packet_limit = usize::from(packet_limit);
4387 let num_tile_parts = packet_lengths.len().div_ceil(packet_limit);
4388 if num_tile_parts > usize::from(u8::MAX) {
4389 return Err("tile-part packet limit would emit more than 255 tile-parts");
4390 }
4391 let num_tile_parts = u8::try_from(num_tile_parts).map_err(|_| "tile-part count exceeds u8")?;
4392
4393 let mut parts = Vec::with_capacity(usize::from(num_tile_parts));
4394 let mut data_offset = 0usize;
4395 for (tile_part_index, packet_chunk) in packet_lengths.chunks(packet_limit).enumerate() {
4396 let chunk_len = packet_chunk.iter().try_fold(0usize, |acc, &len| {
4397 acc.checked_add(usize::try_from(len).map_err(|_| "packet length exceeds usize")?)
4398 .ok_or("packet length sum overflow")
4399 })?;
4400 let end = data_offset
4401 .checked_add(chunk_len)
4402 .ok_or("packet length sum overflow")?;
4403 let tile_part_index =
4404 u8::try_from(tile_part_index).map_err(|_| "tile-part index exceeds u8")?;
4405 parts.push(EncodedTilePart {
4406 tile_index,
4407 tile_part_index,
4408 num_tile_parts,
4409 data: data[data_offset..end].to_vec(),
4410 packet_lengths: packet_chunk.to_vec(),
4411 packet_headers: if packet_headers.is_empty() {
4412 Vec::new()
4413 } else {
4414 let packet_start = tile_part_index as usize * packet_limit;
4415 let packet_end = packet_start + packet_chunk.len();
4416 packet_headers[packet_start..packet_end].to_vec()
4417 },
4418 });
4419 data_offset = end;
4420 }
4421 Ok(parts)
4422}
4423
4424fn write_single_tile_packetized_codestream(
4425 params: &EncodeParams,
4426 packetized_tile: &packet_encode::PacketizedTileData,
4427 quant_params: &[(u16, u16)],
4428 tile_part_packet_limit: Option<u16>,
4429) -> Result<Vec<u8>, &'static str> {
4430 validate_packet_header_marker_payload(params, packetized_tile)?;
4431 let tile_parts = split_packetized_tile_into_tile_parts(
4432 0,
4433 &packetized_tile.data,
4434 &packetized_tile.packet_lengths,
4435 &packetized_tile.packet_headers,
4436 tile_part_packet_limit,
4437 )?;
4438 let codestream_tile_parts = tile_parts
4439 .iter()
4440 .map(|part| codestream_write::TilePartData {
4441 tile_index: part.tile_index,
4442 tile_part_index: part.tile_part_index,
4443 num_tile_parts: part.num_tile_parts,
4444 data: &part.data,
4445 packet_lengths: &part.packet_lengths,
4446 packet_headers: &part.packet_headers,
4447 })
4448 .collect::<Vec<_>>();
4449 Ok(codestream_write::write_codestream_tiles(
4450 params,
4451 &codestream_tile_parts,
4452 quant_params,
4453 ))
4454}
4455
4456fn validate_packet_header_marker_payload(
4457 params: &EncodeParams,
4458 packetized_tile: &packet_encode::PacketizedTileData,
4459) -> Result<(), &'static str> {
4460 if !params.write_ppm && !params.write_ppt {
4461 return Ok(());
4462 }
4463 if params.write_ppm && params.write_ppt {
4464 return Err("PPM and PPT packet header markers are mutually exclusive");
4465 }
4466 validate_packet_header_marker_payloads(
4467 params.write_ppm,
4468 params.write_ppt,
4469 &[&packetized_tile.packet_headers],
4470 )?;
4471 Ok(())
4472}
4473
4474fn validate_packet_header_marker_payloads(
4475 write_ppm: bool,
4476 write_ppt: bool,
4477 tile_packet_headers: &[&[Vec<u8>]],
4478) -> Result<(), &'static str> {
4479 const PACKET_HEADER_MARKER_PAYLOAD_LIMIT: usize = u16::MAX as usize - 3;
4480 const PPM_PACKET_HEADER_LIMIT: usize = PACKET_HEADER_MARKER_PAYLOAD_LIMIT - 2;
4481 const MAX_PACKET_HEADER_MARKERS: usize = u8::MAX as usize + 1;
4482
4483 if !write_ppm && !write_ppt {
4484 return Ok(());
4485 }
4486 if write_ppm && write_ppt {
4487 return Err("PPM and PPT packet header markers are mutually exclusive");
4488 }
4489 if tile_packet_headers.iter().any(|headers| headers.is_empty()) {
4490 return Err("PPM/PPT encode requires separated packet headers");
4491 }
4492 if write_ppm {
4493 let mut marker_count = 0usize;
4494 let mut payload_len = 0usize;
4495 for header in tile_packet_headers
4496 .iter()
4497 .flat_map(|headers| headers.iter())
4498 {
4499 if header.len() > PPM_PACKET_HEADER_LIMIT {
4500 return Err("PPM packet header exceeds marker payload limit");
4501 }
4502 let entry_len = 2usize
4503 .checked_add(header.len())
4504 .ok_or("PPM marker payload length overflow")?;
4505 if payload_len == 0 {
4506 marker_count = marker_count
4507 .checked_add(1)
4508 .ok_or("PPM marker count overflow")?;
4509 } else if payload_len
4510 .checked_add(entry_len)
4511 .is_none_or(|len| len > PACKET_HEADER_MARKER_PAYLOAD_LIMIT)
4512 {
4513 marker_count = marker_count
4514 .checked_add(1)
4515 .ok_or("PPM marker count overflow")?;
4516 payload_len = 0;
4517 }
4518 payload_len = payload_len
4519 .checked_add(entry_len)
4520 .ok_or("PPM marker payload length overflow")?;
4521 if marker_count > MAX_PACKET_HEADER_MARKERS {
4522 return Err("PPM packet headers require more than 256 marker segments");
4523 }
4524 }
4525 }
4526 if write_ppt {
4527 for headers in tile_packet_headers {
4528 let payload_len = headers.iter().try_fold(0usize, |acc, header| {
4529 acc.checked_add(header.len())
4530 .ok_or("PPT marker payload length overflow")
4531 })?;
4532 let marker_count = payload_len.div_ceil(PACKET_HEADER_MARKER_PAYLOAD_LIMIT);
4533 if marker_count > MAX_PACKET_HEADER_MARKERS {
4534 return Err("PPT packet headers require more than 256 marker segments");
4535 }
4536 }
4537 }
4538 Ok(())
4539}
4540fn encode_multitile_impl(
4541 pixels: &[u8],
4542 width: u32,
4543 height: u32,
4544 num_components: u16,
4545 bit_depth: u8,
4546 signed: bool,
4547 options: &EncodeOptions,
4548 block_coding_mode: BlockCodingMode,
4549 roi_regions: &[EncodeRoiRegion],
4550 component_sample_info: &[EncodeComponentSampleInfo],
4551 accelerator: &mut impl J2kEncodeStageAccelerator,
4552 tile_width: u32,
4553 tile_height: u32,
4554) -> Result<Vec<u8>, &'static str> {
4555 let num_x_tiles = width.div_ceil(tile_width);
4556 let num_y_tiles = height.div_ceil(tile_height);
4557 let num_tiles = num_x_tiles
4558 .checked_mul(num_y_tiles)
4559 .ok_or("tile count overflow")?;
4560 if num_tiles > u32::from(u16::MAX) + 1 {
4561 return Err("multi-tile encode supports at most 65536 tiles");
4562 }
4563
4564 let min_tile_width = if width.is_multiple_of(tile_width) {
4565 tile_width
4566 } else {
4567 width % tile_width
4568 };
4569 let min_tile_height = if height.is_multiple_of(tile_height) {
4570 tile_height
4571 } else {
4572 height % tile_height
4573 };
4574 let num_levels = options
4575 .num_decomposition_levels
4576 .min(max_decomposition_levels(min_tile_width, min_tile_height));
4577 let use_mct = options.use_mct && matches!(num_components, 3 | 4);
4578 let requested_guard_bits = if options.reversible {
4579 if use_mct {
4580 options.guard_bits.max(2)
4581 } else {
4582 options.guard_bits
4583 }
4584 } else {
4585 options.guard_bits.max(2)
4586 };
4587 let high_bit_exact = bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH;
4588 let guard_bits = if high_bit_exact && options.reversible {
4589 reversible_guard_bits_for_marker_limit(bit_depth, num_levels, requested_guard_bits)?
4590 } else {
4591 requested_guard_bits
4592 };
4593 let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
4594 let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
4595 bit_depth,
4596 num_levels,
4597 options.reversible,
4598 guard_bits,
4599 options.irreversible_quantization_scale,
4600 options.irreversible_quantization_subband_scales,
4601 );
4602 if options.reversible && reversible_guard_delta != 0 {
4603 adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
4604 }
4605 let quant_params: Vec<(u16, u16)> = step_sizes
4606 .iter()
4607 .map(|s| (s.exponent, s.mantissa))
4608 .collect();
4609 let mut component_step_sizes = component_step_sizes(
4610 component_sample_info,
4611 num_levels,
4612 options.reversible,
4613 guard_bits,
4614 options.irreversible_quantization_scale,
4615 options.irreversible_quantization_subband_scales,
4616 );
4617 if options.reversible && reversible_guard_delta != 0 {
4618 adjust_component_step_sizes_for_guard_delta(
4619 &mut component_step_sizes,
4620 reversible_guard_delta,
4621 )?;
4622 }
4623 let component_quantization_step_sizes = component_step_sizes
4624 .iter()
4625 .map(|steps| {
4626 steps
4627 .iter()
4628 .map(|step| (step.exponent, step.mantissa))
4629 .collect::<Vec<_>>()
4630 })
4631 .collect::<Vec<_>>();
4632
4633 let mut child_options = options.clone();
4634 child_options.num_decomposition_levels = num_levels;
4635 child_options.tile_size = None;
4636 child_options.write_tlm = false;
4637 child_options.write_plt = options.write_plt
4638 || options.write_plm
4639 || options.write_ppm
4640 || options.write_ppt
4641 || options.tile_part_packet_limit.is_some();
4642 child_options.write_plm = false;
4643 child_options.write_ppm = options.write_ppm || options.write_ppt;
4644 child_options.write_ppt = false;
4645
4646 let mut tile_bodies = Vec::with_capacity(num_tiles as usize);
4647 for tile_y in 0..num_y_tiles {
4648 for tile_x in 0..num_x_tiles {
4649 let tile_index = tile_y
4650 .checked_mul(num_x_tiles)
4651 .and_then(|base| base.checked_add(tile_x))
4652 .ok_or("tile index overflow")?;
4653 let tile_index = u16::try_from(tile_index).map_err(|_| "tile index exceeds u16")?;
4654 let x0 = tile_x * tile_width;
4655 let y0 = tile_y * tile_height;
4656 let actual_width = (width - x0).min(tile_width);
4657 let actual_height = (height - y0).min(tile_height);
4658 let tile_pixels = extract_interleaved_tile(
4659 pixels,
4660 width,
4661 x0,
4662 y0,
4663 actual_width,
4664 actual_height,
4665 num_components,
4666 bit_depth,
4667 )?;
4668 let tile_roi_regions =
4669 roi_regions_for_tile(roi_regions, x0, y0, actual_width, actual_height)?;
4670 let tile_codestream = encode_impl(
4671 &tile_pixels,
4672 actual_width,
4673 actual_height,
4674 num_components,
4675 bit_depth,
4676 signed,
4677 &child_options,
4678 block_coding_mode,
4679 &tile_roi_regions,
4680 component_sample_info,
4681 accelerator,
4682 )?;
4683 let packet_lengths = if options.write_plt
4684 || options.write_plm
4685 || options.write_ppm
4686 || options.write_ppt
4687 || options.tile_part_packet_limit.is_some()
4688 {
4689 extract_single_tile_plt_packet_lengths(&tile_codestream)?
4690 } else {
4691 Vec::new()
4692 };
4693 let packet_headers = if options.write_ppm || options.write_ppt {
4694 extract_single_tile_ppm_packet_headers(&tile_codestream)?
4695 } else {
4696 Vec::new()
4697 };
4698 tile_bodies.extend(split_packetized_tile_into_tile_parts(
4699 tile_index,
4700 extract_single_tile_body(&tile_codestream)?,
4701 &packet_lengths,
4702 &packet_headers,
4703 options.tile_part_packet_limit,
4704 )?);
4705 }
4706 }
4707
4708 let component_sampling = component_sampling_for_options(options, num_components)?;
4709 let roi_plans = roi_encode_plans_for_options(
4710 options,
4711 roi_regions,
4712 num_components,
4713 width,
4714 height,
4715 &component_sampling,
4716 max_total_bitplanes_for_components(&step_sizes, &component_step_sizes, guard_bits)?,
4717 block_coding_mode,
4718 )?;
4719 let precinct_exponents = precinct_exponents_for_options(options, num_levels)?;
4720 let params = EncodeParams {
4721 width,
4722 height,
4723 tile_width,
4724 tile_height,
4725 num_components,
4726 bit_depth,
4727 signed,
4728 component_sample_info: component_sample_info.to_vec(),
4729 component_quantization_step_sizes,
4730 num_decomposition_levels: num_levels,
4731 reversible: options.reversible,
4732 code_block_width_exp: options.code_block_width_exp,
4733 code_block_height_exp: options.code_block_height_exp,
4734 num_layers: options.num_layers,
4735 use_mct,
4736 guard_bits,
4737 block_coding_mode,
4738 progression_order: options.progression_order,
4739 write_tlm: options.write_tlm,
4740 write_plt: options.write_plt,
4741 write_plm: options.write_plm,
4742 write_ppm: options.write_ppm,
4743 write_ppt: options.write_ppt,
4744 write_sop: options.write_sop,
4745 write_eph: options.write_eph,
4746 terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
4747 && options.num_layers > 1,
4748 component_sampling,
4749 roi_component_shifts: roi_plans.iter().map(|plan| plan.shift).collect(),
4750 precinct_exponents,
4751 };
4752 let tile_packet_headers = tile_bodies
4753 .iter()
4754 .map(|tile| tile.packet_headers.as_slice())
4755 .collect::<Vec<_>>();
4756 validate_packet_header_marker_payloads(
4757 params.write_ppm,
4758 params.write_ppt,
4759 &tile_packet_headers,
4760 )?;
4761 let tile_parts = tile_bodies
4762 .iter()
4763 .map(|tile| codestream_write::TilePartData {
4764 tile_index: tile.tile_index,
4765 tile_part_index: tile.tile_part_index,
4766 num_tile_parts: tile.num_tile_parts,
4767 data: &tile.data,
4768 packet_lengths: &tile.packet_lengths,
4769 packet_headers: &tile.packet_headers,
4770 })
4771 .collect::<Vec<_>>();
4772
4773 Ok(codestream_write::write_codestream_tiles(
4774 ¶ms,
4775 &tile_parts,
4776 &quant_params,
4777 ))
4778}
4779
4780fn extract_interleaved_tile(
4781 pixels: &[u8],
4782 image_width: u32,
4783 x0: u32,
4784 y0: u32,
4785 tile_width: u32,
4786 tile_height: u32,
4787 num_components: u16,
4788 bit_depth: u8,
4789) -> Result<Vec<u8>, &'static str> {
4790 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
4791 let bytes_per_pixel = usize::from(num_components)
4792 .checked_mul(bytes_per_sample)
4793 .ok_or("pixel stride overflow")?;
4794 let row_bytes = usize::try_from(tile_width)
4795 .map_err(|_| "tile width exceeds usize")?
4796 .checked_mul(bytes_per_pixel)
4797 .ok_or("tile row byte count overflow")?;
4798 let out_len = row_bytes
4799 .checked_mul(usize::try_from(tile_height).map_err(|_| "tile height exceeds usize")?)
4800 .ok_or("tile byte count overflow")?;
4801 let mut tile = Vec::with_capacity(out_len);
4802 let image_row_bytes = usize::try_from(image_width)
4803 .map_err(|_| "image width exceeds usize")?
4804 .checked_mul(bytes_per_pixel)
4805 .ok_or("image row byte count overflow")?;
4806 let x_byte_offset = usize::try_from(x0)
4807 .map_err(|_| "tile x offset exceeds usize")?
4808 .checked_mul(bytes_per_pixel)
4809 .ok_or("tile x byte offset overflow")?;
4810
4811 for y in y0..y0 + tile_height {
4812 let row_start = usize::try_from(y)
4813 .map_err(|_| "tile y offset exceeds usize")?
4814 .checked_mul(image_row_bytes)
4815 .and_then(|offset| offset.checked_add(x_byte_offset))
4816 .ok_or("tile row offset overflow")?;
4817 let row_end = row_start
4818 .checked_add(row_bytes)
4819 .ok_or("tile row range overflow")?;
4820 tile.extend_from_slice(
4821 pixels
4822 .get(row_start..row_end)
4823 .ok_or("tile row range outside source pixels")?,
4824 );
4825 }
4826
4827 Ok(tile)
4828}
4829
4830fn extract_component_plane_tile(
4831 data: &[u8],
4832 image_width: u32,
4833 x0: u32,
4834 y0: u32,
4835 tile_width: u32,
4836 tile_height: u32,
4837 bit_depth: u8,
4838) -> Result<Vec<u8>, &'static str> {
4839 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
4840 let row_bytes = usize::try_from(tile_width)
4841 .map_err(|_| "tile width exceeds usize")?
4842 .checked_mul(bytes_per_sample)
4843 .ok_or("tile row byte count overflow")?;
4844 let out_len = row_bytes
4845 .checked_mul(usize::try_from(tile_height).map_err(|_| "tile height exceeds usize")?)
4846 .ok_or("tile byte count overflow")?;
4847 let mut tile = Vec::with_capacity(out_len);
4848 let image_row_bytes = usize::try_from(image_width)
4849 .map_err(|_| "image width exceeds usize")?
4850 .checked_mul(bytes_per_sample)
4851 .ok_or("image row byte count overflow")?;
4852 let x_byte_offset = usize::try_from(x0)
4853 .map_err(|_| "tile x offset exceeds usize")?
4854 .checked_mul(bytes_per_sample)
4855 .ok_or("tile x byte offset overflow")?;
4856
4857 for y in y0..y0 + tile_height {
4858 let row_start = usize::try_from(y)
4859 .map_err(|_| "tile y offset exceeds usize")?
4860 .checked_mul(image_row_bytes)
4861 .and_then(|offset| offset.checked_add(x_byte_offset))
4862 .ok_or("tile row offset overflow")?;
4863 let row_end = row_start
4864 .checked_add(row_bytes)
4865 .ok_or("tile row range overflow")?;
4866 tile.extend_from_slice(
4867 data.get(row_start..row_end)
4868 .ok_or("component plane tile row range outside source data")?,
4869 );
4870 }
4871
4872 Ok(tile)
4873}
4874
4875fn roi_regions_for_tile(
4876 roi_regions: &[EncodeRoiRegion],
4877 tile_x: u32,
4878 tile_y: u32,
4879 tile_width: u32,
4880 tile_height: u32,
4881) -> Result<Vec<EncodeRoiRegion>, &'static str> {
4882 let tile_x1 = tile_x
4883 .checked_add(tile_width)
4884 .ok_or("tile ROI bounds overflow")?;
4885 let tile_y1 = tile_y
4886 .checked_add(tile_height)
4887 .ok_or("tile ROI bounds overflow")?;
4888 let mut clipped = Vec::new();
4889
4890 for region in roi_regions {
4891 let region_x1 = region
4892 .x
4893 .checked_add(region.width)
4894 .ok_or("ROI region bounds overflow")?;
4895 let region_y1 = region
4896 .y
4897 .checked_add(region.height)
4898 .ok_or("ROI region bounds overflow")?;
4899 let x0 = region.x.max(tile_x);
4900 let y0 = region.y.max(tile_y);
4901 let x1 = region_x1.min(tile_x1);
4902 let y1 = region_y1.min(tile_y1);
4903 if x0 >= x1 || y0 >= y1 {
4904 continue;
4905 }
4906 clipped.push(EncodeRoiRegion {
4907 component: region.component,
4908 x: x0 - tile_x,
4909 y: y0 - tile_y,
4910 width: x1 - x0,
4911 height: y1 - y0,
4912 shift: region.shift,
4913 });
4914 }
4915
4916 Ok(clipped)
4917}
4918
4919fn extract_single_tile_body(codestream: &[u8]) -> Result<&[u8], &'static str> {
4920 let sod = codestream
4921 .windows(2)
4922 .position(|marker| marker == [0xFF, super::codestream::markers::SOD])
4923 .ok_or("encoded tile codestream missing SOD")?;
4924 let eoc = codestream
4925 .windows(2)
4926 .rposition(|marker| marker == [0xFF, super::codestream::markers::EOC])
4927 .ok_or("encoded tile codestream missing EOC")?;
4928 if eoc < sod + 2 {
4929 return Err("encoded tile codestream marker order invalid");
4930 }
4931 Ok(&codestream[sod + 2..eoc])
4932}
4933
4934fn extract_single_tile_plt_packet_lengths(codestream: &[u8]) -> Result<Vec<u32>, &'static str> {
4935 let sod = codestream
4936 .windows(2)
4937 .position(|marker| marker == [0xFF, super::codestream::markers::SOD])
4938 .ok_or("encoded tile codestream missing SOD")?;
4939 let mut packet_lengths = Vec::new();
4940 let mut offset = 0usize;
4941
4942 while offset + 4 <= sod {
4943 if codestream[offset] == 0xFF && codestream[offset + 1] == super::codestream::markers::PLT {
4944 let marker_len =
4945 u16::from_be_bytes([codestream[offset + 2], codestream[offset + 3]]) as usize;
4946 if marker_len < 3 {
4947 return Err("encoded tile codestream has invalid PLT length");
4948 }
4949 let marker_end = offset
4950 .checked_add(2)
4951 .and_then(|value| value.checked_add(marker_len))
4952 .ok_or("encoded tile codestream PLT length overflow")?;
4953 if marker_end > sod {
4954 return Err("encoded tile codestream PLT extends past SOD");
4955 }
4956 let length_bytes = codestream
4957 .get(offset + 5..marker_end)
4958 .ok_or("encoded tile codestream PLT payload out of range")?;
4959 packet_lengths.extend(
4960 super::codestream::decode_packet_lengths(length_bytes)
4961 .ok_or("encoded tile codestream has invalid PLT packet lengths")?,
4962 );
4963 offset = marker_end;
4964 } else {
4965 offset += 1;
4966 }
4967 }
4968
4969 if packet_lengths.is_empty() {
4970 return Err("encoded tile codestream missing PLT packet lengths");
4971 }
4972
4973 Ok(packet_lengths)
4974}
4975
4976fn extract_single_tile_ppm_packet_headers(codestream: &[u8]) -> Result<Vec<Vec<u8>>, &'static str> {
4977 let sot = codestream
4978 .windows(2)
4979 .position(|marker| marker == [0xFF, super::codestream::markers::SOT])
4980 .ok_or("encoded tile codestream missing SOT")?;
4981 let mut packet_headers = Vec::new();
4982 let mut offset = 0usize;
4983
4984 while offset + 4 <= sot {
4985 if codestream[offset] == 0xFF && codestream[offset + 1] == super::codestream::markers::PPM {
4986 let marker_len =
4987 u16::from_be_bytes([codestream[offset + 2], codestream[offset + 3]]) as usize;
4988 if marker_len < 3 {
4989 return Err("encoded tile codestream has invalid PPM length");
4990 }
4991 let marker_end = offset
4992 .checked_add(2)
4993 .and_then(|value| value.checked_add(marker_len))
4994 .ok_or("encoded tile codestream PPM length overflow")?;
4995 if marker_end > sot {
4996 return Err("encoded tile codestream PPM extends past SOT");
4997 }
4998 let mut payload_offset = offset + 5;
4999 while payload_offset < marker_end {
5000 let header_len_end = payload_offset
5001 .checked_add(2)
5002 .ok_or("encoded tile codestream PPM payload overflow")?;
5003 let len_bytes = codestream
5004 .get(payload_offset..header_len_end)
5005 .ok_or("encoded tile codestream PPM packet length truncated")?;
5006 let header_len = u16::from_be_bytes([len_bytes[0], len_bytes[1]]) as usize;
5007 let header_start = header_len_end;
5008 let header_end = header_start
5009 .checked_add(header_len)
5010 .ok_or("encoded tile codestream PPM packet header overflow")?;
5011 let header = codestream
5012 .get(header_start..header_end)
5013 .ok_or("encoded tile codestream PPM packet header truncated")?;
5014 packet_headers.push(header.to_vec());
5015 payload_offset = header_end;
5016 }
5017 offset = marker_end;
5018 } else {
5019 offset += 1;
5020 }
5021 }
5022
5023 if packet_headers.is_empty() {
5024 return Err("encoded tile codestream missing PPM packet headers");
5025 }
5026
5027 Ok(packet_headers)
5028}
5029
5030fn try_encode_forward_rct(
5031 components: &mut [Vec<f32>],
5032 accelerator: &mut impl J2kEncodeStageAccelerator,
5033) -> Result<bool, &'static str> {
5034 debug_assert!(components.len() >= 3);
5035 let (plane0, rest) = components.split_at_mut(1);
5036 let (plane1, plane2) = rest.split_at_mut(1);
5037 accelerator.encode_forward_rct(J2kForwardRctJob {
5038 plane0: &mut plane0[0],
5039 plane1: &mut plane1[0],
5040 plane2: &mut plane2[0],
5041 })
5042}
5043
5044fn try_encode_forward_ict(
5045 components: &mut [Vec<f32>],
5046 accelerator: &mut impl J2kEncodeStageAccelerator,
5047) -> Result<bool, &'static str> {
5048 debug_assert!(components.len() >= 3);
5049 let (plane0, rest) = components.split_at_mut(1);
5050 let (plane1, plane2) = rest.split_at_mut(1);
5051 accelerator.encode_forward_ict(J2kForwardIctJob {
5052 plane0: &mut plane0[0],
5053 plane1: &mut plane1[0],
5054 plane2: &mut plane2[0],
5055 })
5056}
5057
5058fn encode_forward_dwt(
5059 component: &[f32],
5060 width: u32,
5061 height: u32,
5062 num_levels: u8,
5063 reversible: bool,
5064 accelerator: &mut impl J2kEncodeStageAccelerator,
5065) -> Result<DwtDecomposition, &'static str> {
5066 if reversible {
5067 if let Some(output) = accelerator.encode_forward_dwt53(J2kForwardDwt53Job {
5068 samples: component,
5069 width,
5070 height,
5071 num_levels,
5072 })? {
5073 return convert_forward_dwt53_output(output);
5074 }
5075 } else if let Some(output) = accelerator.encode_forward_dwt97(J2kForwardDwt97Job {
5076 samples: component,
5077 width,
5078 height,
5079 num_levels,
5080 })? {
5081 return convert_forward_dwt97_output(output);
5082 }
5083
5084 Ok(fdwt::forward_dwt(
5085 component, width, height, num_levels, reversible,
5086 ))
5087}
5088
5089fn convert_forward_dwt53_output(
5090 output: J2kForwardDwt53Output,
5091) -> Result<DwtDecomposition, &'static str> {
5092 validate_band_len(output.ll.len(), output.ll_width, output.ll_height)?;
5093 let mut levels = Vec::with_capacity(output.levels.len());
5094 for level in output.levels {
5095 validate_dwt53_level(&level)?;
5096 levels.push(fdwt::DwtLevel {
5097 hl: level.hl,
5098 lh: level.lh,
5099 hh: level.hh,
5100 low_width: level.low_width,
5101 low_height: level.low_height,
5102 high_width: level.high_width,
5103 high_height: level.high_height,
5104 });
5105 }
5106 Ok(DwtDecomposition {
5107 ll: output.ll,
5108 ll_width: output.ll_width,
5109 ll_height: output.ll_height,
5110 levels,
5111 })
5112}
5113
5114fn convert_forward_dwt97_output(
5115 output: J2kForwardDwt97Output,
5116) -> Result<DwtDecomposition, &'static str> {
5117 validate_band_len(output.ll.len(), output.ll_width, output.ll_height)?;
5118 let mut levels = Vec::with_capacity(output.levels.len());
5119 for level in output.levels {
5120 validate_dwt97_level(&level)?;
5121 levels.push(fdwt::DwtLevel {
5122 hl: level.hl,
5123 lh: level.lh,
5124 hh: level.hh,
5125 low_width: level.low_width,
5126 low_height: level.low_height,
5127 high_width: level.high_width,
5128 high_height: level.high_height,
5129 });
5130 }
5131 Ok(DwtDecomposition {
5132 ll: output.ll,
5133 ll_width: output.ll_width,
5134 ll_height: output.ll_height,
5135 levels,
5136 })
5137}
5138
5139fn validate_dwt53_level(level: &J2kForwardDwt53Level) -> Result<(), &'static str> {
5140 validate_band_len(level.hl.len(), level.high_width, level.low_height)?;
5141 validate_band_len(level.lh.len(), level.low_width, level.high_height)?;
5142 validate_band_len(level.hh.len(), level.high_width, level.high_height)?;
5143 Ok(())
5144}
5145
5146fn validate_dwt97_level(level: &J2kForwardDwt97Level) -> Result<(), &'static str> {
5147 validate_band_len(level.hl.len(), level.high_width, level.low_height)?;
5148 validate_band_len(level.lh.len(), level.low_width, level.high_height)?;
5149 validate_band_len(level.hh.len(), level.high_width, level.high_height)?;
5150 Ok(())
5151}
5152
5153fn validate_band_len(actual: usize, width: u32, height: u32) -> Result<(), &'static str> {
5154 let expected = (width as usize)
5155 .checked_mul(height as usize)
5156 .ok_or("accelerated DWT output dimensions overflow")?;
5157 if actual != expected {
5158 return Err("accelerated DWT output length mismatch");
5159 }
5160 Ok(())
5161}
5162
5163fn validate_deinterleaved_components(
5164 components: Vec<Vec<f32>>,
5165 num_components: u16,
5166 num_pixels: usize,
5167) -> Result<Vec<Vec<f32>>, &'static str> {
5168 if components.len() != usize::from(num_components) {
5169 return Err("accelerated deinterleave component count mismatch");
5170 }
5171 if components
5172 .iter()
5173 .any(|component| component.len() != num_pixels)
5174 {
5175 return Err("accelerated deinterleave component length mismatch");
5176 }
5177 Ok(components)
5178}
5179
5180fn component_plane_to_f32(
5181 data: &[u8],
5182 width: u32,
5183 height: u32,
5184 bit_depth: u8,
5185 signed: bool,
5186) -> Result<Vec<f32>, &'static str> {
5187 let sample_count = (width as usize)
5188 .checked_mul(height as usize)
5189 .ok_or("image dimensions overflow")?;
5190 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
5191 let expected_len = sample_count
5192 .checked_mul(bytes_per_sample)
5193 .ok_or("image dimensions overflow")?;
5194 if data.len() != expected_len {
5195 return Err("component plane data length mismatch");
5196 }
5197
5198 let unsigned_offset = if signed {
5199 0.0
5200 } else {
5201 (1_u64 << (u32::from(bit_depth) - 1)) as f32
5202 };
5203 Ok(data
5204 .chunks_exact(bytes_per_sample)
5205 .map(|sample| {
5206 let raw = read_le_sample_value(sample, bit_depth);
5207 if signed {
5208 sign_extend_sample(raw, bit_depth) as f32
5209 } else {
5210 raw as f32 - unsigned_offset
5211 }
5212 })
5213 .collect())
5214}
5215
5216fn forward_dwt53_output_from_decomposition(
5217 decomposition: DwtDecomposition,
5218) -> J2kForwardDwt53Output {
5219 J2kForwardDwt53Output {
5220 ll: decomposition.ll,
5221 ll_width: decomposition.ll_width,
5222 ll_height: decomposition.ll_height,
5223 levels: decomposition
5224 .levels
5225 .into_iter()
5226 .map(|level| {
5227 let width = level.low_width + level.high_width;
5228 let height = level.low_height + level.high_height;
5229 J2kForwardDwt53Level {
5230 hl: level.hl,
5231 lh: level.lh,
5232 hh: level.hh,
5233 width,
5234 height,
5235 low_width: level.low_width,
5236 low_height: level.low_height,
5237 high_width: level.high_width,
5238 high_height: level.high_height,
5239 }
5240 })
5241 .collect(),
5242 }
5243}
5244
5245fn validate_component_sampling_dwt_geometry(
5246 decompositions: &[DwtDecomposition],
5247 reference_width: u32,
5248 reference_height: u32,
5249 component_sampling: &[(u8, u8)],
5250) -> Result<(), &'static str> {
5251 if decompositions.len() != component_sampling.len() {
5252 return Err("component sampling count does not match component count");
5253 }
5254 for (decomposition, &(x_rsiz, y_rsiz)) in decompositions.iter().zip(component_sampling) {
5255 let expected_width = reference_width.div_ceil(u32::from(x_rsiz.max(1)));
5256 let expected_height = reference_height.div_ceil(u32::from(y_rsiz.max(1)));
5257 if dwt_decomposition_dimensions(decomposition) != (expected_width, expected_height) {
5258 return Err("component sampling requires component-sized DWT geometry");
5259 }
5260 }
5261 Ok(())
5262}
5263
5264fn dwt_decomposition_dimensions(decomposition: &DwtDecomposition) -> (u32, u32) {
5265 decomposition
5266 .levels
5267 .last()
5268 .map_or((decomposition.ll_width, decomposition.ll_height), |level| {
5269 (
5270 level.low_width + level.high_width,
5271 level.low_height + level.high_height,
5272 )
5273 })
5274}
5275
5276#[derive(Clone, Debug, Default)]
5277struct ComponentRoiEncodePlan {
5278 shift: u8,
5279 regions: Vec<ComponentRoiEncodeRegion>,
5280}
5281
5282#[derive(Clone, Copy, Debug)]
5283struct ComponentRoiEncodeRegion {
5284 x: u32,
5285 y: u32,
5286 width: u32,
5287 height: u32,
5288}
5289
5290fn component_sampling_for_options(
5291 options: &EncodeOptions,
5292 num_components: u16,
5293) -> Result<Vec<(u8, u8)>, &'static str> {
5294 match &options.component_sampling {
5295 Some(component_sampling) => {
5296 if component_sampling.len() != usize::from(num_components) {
5297 return Err("component sampling count does not match component count");
5298 }
5299 if component_sampling
5300 .iter()
5301 .any(|&(x_rsiz, y_rsiz)| x_rsiz == 0 || y_rsiz == 0)
5302 {
5303 return Err("component sampling factors must be non-zero");
5304 }
5305 Ok(component_sampling.clone())
5306 }
5307 None => Ok(vec![(1, 1); usize::from(num_components)]),
5308 }
5309}
5310
5311fn roi_encode_plans_for_options(
5312 options: &EncodeOptions,
5313 roi_regions: &[EncodeRoiRegion],
5314 num_components: u16,
5315 width: u32,
5316 height: u32,
5317 component_sampling: &[(u8, u8)],
5318 base_bitplanes: u8,
5319 block_coding_mode: BlockCodingMode,
5320) -> Result<Vec<ComponentRoiEncodePlan>, &'static str> {
5321 let whole_component_shifts = roi_component_shifts_for_options(
5322 options,
5323 num_components,
5324 base_bitplanes,
5325 block_coding_mode,
5326 )?;
5327 let mut plans = whole_component_shifts
5328 .iter()
5329 .map(|&shift| ComponentRoiEncodePlan {
5330 shift,
5331 regions: Vec::new(),
5332 })
5333 .collect::<Vec<_>>();
5334
5335 for region in roi_regions {
5336 if region.component >= num_components {
5337 return Err("ROI region component index out of range");
5338 }
5339 if region.width == 0 || region.height == 0 {
5340 return Err("ROI region dimensions must be non-zero");
5341 }
5342 if region.shift == 0 {
5343 return Err("ROI region maxshift must be non-zero");
5344 }
5345
5346 let x1 = region
5347 .x
5348 .checked_add(region.width)
5349 .ok_or("ROI region bounds overflow")?;
5350 let y1 = region
5351 .y
5352 .checked_add(region.height)
5353 .ok_or("ROI region bounds overflow")?;
5354 if region.x >= width || region.y >= height || x1 > width || y1 > height {
5355 return Err("ROI region must be inside image bounds");
5356 }
5357
5358 let component_idx = usize::from(region.component);
5359 if whole_component_shifts[component_idx] != 0 {
5360 return Err("ROI region cannot be combined with whole-component ROI shift");
5361 }
5362 if region.shift < base_bitplanes {
5363 return Err("ROI region maxshift must cover background bitplanes");
5364 }
5365 validate_roi_shift(region.shift, base_bitplanes, block_coding_mode)?;
5366
5367 let plan = &mut plans[component_idx];
5368 if plan.shift == 0 {
5369 plan.shift = region.shift;
5370 } else if plan.shift != region.shift {
5371 return Err("ROI regions for one component must use one maxshift");
5372 }
5373
5374 let &(x_rsiz, y_rsiz) = component_sampling
5375 .get(component_idx)
5376 .ok_or("component sampling count does not match component count")?;
5377 let component_width = width.div_ceil(u32::from(x_rsiz));
5378 let component_height = height.div_ceil(u32::from(y_rsiz));
5379 let component_x0 = region.x / u32::from(x_rsiz);
5380 let component_y0 = region.y / u32::from(y_rsiz);
5381 let component_x1 = x1.div_ceil(u32::from(x_rsiz)).min(component_width);
5382 let component_y1 = y1.div_ceil(u32::from(y_rsiz)).min(component_height);
5383 if component_x0 >= component_x1 || component_y0 >= component_y1 {
5384 return Err("ROI region does not intersect component grid");
5385 }
5386 plan.regions.push(ComponentRoiEncodeRegion {
5387 x: component_x0,
5388 y: component_y0,
5389 width: component_x1 - component_x0,
5390 height: component_y1 - component_y0,
5391 });
5392 }
5393
5394 Ok(plans)
5395}
5396
5397fn roi_component_shifts_for_options(
5398 options: &EncodeOptions,
5399 num_components: u16,
5400 base_bitplanes: u8,
5401 block_coding_mode: BlockCodingMode,
5402) -> Result<Vec<u8>, &'static str> {
5403 if options.roi_component_shifts.is_empty() {
5404 return Ok(vec![0; usize::from(num_components)]);
5405 }
5406 if options.roi_component_shifts.len() != usize::from(num_components) {
5407 return Err("ROI component shift count does not match component count");
5408 }
5409 let max_bitplanes = max_roi_coded_bitplanes(block_coding_mode);
5410 for &shift in &options.roi_component_shifts {
5411 validate_roi_shift_for_max(shift, base_bitplanes, max_bitplanes)?;
5412 }
5413 Ok(options.roi_component_shifts.clone())
5414}
5415
5416fn validate_roi_shift(
5417 shift: u8,
5418 base_bitplanes: u8,
5419 block_coding_mode: BlockCodingMode,
5420) -> Result<(), &'static str> {
5421 let max_bitplanes = max_roi_coded_bitplanes(block_coding_mode);
5422 validate_roi_shift_for_max(shift, base_bitplanes, max_bitplanes)
5423}
5424
5425fn max_roi_coded_bitplanes(block_coding_mode: BlockCodingMode) -> u8 {
5426 match block_coding_mode {
5427 BlockCodingMode::Classic => MAX_CLASSIC_ROI_CODED_BITPLANES,
5428 BlockCodingMode::HighThroughput => MAX_HT_ROI_CODED_BITPLANES,
5429 }
5430}
5431
5432fn validate_roi_shift_for_max(
5433 shift: u8,
5434 base_bitplanes: u8,
5435 max_bitplanes: u8,
5436) -> Result<(), &'static str> {
5437 if base_bitplanes
5438 .checked_add(shift)
5439 .is_none_or(|bitplanes| bitplanes > max_bitplanes)
5440 {
5441 return Err("ROI maxshift exceeds supported coded bitplane count");
5442 }
5443 Ok(())
5444}
5445
5446fn roi_subband_scale(num_levels: u8, level_idx: Option<usize>) -> Result<u32, &'static str> {
5447 let shift = match level_idx {
5448 Some(level_idx) => usize::from(num_levels)
5449 .checked_sub(level_idx)
5450 .ok_or("ROI subband level exceeds decomposition level count")?,
5451 None => usize::from(num_levels),
5452 };
5453 if shift >= u32::BITS as usize {
5454 return Err("ROI subband scale exceeds supported coordinate range");
5455 }
5456 Ok(1_u32 << shift)
5457}
5458
5459fn max_total_bitplanes(step_sizes: &[QuantStepSize], guard_bits: u8) -> Result<u8, &'static str> {
5460 step_sizes
5461 .iter()
5462 .map(|step_size| {
5463 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
5464 guard_bits
5465 .checked_add(
5466 u8::try_from(step_size.exponent)
5467 .map_err(|_| "quantization exponent exceeds supported bitplane count")?,
5468 )
5469 .and_then(|value| value.checked_sub(1))
5470 .ok_or("quantization bitplane count underflows")
5471 })
5472 .max()
5473 .unwrap_or(Ok(0))
5474}
5475
5476fn max_total_bitplanes_for_components(
5477 default_step_sizes: &[QuantStepSize],
5478 component_step_sizes: &[Vec<QuantStepSize>],
5479 guard_bits: u8,
5480) -> Result<u8, &'static str> {
5481 let default = max_total_bitplanes(default_step_sizes, guard_bits)?;
5482 component_step_sizes
5483 .iter()
5484 .try_fold(default, |max_bitplanes, step_sizes| {
5485 Ok(max_bitplanes.max(max_total_bitplanes(step_sizes, guard_bits)?))
5486 })
5487}
5488
5489fn validate_component_sample_info(
5490 component_sample_info: &[EncodeComponentSampleInfo],
5491 num_components: usize,
5492) -> Result<(), &'static str> {
5493 if component_sample_info.is_empty() {
5494 return Ok(());
5495 }
5496 if component_sample_info.len() != num_components {
5497 return Err("component sample metadata count does not match component count");
5498 }
5499 if component_sample_info
5500 .iter()
5501 .any(|info| info.bit_depth == 0 || info.bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH)
5502 {
5503 return Err("unsupported bit depth");
5504 }
5505 Ok(())
5506}
5507
5508fn component_step_sizes(
5509 component_sample_info: &[EncodeComponentSampleInfo],
5510 num_levels: u8,
5511 reversible: bool,
5512 guard_bits: u8,
5513 quantization_scale: f32,
5514 subband_scales: IrreversibleQuantizationSubbandScales,
5515) -> Vec<Vec<QuantStepSize>> {
5516 component_sample_info
5517 .iter()
5518 .map(|info| {
5519 quantize::compute_step_sizes_with_irreversible_profile(
5520 info.bit_depth,
5521 num_levels,
5522 reversible,
5523 guard_bits,
5524 quantization_scale,
5525 subband_scales,
5526 )
5527 })
5528 .collect()
5529}
5530
5531fn reversible_guard_bits_for_marker_limit(
5532 bit_depth: u8,
5533 num_levels: u8,
5534 requested_guard_bits: u8,
5535) -> Result<u8, &'static str> {
5536 if requested_guard_bits > MAX_REVERSIBLE_NO_QUANT_GUARD_BITS {
5537 return Err("reversible guard bits exceed the Part 1 marker field");
5538 }
5539 let max_reversible_gain = if num_levels == 0 { 0 } else { 2 };
5540 let requested_bitplanes = u16::from(requested_guard_bits)
5541 .checked_add(u16::from(bit_depth))
5542 .and_then(|value| value.checked_add(max_reversible_gain))
5543 .and_then(|value| value.checked_sub(1))
5544 .ok_or("reversible no-quantization bitplane count underflows")?;
5545 if requested_bitplanes > MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES {
5546 return Err("25-38 bit reversible encode exceeds the current no-quantization guard/exponent signaling limit");
5547 }
5548 let min_guard_bits = requested_bitplanes.saturating_sub(MAX_REVERSIBLE_NO_QUANT_EXPONENT - 1);
5549 let guard_bits = requested_guard_bits
5550 .max(u8::try_from(min_guard_bits).map_err(|_| "reversible guard bits exceed u8")?);
5551 if guard_bits > MAX_REVERSIBLE_NO_QUANT_GUARD_BITS {
5552 return Err("reversible guard bits exceed the Part 1 marker field");
5553 }
5554 Ok(guard_bits)
5555}
5556
5557fn adjust_component_step_sizes_for_guard_delta(
5558 component_step_sizes: &mut [Vec<QuantStepSize>],
5559 guard_delta: u8,
5560) -> Result<(), &'static str> {
5561 for step_sizes in component_step_sizes {
5562 adjust_reversible_step_sizes_for_guard_delta(step_sizes, guard_delta)?;
5563 }
5564 Ok(())
5565}
5566
5567fn adjust_reversible_step_sizes_for_guard_delta(
5568 step_sizes: &mut [QuantStepSize],
5569 guard_delta: u8,
5570) -> Result<(), &'static str> {
5571 let guard_delta = u16::from(guard_delta);
5572 for step in step_sizes {
5573 step.exponent = step
5574 .exponent
5575 .checked_sub(guard_delta)
5576 .ok_or("reversible no-quantization exponent underflows guard-bit adjustment")?;
5577 if step.exponent > MAX_REVERSIBLE_NO_QUANT_EXPONENT {
5578 return Err("reversible no-quantization exponent exceeds the Part 1 marker field");
5579 }
5580 }
5581 Ok(())
5582}
5583
5584fn precinct_exponents_for_options(
5585 options: &EncodeOptions,
5586 num_decomposition_levels: u8,
5587) -> Result<Vec<(u8, u8)>, &'static str> {
5588 if options.precinct_exponents.is_empty() {
5589 return Ok(Vec::new());
5590 }
5591
5592 let expected = usize::from(num_decomposition_levels) + 1;
5593 if options.precinct_exponents.len() != expected {
5594 return Err("precinct exponent count must match resolution level count");
5595 }
5596 if options
5597 .precinct_exponents
5598 .iter()
5599 .any(|&(ppx, ppy)| ppx > 15 || ppy > 15)
5600 {
5601 return Err("precinct exponents must fit in COD marker nybbles");
5602 }
5603 let code_block_width_exp = options.code_block_width_exp + 2;
5604 let code_block_height_exp = options.code_block_height_exp + 2;
5605 for (resolution, &(ppx, ppy)) in options.precinct_exponents.iter().enumerate() {
5606 let min_ppx = if resolution == 0 {
5607 code_block_width_exp
5608 } else {
5609 code_block_width_exp + 1
5610 };
5611 let min_ppy = if resolution == 0 {
5612 code_block_height_exp
5613 } else {
5614 code_block_height_exp + 1
5615 };
5616 if ppx < min_ppx || ppy < min_ppy {
5617 return Err("precinct exponents must not reduce encoder code-block dimensions");
5618 }
5619 }
5620 Ok(options.precinct_exponents.clone())
5621}
5622
5623fn count_code_blocks(resolution_packets: &[ResolutionPacket]) -> Result<u32, &'static str> {
5624 let count = resolution_packets
5625 .iter()
5626 .flat_map(|resolution| resolution.subbands.iter())
5627 .try_fold(0usize, |acc, subband| {
5628 acc.checked_add(subband.code_blocks.len())
5629 .ok_or("packetization code-block count overflow")
5630 })?;
5631 u32::try_from(count).map_err(|_| "packetization code-block count exceeds u32")
5632}
5633
5634fn count_compact_code_blocks(
5635 resolution_packets: &[PreparedCompactResolutionPacket<'_>],
5636) -> Result<u32, &'static str> {
5637 let count = resolution_packets
5638 .iter()
5639 .flat_map(|resolution| resolution.subbands.iter())
5640 .try_fold(0usize, |acc, subband| {
5641 acc.checked_add(subband.code_blocks.len())
5642 .ok_or("packetization code-block count overflow")
5643 })?;
5644 u32::try_from(count).map_err(|_| "packetization code-block count exceeds u32")
5645}
5646
5647fn split_component_resolution_packets_by_precinct(
5648 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
5649 width: u32,
5650 height: u32,
5651 num_decomposition_levels: u8,
5652 precinct_exponents: &[(u8, u8)],
5653) -> Result<Vec<Vec<PreparedResolutionPacket>>, &'static str> {
5654 if precinct_exponents.is_empty() {
5655 return Ok(component_resolution_packets);
5656 }
5657
5658 component_resolution_packets
5659 .into_iter()
5660 .map(|component_packets| {
5661 let mut split_packets = Vec::new();
5662 for packet in component_packets {
5663 split_packets.extend(split_prepared_resolution_packet_by_precinct(
5664 packet,
5665 width,
5666 height,
5667 num_decomposition_levels,
5668 precinct_exponents,
5669 )?);
5670 }
5671 Ok(split_packets)
5672 })
5673 .collect()
5674}
5675
5676fn split_prepared_resolution_packet_by_precinct(
5677 packet: PreparedResolutionPacket,
5678 width: u32,
5679 height: u32,
5680 num_decomposition_levels: u8,
5681 precinct_exponents: &[(u8, u8)],
5682) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
5683 let resolution =
5684 usize::try_from(packet.resolution).map_err(|_| "resolution index exceeds usize")?;
5685 let &(ppx, ppy) = precinct_exponents
5686 .get(resolution)
5687 .ok_or("missing precinct exponents for resolution")?;
5688 let (precincts_x, precincts_y) = resolution_precinct_grid(
5689 width,
5690 height,
5691 num_decomposition_levels,
5692 packet.resolution,
5693 ppx,
5694 ppy,
5695 )?;
5696 let packet_count = (precincts_x as usize)
5697 .checked_mul(precincts_y as usize)
5698 .ok_or("precinct packet count overflow")?;
5699 let component = packet.component;
5700 let resolution = packet.resolution;
5701 let subbands = packet.subbands;
5702 let mut packets = Vec::with_capacity(packet_count);
5703
5704 for precinct_y in 0..precincts_y {
5705 for precinct_x in 0..precincts_x {
5706 let precinct = u64::from(precinct_y)
5707 .checked_mul(u64::from(precincts_x))
5708 .and_then(|value| value.checked_add(u64::from(precinct_x)))
5709 .ok_or("precinct index overflow")?;
5710 let split_subbands = subbands
5711 .iter()
5712 .map(|subband| {
5713 split_prepared_subband_by_precinct(
5714 subband, resolution, ppx, ppy, precinct_x, precinct_y,
5715 )
5716 })
5717 .collect::<Result<Vec<_>, &'static str>>()?;
5718 packets.push(PreparedResolutionPacket {
5719 component,
5720 resolution,
5721 precinct,
5722 subbands: split_subbands,
5723 });
5724 }
5725 }
5726
5727 Ok(packets)
5728}
5729
5730fn resolution_precinct_grid(
5731 width: u32,
5732 height: u32,
5733 num_decomposition_levels: u8,
5734 resolution: u32,
5735 ppx: u8,
5736 ppy: u8,
5737) -> Result<(u32, u32), &'static str> {
5738 let resolution_shift = u32::from(num_decomposition_levels)
5739 .checked_sub(resolution)
5740 .ok_or("resolution exceeds decomposition level count")?;
5741 let resolution_scale = pow2_u32(resolution_shift)?;
5742 let resolution_width = width.div_ceil(resolution_scale);
5743 let resolution_height = height.div_ceil(resolution_scale);
5744 let precinct_width = pow2_u32(u32::from(ppx))?;
5745 let precinct_height = pow2_u32(u32::from(ppy))?;
5746
5747 Ok((
5748 if resolution_width == 0 {
5749 0
5750 } else {
5751 resolution_width.div_ceil(precinct_width)
5752 },
5753 if resolution_height == 0 {
5754 0
5755 } else {
5756 resolution_height.div_ceil(precinct_height)
5757 },
5758 ))
5759}
5760
5761fn split_prepared_subband_by_precinct(
5762 subband: &PreparedEncodeSubband,
5763 resolution: u32,
5764 ppx: u8,
5765 ppy: u8,
5766 precinct_x: u32,
5767 precinct_y: u32,
5768) -> Result<PreparedEncodeSubband, &'static str> {
5769 if subband.code_blocks.is_empty() || subband.width == 0 || subband.height == 0 {
5770 return Ok(empty_prepared_subband_precinct(subband));
5771 }
5772
5773 let subband_ppx = if resolution > 0 {
5774 ppx.checked_sub(1)
5775 .ok_or("nonzero resolution precinct exponent underflow")?
5776 } else {
5777 ppx
5778 };
5779 let subband_ppy = if resolution > 0 {
5780 ppy.checked_sub(1)
5781 .ok_or("nonzero resolution precinct exponent underflow")?
5782 } else {
5783 ppy
5784 };
5785 let precinct_width = pow2_u32(u32::from(subband_ppx))?;
5786 let precinct_height = pow2_u32(u32::from(subband_ppy))?;
5787 let precinct_x0 = precinct_x
5788 .checked_mul(precinct_width)
5789 .ok_or("precinct x coordinate overflow")?;
5790 let precinct_y0 = precinct_y
5791 .checked_mul(precinct_height)
5792 .ok_or("precinct y coordinate overflow")?;
5793 let x0 = precinct_x0.min(subband.width);
5794 let y0 = precinct_y0.min(subband.height);
5795 let x1 = precinct_x0
5796 .checked_add(precinct_width)
5797 .ok_or("precinct x extent overflow")?
5798 .min(subband.width);
5799 let y1 = precinct_y0
5800 .checked_add(precinct_height)
5801 .ok_or("precinct y extent overflow")?
5802 .min(subband.height);
5803
5804 if x0 >= x1 || y0 >= y1 {
5805 return Ok(empty_prepared_subband_precinct(subband));
5806 }
5807
5808 let cb_width = subband.code_block_width;
5809 let cb_height = subband.code_block_height;
5810 if cb_width == 0 || cb_height == 0 {
5811 return Ok(empty_prepared_subband_precinct(subband));
5812 }
5813
5814 let cb_x0 = (x0 / cb_width) * cb_width;
5815 let cb_y0 = (y0 / cb_height) * cb_height;
5816 let cb_x1 = x1.div_ceil(cb_width) * cb_width;
5817 let cb_y1 = y1.div_ceil(cb_height) * cb_height;
5818 let cbx_start = cb_x0 / cb_width;
5819 let cby_start = cb_y0 / cb_height;
5820 let cbx_end = cb_x1 / cb_width;
5821 let cby_end = cb_y1 / cb_height;
5822 let num_cbs_x = cbx_end.saturating_sub(cbx_start);
5823 let num_cbs_y = cby_end.saturating_sub(cby_start);
5824 let mut indices = Vec::with_capacity((num_cbs_x as usize).saturating_mul(num_cbs_y as usize));
5825
5826 for cby in cby_start..cby_end {
5827 for cbx in cbx_start..cbx_end {
5828 let index = cby
5829 .checked_mul(subband.num_cbs_x)
5830 .and_then(|value| value.checked_add(cbx))
5831 .ok_or("precinct code-block index overflow")?;
5832 indices.push(usize::try_from(index).map_err(|_| "code-block index exceeds usize")?);
5833 }
5834 }
5835
5836 let code_blocks = indices
5837 .iter()
5838 .map(|&idx| {
5839 subband
5840 .code_blocks
5841 .get(idx)
5842 .cloned()
5843 .ok_or("precinct code-block index out of range")
5844 })
5845 .collect::<Result<Vec<_>, &'static str>>()?;
5846 let preencoded_ht_code_blocks = subband
5847 .preencoded_ht_code_blocks
5848 .as_ref()
5849 .map(|blocks| {
5850 indices
5851 .iter()
5852 .map(|&idx| {
5853 blocks
5854 .get(idx)
5855 .cloned()
5856 .ok_or("precinct preencoded code-block index out of range")
5857 })
5858 .collect::<Result<Vec<_>, &'static str>>()
5859 })
5860 .transpose()?;
5861
5862 Ok(PreparedEncodeSubband {
5863 code_blocks,
5864 preencoded_ht_code_blocks,
5865 num_cbs_x,
5866 num_cbs_y,
5867 code_block_width: cb_width,
5868 code_block_height: cb_height,
5869 width: x1 - x0,
5870 height: y1 - y0,
5871 sub_band_type: subband.sub_band_type,
5872 total_bitplanes: subband.total_bitplanes,
5873 block_coding_mode: subband.block_coding_mode,
5874 ht_target_coding_passes: subband.ht_target_coding_passes,
5875 })
5876}
5877
5878fn empty_prepared_subband_precinct(subband: &PreparedEncodeSubband) -> PreparedEncodeSubband {
5879 PreparedEncodeSubband {
5880 code_blocks: Vec::new(),
5881 preencoded_ht_code_blocks: subband
5882 .preencoded_ht_code_blocks
5883 .as_ref()
5884 .map(|_| Vec::new()),
5885 num_cbs_x: 0,
5886 num_cbs_y: 0,
5887 code_block_width: subband.code_block_width,
5888 code_block_height: subband.code_block_height,
5889 width: 0,
5890 height: 0,
5891 sub_band_type: subband.sub_band_type,
5892 total_bitplanes: subband.total_bitplanes,
5893 block_coding_mode: subband.block_coding_mode,
5894 ht_target_coding_passes: subband.ht_target_coding_passes,
5895 }
5896}
5897
5898fn pow2_u32(exponent: u32) -> Result<u32, &'static str> {
5899 1_u32
5900 .checked_shl(exponent)
5901 .ok_or("precinct exponent exceeds u32 shift width")
5902}
5903
5904fn packet_descriptors_for_order(
5905 packets: &[PreparedResolutionPacket],
5906 num_layers: u8,
5907 progression_order: EncodeProgressionOrder,
5908) -> Result<Vec<J2kPacketizationPacketDescriptor>, &'static str> {
5909 if num_layers != 1 {
5910 return Err("encode currently prepares one packet contribution layer");
5911 }
5912 let mut descriptors = packets
5913 .iter()
5914 .enumerate()
5915 .map(|(packet_index, packet)| {
5916 Ok(J2kPacketizationPacketDescriptor {
5917 packet_index: u32::try_from(packet_index)
5918 .map_err(|_| "packet descriptor index exceeds u32")?,
5919 state_index: u32::try_from(packet_index)
5920 .map_err(|_| "packet descriptor state index exceeds u32")?,
5921 layer: 0,
5922 resolution: packet.resolution,
5923 component: packet.component,
5924 precinct: packet.precinct,
5925 })
5926 })
5927 .collect::<Result<Vec<_>, &'static str>>()?;
5928 sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
5929 Ok(descriptors)
5930}
5931
5932fn packet_descriptors_for_compact_order(
5933 packets: &[PreparedCompactResolutionPacket<'_>],
5934 num_layers: u8,
5935 progression_order: EncodeProgressionOrder,
5936) -> Result<Vec<J2kPacketizationPacketDescriptor>, &'static str> {
5937 if num_layers != 1 {
5938 return Err("encode currently prepares one packet contribution layer");
5939 }
5940 let mut descriptors = packets
5941 .iter()
5942 .enumerate()
5943 .map(|(packet_index, packet)| {
5944 Ok(J2kPacketizationPacketDescriptor {
5945 packet_index: u32::try_from(packet_index)
5946 .map_err(|_| "packet descriptor index exceeds u32")?,
5947 state_index: u32::try_from(packet_index)
5948 .map_err(|_| "packet descriptor state index exceeds u32")?,
5949 layer: 0,
5950 resolution: packet.resolution,
5951 component: packet.component,
5952 precinct: packet.precinct,
5953 })
5954 })
5955 .collect::<Result<Vec<_>, &'static str>>()?;
5956 sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
5957 Ok(descriptors)
5958}
5959
5960fn ordered_prepared_resolution_packets(
5961 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
5962 options: &EncodeOptions,
5963) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
5964 match options.progression_order {
5965 EncodeProgressionOrder::Lrcp
5966 | EncodeProgressionOrder::Rlcp
5967 | EncodeProgressionOrder::Rpcl => {
5968 lrcp_ordered_prepared_resolution_packets(component_resolution_packets)
5969 }
5970 EncodeProgressionOrder::Pcrl | EncodeProgressionOrder::Cprl => {
5971 component_ordered_prepared_resolution_packets(component_resolution_packets)
5972 }
5973 }
5974}
5975
5976fn ordered_prepared_compact_resolution_packets<'a>(
5977 component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
5978 options: &EncodeOptions,
5979) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
5980 match options.progression_order {
5981 EncodeProgressionOrder::Lrcp
5982 | EncodeProgressionOrder::Rlcp
5983 | EncodeProgressionOrder::Rpcl => {
5984 lrcp_ordered_prepared_compact_resolution_packets(component_resolution_packets)
5985 }
5986 EncodeProgressionOrder::Pcrl | EncodeProgressionOrder::Cprl => {
5987 component_ordered_prepared_compact_resolution_packets(component_resolution_packets)
5988 }
5989 }
5990}
5991
5992fn lrcp_ordered_prepared_resolution_packets(
5993 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
5994) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
5995 let resolution_count = component_resolution_packets
5996 .first()
5997 .map_or(0usize, alloc::vec::Vec::len);
5998 let mut component_iters: Vec<_> = component_resolution_packets
5999 .into_iter()
6000 .map(alloc::vec::Vec::into_iter)
6001 .collect();
6002 let mut resolution_packets =
6003 Vec::with_capacity(resolution_count.saturating_mul(component_iters.len()));
6004
6005 for _resolution in 0..resolution_count {
6006 for component in &mut component_iters {
6007 resolution_packets.push(
6008 component
6009 .next()
6010 .ok_or("component packet resolution count mismatch")?,
6011 );
6012 }
6013 }
6014
6015 if component_iters
6016 .iter_mut()
6017 .any(|component| component.next().is_some())
6018 {
6019 return Err("component packet resolution count mismatch");
6020 }
6021
6022 Ok(resolution_packets)
6023}
6024
6025fn lrcp_ordered_prepared_compact_resolution_packets<'a>(
6026 component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
6027) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
6028 let resolution_count = component_resolution_packets
6029 .first()
6030 .map_or(0usize, alloc::vec::Vec::len);
6031 let mut component_iters: Vec<_> = component_resolution_packets
6032 .into_iter()
6033 .map(alloc::vec::Vec::into_iter)
6034 .collect();
6035 let mut resolution_packets =
6036 Vec::with_capacity(resolution_count.saturating_mul(component_iters.len()));
6037
6038 for _resolution in 0..resolution_count {
6039 for component in &mut component_iters {
6040 resolution_packets.push(
6041 component
6042 .next()
6043 .ok_or("component packet resolution count mismatch")?,
6044 );
6045 }
6046 }
6047
6048 if component_iters
6049 .iter_mut()
6050 .any(|component| component.next().is_some())
6051 {
6052 return Err("component packet resolution count mismatch");
6053 }
6054
6055 Ok(resolution_packets)
6056}
6057
6058fn component_ordered_prepared_resolution_packets(
6059 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
6060) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
6061 let resolution_count = component_resolution_packets
6062 .first()
6063 .map_or(0usize, alloc::vec::Vec::len);
6064 let mut resolution_packets =
6065 Vec::with_capacity(resolution_count.saturating_mul(component_resolution_packets.len()));
6066
6067 for component in component_resolution_packets {
6068 if component.len() != resolution_count {
6069 return Err("component packet resolution count mismatch");
6070 }
6071 resolution_packets.extend(component);
6072 }
6073
6074 Ok(resolution_packets)
6075}
6076
6077fn component_ordered_prepared_compact_resolution_packets<'a>(
6078 component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
6079) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
6080 let resolution_count = component_resolution_packets
6081 .first()
6082 .map_or(0usize, alloc::vec::Vec::len);
6083 let mut resolution_packets =
6084 Vec::with_capacity(resolution_count.saturating_mul(component_resolution_packets.len()));
6085
6086 for component in component_resolution_packets {
6087 if component.len() != resolution_count {
6088 return Err("component packet resolution count mismatch");
6089 }
6090 resolution_packets.extend(component);
6091 }
6092
6093 Ok(resolution_packets)
6094}
6095
6096fn public_packetization_progression_order(
6097 progression_order: EncodeProgressionOrder,
6098) -> crate::J2kPacketizationProgressionOrder {
6099 match progression_order {
6100 EncodeProgressionOrder::Lrcp => crate::J2kPacketizationProgressionOrder::Lrcp,
6101 EncodeProgressionOrder::Rlcp => crate::J2kPacketizationProgressionOrder::Rlcp,
6102 EncodeProgressionOrder::Rpcl => crate::J2kPacketizationProgressionOrder::Rpcl,
6103 EncodeProgressionOrder::Pcrl => crate::J2kPacketizationProgressionOrder::Pcrl,
6104 EncodeProgressionOrder::Cprl => crate::J2kPacketizationProgressionOrder::Cprl,
6105 }
6106}
6107
6108fn scalar_packet_descriptors(
6109 descriptors: &[J2kPacketizationPacketDescriptor],
6110) -> Vec<packet_encode::PacketDescriptor> {
6111 descriptors
6112 .iter()
6113 .map(|descriptor| packet_encode::PacketDescriptor {
6114 packet_index: descriptor.packet_index,
6115 state_index: descriptor.state_index,
6116 layer: descriptor.layer,
6117 resolution: descriptor.resolution,
6118 component: descriptor.component,
6119 precinct: descriptor.precinct,
6120 })
6121 .collect()
6122}
6123
6124fn public_packetization_resolutions(
6125 resolution_packets: &[ResolutionPacket],
6126) -> Vec<J2kPacketizationResolution<'_>> {
6127 resolution_packets
6128 .iter()
6129 .map(|resolution| J2kPacketizationResolution {
6130 subbands: resolution
6131 .subbands
6132 .iter()
6133 .map(|subband| J2kPacketizationSubband {
6134 code_blocks: subband
6135 .code_blocks
6136 .iter()
6137 .map(|code_block| J2kPacketizationCodeBlock {
6138 data: &code_block.data,
6139 ht_cleanup_length: code_block.ht_cleanup_length,
6140 ht_refinement_length: code_block.ht_refinement_length,
6141 num_coding_passes: code_block.num_coding_passes,
6142 num_zero_bitplanes: code_block.num_zero_bitplanes,
6143 previously_included: code_block.previously_included,
6144 l_block: code_block.l_block,
6145 block_coding_mode: public_packetization_block_coding_mode(
6146 code_block.block_coding_mode,
6147 ),
6148 })
6149 .collect(),
6150 num_cbs_x: subband.num_cbs_x,
6151 num_cbs_y: subband.num_cbs_y,
6152 })
6153 .collect(),
6154 })
6155 .collect()
6156}
6157
6158fn packetize_resolution_packets_with_options(
6159 resolution_packets: &mut [ResolutionPacket],
6160 packet_descriptors: &[J2kPacketizationPacketDescriptor],
6161 num_layers: u8,
6162 num_components: u16,
6163 progression_order: EncodeProgressionOrder,
6164 marker_options: packet_encode::PacketMarkerOptions,
6165 allow_packetization_accelerator: bool,
6166 force_scalar_packetization: bool,
6167 accelerator: &mut impl J2kEncodeStageAccelerator,
6168) -> Result<packet_encode::PacketizedTileData, &'static str> {
6169 let packetization_resolutions = public_packetization_resolutions(resolution_packets);
6170 let packetization_job = J2kPacketizationEncodeJob {
6171 resolution_count: resolution_packets.len() as u32,
6172 num_layers,
6173 num_components,
6174 code_block_count: count_code_blocks(resolution_packets)?,
6175 progression_order: public_packetization_progression_order(progression_order),
6176 packet_descriptors,
6177 resolutions: &packetization_resolutions,
6178 };
6179 if allow_packetization_accelerator && !force_scalar_packetization {
6180 if let Some(data) = accelerator.encode_packetization(packetization_job)? {
6181 return Ok(packet_encode::PacketizedTileData {
6182 data,
6183 packet_lengths: Vec::new(),
6184 packet_headers: Vec::new(),
6185 });
6186 }
6187 }
6188
6189 let scalar_packet_descriptors = scalar_packet_descriptors(packet_descriptors);
6190 packet_encode::form_tile_bitstream_with_descriptors_lengths_and_markers(
6191 resolution_packets,
6192 &scalar_packet_descriptors,
6193 marker_options,
6194 )
6195}
6196
6197fn packetization_requires_scalar(
6198 params: &EncodeParams,
6199 tile_part_packet_limit: Option<u16>,
6200) -> bool {
6201 params.write_plt
6202 || params.write_plm
6203 || params.write_ppm
6204 || params.write_ppt
6205 || params.write_sop
6206 || params.write_eph
6207 || tile_part_packet_limit.is_some()
6208}
6209
6210fn public_packetization_resolutions_from_compact<'a>(
6211 resolution_packets: &'a [PreparedCompactResolutionPacket<'a>],
6212) -> Vec<J2kPacketizationResolution<'a>> {
6213 resolution_packets
6214 .iter()
6215 .map(|resolution| J2kPacketizationResolution {
6216 subbands: resolution
6217 .subbands
6218 .iter()
6219 .map(|subband| J2kPacketizationSubband {
6220 code_blocks: subband
6221 .code_blocks
6222 .iter()
6223 .map(|code_block| J2kPacketizationCodeBlock {
6224 data: code_block.data,
6225 ht_cleanup_length: code_block.cleanup_length,
6226 ht_refinement_length: code_block.refinement_length,
6227 num_coding_passes: code_block.num_coding_passes,
6228 num_zero_bitplanes: code_block.num_zero_bitplanes,
6229 previously_included: false,
6230 l_block: 3,
6231 block_coding_mode: J2kPacketizationBlockCodingMode::HighThroughput,
6232 })
6233 .collect(),
6234 num_cbs_x: subband.num_cbs_x,
6235 num_cbs_y: subband.num_cbs_y,
6236 })
6237 .collect(),
6238 })
6239 .collect()
6240}
6241
6242fn public_packetization_block_coding_mode(
6243 block_coding_mode: BlockCodingMode,
6244) -> J2kPacketizationBlockCodingMode {
6245 match block_coding_mode {
6246 BlockCodingMode::Classic => J2kPacketizationBlockCodingMode::Classic,
6247 BlockCodingMode::HighThroughput => J2kPacketizationBlockCodingMode::HighThroughput,
6248 }
6249}
6250
6251#[derive(Clone)]
6252struct PreparedEncodeCodeBlock {
6253 coefficients: Vec<i64>,
6254 width: u32,
6255 height: u32,
6256}
6257
6258#[derive(Clone)]
6259struct PreparedEncodeSubband {
6260 code_blocks: Vec<PreparedEncodeCodeBlock>,
6261 preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
6262 num_cbs_x: u32,
6263 num_cbs_y: u32,
6264 code_block_width: u32,
6265 code_block_height: u32,
6266 width: u32,
6267 height: u32,
6268 sub_band_type: SubBandType,
6269 total_bitplanes: u8,
6270 block_coding_mode: BlockCodingMode,
6271 ht_target_coding_passes: u8,
6272}
6273
6274struct PreparedResolutionPacket {
6275 component: u16,
6276 resolution: u32,
6277 precinct: u64,
6278 subbands: Vec<PreparedEncodeSubband>,
6279}
6280
6281struct PreparedCompactCodeBlock<'a> {
6282 data: &'a [u8],
6283 cleanup_length: u32,
6284 refinement_length: u32,
6285 num_coding_passes: u8,
6286 num_zero_bitplanes: u8,
6287}
6288
6289struct PreparedCompactSubband<'a> {
6290 code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
6291 num_cbs_x: u32,
6292 num_cbs_y: u32,
6293}
6294
6295struct PreparedCompactResolutionPacket<'a> {
6296 component: u16,
6297 resolution: u32,
6298 precinct: u64,
6299 subbands: Vec<PreparedCompactSubband<'a>>,
6300}
6301
6302struct PreparedPrecomputedHtj2k97Image {
6303 params: EncodeParams,
6304 quant_params: Vec<(u16, u16)>,
6305 packet_descriptors: Vec<J2kPacketizationPacketDescriptor>,
6306 packet_count: usize,
6307 prepared_packets: Vec<PreparedResolutionPacket>,
6308}
6309
6310fn prepare_precomputed_htj2k97_image_for_batch(
6311 image: &PrecomputedHtj2k97Image,
6312 options: &EncodeOptions,
6313) -> Result<PreparedPrecomputedHtj2k97Image, &'static str> {
6314 if image.width == 0 || image.height == 0 {
6315 return Err("invalid dimensions");
6316 }
6317 if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
6318 {
6319 return Err("unsupported component count");
6320 }
6321 if image.bit_depth == 0 || image.bit_depth > 16 {
6322 return Err("unsupported bit depth");
6323 }
6324 validate_irreversible_quantization_profile(options)?;
6325 if image
6326 .components
6327 .iter()
6328 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
6329 {
6330 return Err("component sampling factors must be non-zero");
6331 }
6332 validate_precomputed_dwt97_geometry(image)?;
6333
6334 let num_components =
6335 u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
6336 let num_levels = precomputed_97_level_count(&image.components)?;
6337 let guard_bits = options.guard_bits.max(2);
6338 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
6339 image.bit_depth,
6340 num_levels,
6341 false,
6342 guard_bits,
6343 options.irreversible_quantization_scale,
6344 options.irreversible_quantization_subband_scales,
6345 );
6346 let quant_params: Vec<(u16, u16)> = step_sizes
6347 .iter()
6348 .map(|s| (s.exponent, s.mantissa))
6349 .collect();
6350 let cb_width = 1u32 << (options.code_block_width_exp + 2);
6351 let cb_height = 1u32 << (options.code_block_height_exp + 2);
6352 let component_sampling = image
6353 .components
6354 .iter()
6355 .map(|component| (component.x_rsiz, component.y_rsiz))
6356 .collect::<Vec<_>>();
6357 let mut precomputed_options = options.clone();
6358 precomputed_options.num_decomposition_levels = num_levels;
6359 precomputed_options.reversible = false;
6360 precomputed_options.use_ht_block_coding = true;
6361 precomputed_options.use_mct = false;
6362 precomputed_options.validate_high_throughput_codestream = false;
6363 precomputed_options.component_sampling = Some(component_sampling.clone());
6364 let precinct_exponents = precinct_exponents_for_options(&precomputed_options, num_levels)?;
6365 let params = EncodeParams {
6366 width: image.width,
6367 height: image.height,
6368 tile_width: image.width,
6369 tile_height: image.height,
6370 num_components,
6371 bit_depth: image.bit_depth,
6372 signed: image.signed,
6373 component_sample_info: Vec::new(),
6374 component_quantization_step_sizes: Vec::new(),
6375 num_decomposition_levels: num_levels,
6376 reversible: false,
6377 code_block_width_exp: precomputed_options.code_block_width_exp,
6378 code_block_height_exp: precomputed_options.code_block_height_exp,
6379 num_layers: 1,
6380 use_mct: false,
6381 guard_bits,
6382 block_coding_mode: BlockCodingMode::HighThroughput,
6383 progression_order: precomputed_options.progression_order,
6384 write_tlm: precomputed_options.write_tlm,
6385 write_plt: precomputed_options.write_plt,
6386 write_plm: precomputed_options.write_plm,
6387 write_ppm: precomputed_options.write_ppm,
6388 write_ppt: precomputed_options.write_ppt,
6389 write_sop: precomputed_options.write_sop,
6390 write_eph: precomputed_options.write_eph,
6391 terminate_coding_passes: false,
6392 component_sampling,
6393 roi_component_shifts: vec![0; usize::from(num_components)],
6394 precinct_exponents,
6395 };
6396
6397 let component_resolution_packets = image
6398 .components
6399 .iter()
6400 .enumerate()
6401 .map(|(component_idx, component)| {
6402 prepared_resolution_packets_from_precomputed_97_component(
6403 component_idx,
6404 component,
6405 &step_sizes,
6406 image.bit_depth,
6407 guard_bits,
6408 cb_width,
6409 cb_height,
6410 )
6411 })
6412 .collect::<Result<Vec<_>, _>>()?;
6413 let component_resolution_packets = split_component_resolution_packets_by_precinct(
6414 component_resolution_packets,
6415 image.width,
6416 image.height,
6417 num_levels,
6418 ¶ms.precinct_exponents,
6419 )?;
6420 let prepared_packets =
6421 ordered_prepared_resolution_packets(component_resolution_packets, &precomputed_options)?;
6422 let packet_descriptors =
6423 packet_descriptors_for_order(&prepared_packets, 1, precomputed_options.progression_order)?;
6424
6425 Ok(PreparedPrecomputedHtj2k97Image {
6426 params,
6427 quant_params,
6428 packet_descriptors,
6429 packet_count: 0,
6430 prepared_packets,
6431 })
6432}
6433
6434fn prepared_resolution_packets_from_precomputed_97_component(
6435 component_idx: usize,
6436 component: &PrecomputedHtj2k97Component,
6437 step_sizes: &[QuantStepSize],
6438 bit_depth: u8,
6439 guard_bits: u8,
6440 cb_width: u32,
6441 cb_height: u32,
6442) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
6443 let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
6444 let mut packets = Vec::with_capacity(component.dwt.levels.len() + 1);
6445 packets.push(PreparedResolutionPacket {
6446 component: component_idx,
6447 resolution: 0,
6448 precinct: 0,
6449 subbands: vec![prepare_subband_cpu_quantized(
6450 &component.dwt.ll,
6451 component.dwt.ll_width,
6452 component.dwt.ll_height,
6453 step_sizes
6454 .first()
6455 .ok_or("irreversible quantization step missing")?,
6456 bit_depth,
6457 guard_bits,
6458 false,
6459 BlockCodingMode::HighThroughput,
6460 cb_width,
6461 cb_height,
6462 SubBandType::LowLow,
6463 )?],
6464 });
6465
6466 for (level_idx, level) in component.dwt.levels.iter().enumerate() {
6467 let step_base = 1 + level_idx * 3;
6468 packets.push(PreparedResolutionPacket {
6469 component: component_idx,
6470 resolution: u32::try_from(level_idx + 1).map_err(|_| "resolution index exceeds u32")?,
6471 precinct: 0,
6472 subbands: vec![
6473 prepare_subband_cpu_quantized(
6474 &level.hl,
6475 level.high_width,
6476 level.low_height,
6477 step_sizes
6478 .get(step_base)
6479 .ok_or("irreversible quantization step missing")?,
6480 bit_depth,
6481 guard_bits,
6482 false,
6483 BlockCodingMode::HighThroughput,
6484 cb_width,
6485 cb_height,
6486 SubBandType::HighLow,
6487 )?,
6488 prepare_subband_cpu_quantized(
6489 &level.lh,
6490 level.low_width,
6491 level.high_height,
6492 step_sizes
6493 .get(step_base + 1)
6494 .ok_or("irreversible quantization step missing")?,
6495 bit_depth,
6496 guard_bits,
6497 false,
6498 BlockCodingMode::HighThroughput,
6499 cb_width,
6500 cb_height,
6501 SubBandType::LowHigh,
6502 )?,
6503 prepare_subband_cpu_quantized(
6504 &level.hh,
6505 level.high_width,
6506 level.high_height,
6507 step_sizes
6508 .get(step_base + 2)
6509 .ok_or("irreversible quantization step missing")?,
6510 bit_depth,
6511 guard_bits,
6512 false,
6513 BlockCodingMode::HighThroughput,
6514 cb_width,
6515 cb_height,
6516 SubBandType::HighHigh,
6517 )?,
6518 ],
6519 });
6520 }
6521
6522 Ok(packets)
6523}
6524
6525fn copy_code_block_coefficients(
6526 quantized: &[i32],
6527 width: usize,
6528 x0: usize,
6529 y0: usize,
6530 cbw: usize,
6531 cbh: usize,
6532) -> Vec<i32> {
6533 let len = cbw * cbh;
6534 let start = y0 * width + x0;
6535 if cbw == width {
6536 return quantized[start..start + len].to_vec();
6537 }
6538
6539 let mut coefficients = Vec::with_capacity(len);
6540 for y in 0..cbh {
6541 let row_start = (y0 + y) * width + x0;
6542 coefficients.extend_from_slice(&quantized[row_start..row_start + cbw]);
6543 }
6544 coefficients
6545}
6546
6547fn copy_code_block_coefficients_i64(
6548 quantized: &[i64],
6549 width: usize,
6550 x0: usize,
6551 y0: usize,
6552 cbw: usize,
6553 cbh: usize,
6554) -> Vec<i64> {
6555 let len = cbw * cbh;
6556 let start = y0 * width + x0;
6557 if cbw == width {
6558 return quantized[start..start + len].to_vec();
6559 }
6560
6561 let mut coefficients = Vec::with_capacity(len);
6562 for y in 0..cbh {
6563 let row_start = (y0 + y) * width + x0;
6564 coefficients.extend_from_slice(&quantized[row_start..row_start + cbw]);
6565 }
6566 coefficients
6567}
6568
6569fn coefficients_fit_i32(coefficients: &[i64]) -> bool {
6570 coefficients
6571 .iter()
6572 .all(|&coefficient| i32::try_from(coefficient).is_ok())
6573}
6574
6575fn downcast_i64_coefficients_to_i32(coefficients: &[i64]) -> Result<Vec<i32>, &'static str> {
6576 coefficients
6577 .iter()
6578 .map(|&coefficient| {
6579 i32::try_from(coefficient).map_err(|_| {
6580 "HTJ2K/accelerated code-block encode does not support i64 coefficients"
6581 })
6582 })
6583 .collect()
6584}
6585
6586fn apply_roi_maxshift_encode(
6587 coefficients: &mut [i32],
6588 width: u32,
6589 height: u32,
6590 roi_shift: u8,
6591 roi_regions: &[ComponentRoiEncodeRegion],
6592 roi_scale: u32,
6593) -> Result<(), &'static str> {
6594 if roi_shift == 0 {
6595 return Ok(());
6596 }
6597 let expected_len = (width as usize)
6598 .checked_mul(height as usize)
6599 .ok_or("ROI subband dimensions overflow")?;
6600 if coefficients.len() != expected_len {
6601 return Err("ROI subband coefficient length mismatch");
6602 }
6603 if roi_regions.is_empty() {
6604 for coefficient in coefficients {
6605 shift_roi_coefficient(coefficient, roi_shift)?;
6606 }
6607 return Ok(());
6608 }
6609
6610 let mut selected = vec![false; coefficients.len()];
6611 for region in roi_regions {
6612 let Some((x0, y0, x1, y1)) = roi_region_subband_window(*region, width, height, roi_scale)
6613 else {
6614 continue;
6615 };
6616 for y in y0..y1 {
6617 for x in x0..x1 {
6618 let idx = (y as usize)
6619 .checked_mul(width as usize)
6620 .and_then(|row| row.checked_add(x as usize))
6621 .ok_or("ROI subband index overflow")?;
6622 if selected[idx] {
6623 continue;
6624 }
6625 selected[idx] = true;
6626 shift_roi_coefficient(&mut coefficients[idx], roi_shift)?;
6627 }
6628 }
6629 }
6630 Ok(())
6631}
6632
6633fn apply_roi_maxshift_encode_i64(
6634 coefficients: &mut [i64],
6635 width: u32,
6636 height: u32,
6637 roi_shift: u8,
6638 roi_regions: &[ComponentRoiEncodeRegion],
6639 roi_scale: u32,
6640) -> Result<(), &'static str> {
6641 if roi_shift == 0 {
6642 return Ok(());
6643 }
6644 let expected_len = (width as usize)
6645 .checked_mul(height as usize)
6646 .ok_or("ROI subband dimensions overflow")?;
6647 if coefficients.len() != expected_len {
6648 return Err("ROI subband coefficient length mismatch");
6649 }
6650 if roi_regions.is_empty() {
6651 for coefficient in coefficients {
6652 shift_roi_coefficient_i64(coefficient, roi_shift)?;
6653 }
6654 return Ok(());
6655 }
6656
6657 let mut selected = vec![false; coefficients.len()];
6658 for region in roi_regions {
6659 let Some((x0, y0, x1, y1)) = roi_region_subband_window(*region, width, height, roi_scale)
6660 else {
6661 continue;
6662 };
6663 for y in y0..y1 {
6664 for x in x0..x1 {
6665 let idx = (y as usize)
6666 .checked_mul(width as usize)
6667 .and_then(|row| row.checked_add(x as usize))
6668 .ok_or("ROI subband index overflow")?;
6669 if selected[idx] {
6670 continue;
6671 }
6672 selected[idx] = true;
6673 shift_roi_coefficient_i64(&mut coefficients[idx], roi_shift)?;
6674 }
6675 }
6676 }
6677 Ok(())
6678}
6679
6680fn shift_roi_coefficient(coefficient: &mut i32, roi_shift: u8) -> Result<(), &'static str> {
6681 *coefficient = coefficient
6682 .checked_shl(u32::from(roi_shift))
6683 .ok_or("ROI maxshift coefficient overflow")?;
6684 Ok(())
6685}
6686
6687fn shift_roi_coefficient_i64(coefficient: &mut i64, roi_shift: u8) -> Result<(), &'static str> {
6688 let factor = 1_i64
6689 .checked_shl(u32::from(roi_shift))
6690 .ok_or("ROI maxshift coefficient overflow")?;
6691 *coefficient = coefficient
6692 .checked_mul(factor)
6693 .ok_or("ROI maxshift coefficient overflow")?;
6694 Ok(())
6695}
6696
6697fn roi_region_subband_window(
6698 region: ComponentRoiEncodeRegion,
6699 width: u32,
6700 height: u32,
6701 roi_scale: u32,
6702) -> Option<(u32, u32, u32, u32)> {
6703 if width == 0 || height == 0 || roi_scale == 0 {
6704 return None;
6705 }
6706 let x1 = region.x.saturating_add(region.width);
6707 let y1 = region.y.saturating_add(region.height);
6708 let x0 = (region.x / roi_scale).min(width);
6709 let y0 = (region.y / roi_scale).min(height);
6710 let x1 = x1.div_ceil(roi_scale).min(width);
6711 let y1 = y1.div_ceil(roi_scale).min(height);
6712 if x0 >= x1 || y0 >= y1 {
6713 None
6714 } else {
6715 Some((x0, y0, x1, y1))
6716 }
6717}
6718
6719fn prepare_subband(
6720 coefficients: &[f32],
6721 width: u32,
6722 height: u32,
6723 step_size: &QuantStepSize,
6724 bit_depth: u8,
6725 guard_bits: u8,
6726 reversible: bool,
6727 block_coding_mode: BlockCodingMode,
6728 cb_width: u32,
6729 cb_height: u32,
6730 sub_band_type: SubBandType,
6731 roi_shift: u8,
6732 roi_regions: &[ComponentRoiEncodeRegion],
6733 roi_scale: u32,
6734 ht_target_coding_passes: u8,
6735 accelerator: &mut impl J2kEncodeStageAccelerator,
6736) -> Result<PreparedEncodeSubband, &'static str> {
6737 if width == 0 || height == 0 {
6738 return Ok(PreparedEncodeSubband {
6739 code_blocks: Vec::new(),
6740 preencoded_ht_code_blocks: None,
6741 num_cbs_x: 0,
6742 num_cbs_y: 0,
6743 code_block_width: cb_width,
6744 code_block_height: cb_height,
6745 width,
6746 height,
6747 sub_band_type,
6748 total_bitplanes: 0,
6749 block_coding_mode,
6750 ht_target_coding_passes,
6751 });
6752 }
6753
6754 let range_bits = subband_range_bits(bit_depth, sub_band_type);
6755 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
6756 let base_total_bitplanes = guard_bits
6757 .saturating_add(step_size.exponent as u8)
6758 .saturating_sub(1);
6759 let total_bitplanes = base_total_bitplanes
6760 .checked_add(roi_shift)
6761 .ok_or("ROI maxshift exceeds supported coded bitplane count")?;
6762 let num_cbs_x = width.div_ceil(cb_width);
6763 let num_cbs_y = height.div_ceil(cb_height);
6764
6765 if block_coding_mode == BlockCodingMode::HighThroughput
6766 && roi_shift == 0
6767 && ht_target_coding_passes == 1
6768 {
6769 if let Some(encoded) = accelerator.encode_ht_subband(J2kHtSubbandEncodeJob {
6770 coefficients,
6771 width,
6772 height,
6773 step_exponent: step_size.exponent,
6774 step_mantissa: step_size.mantissa,
6775 range_bits,
6776 reversible,
6777 code_block_width: cb_width,
6778 code_block_height: cb_height,
6779 total_bitplanes,
6780 })? {
6781 let expected_code_blocks = (num_cbs_x as usize)
6782 .checked_mul(num_cbs_y as usize)
6783 .ok_or("code-block count overflow")?;
6784 if encoded.len() != expected_code_blocks {
6785 return Err("accelerated HT subband code-block count mismatch");
6786 }
6787 return Ok(PreparedEncodeSubband {
6788 code_blocks: code_block_shapes(width, height, cb_width, cb_height)?,
6789 preencoded_ht_code_blocks: Some(encoded),
6790 num_cbs_x,
6791 num_cbs_y,
6792 code_block_width: cb_width,
6793 code_block_height: cb_height,
6794 width,
6795 height,
6796 sub_band_type,
6797 total_bitplanes,
6798 block_coding_mode,
6799 ht_target_coding_passes,
6800 });
6801 }
6802 }
6803
6804 let mut quantized = match accelerator.encode_quantize_subband(J2kQuantizeSubbandJob {
6805 coefficients,
6806 step_exponent: step_size.exponent,
6807 step_mantissa: step_size.mantissa,
6808 range_bits,
6809 reversible,
6810 })? {
6811 Some(quantized) => {
6812 if quantized.len() != coefficients.len() {
6813 return Err("accelerated quantized subband length mismatch");
6814 }
6815 quantized
6816 }
6817 None => quantize::quantize_subband(coefficients, step_size, range_bits, reversible),
6818 };
6819 apply_roi_maxshift_encode(
6820 &mut quantized,
6821 width,
6822 height,
6823 roi_shift,
6824 roi_regions,
6825 roi_scale,
6826 )?;
6827
6828 let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
6830
6831 for cby in 0..num_cbs_y {
6832 for cbx in 0..num_cbs_x {
6833 let x0 = cbx * cb_width;
6834 let y0 = cby * cb_height;
6835 let x1 = (x0 + cb_width).min(width);
6836 let y1 = (y0 + cb_height).min(height);
6837 let cbw = x1 - x0;
6838 let cbh = y1 - y0;
6839
6840 let cb_coeffs = copy_code_block_coefficients(
6841 &quantized,
6842 width as usize,
6843 x0 as usize,
6844 y0 as usize,
6845 cbw as usize,
6846 cbh as usize,
6847 )
6848 .into_iter()
6849 .map(i64::from)
6850 .collect();
6851
6852 code_blocks.push(PreparedEncodeCodeBlock {
6853 coefficients: cb_coeffs,
6854 width: cbw,
6855 height: cbh,
6856 });
6857 }
6858 }
6859
6860 Ok(PreparedEncodeSubband {
6861 code_blocks,
6862 preencoded_ht_code_blocks: None,
6863 num_cbs_x,
6864 num_cbs_y,
6865 code_block_width: cb_width,
6866 code_block_height: cb_height,
6867 width,
6868 height,
6869 sub_band_type,
6870 total_bitplanes,
6871 block_coding_mode,
6872 ht_target_coding_passes,
6873 })
6874}
6875
6876#[allow(clippy::too_many_arguments)]
6877fn prepare_subband_i64(
6878 coefficients: &[i64],
6879 width: u32,
6880 height: u32,
6881 step_size: &QuantStepSize,
6882 guard_bits: u8,
6883 cb_width: u32,
6884 cb_height: u32,
6885 sub_band_type: SubBandType,
6886 roi_shift: u8,
6887 roi_regions: &[ComponentRoiEncodeRegion],
6888 roi_scale: u32,
6889 block_coding_mode: BlockCodingMode,
6890 ht_target_coding_passes: u8,
6891) -> Result<PreparedEncodeSubband, &'static str> {
6892 if width == 0 || height == 0 {
6893 return Ok(PreparedEncodeSubband {
6894 code_blocks: Vec::new(),
6895 preencoded_ht_code_blocks: None,
6896 num_cbs_x: 0,
6897 num_cbs_y: 0,
6898 code_block_width: cb_width,
6899 code_block_height: cb_height,
6900 width,
6901 height,
6902 sub_band_type,
6903 total_bitplanes: 0,
6904 block_coding_mode,
6905 ht_target_coding_passes,
6906 });
6907 }
6908
6909 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
6910 let base_total_bitplanes = guard_bits
6911 .saturating_add(step_size.exponent as u8)
6912 .saturating_sub(1);
6913 let total_bitplanes = base_total_bitplanes
6914 .checked_add(roi_shift)
6915 .ok_or("ROI maxshift exceeds supported coded bitplane count")?;
6916 let num_cbs_x = width.div_ceil(cb_width);
6917 let num_cbs_y = height.div_ceil(cb_height);
6918 let mut quantized = coefficients.to_vec();
6919 apply_roi_maxshift_encode_i64(
6920 &mut quantized,
6921 width,
6922 height,
6923 roi_shift,
6924 roi_regions,
6925 roi_scale,
6926 )?;
6927
6928 let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
6929 for cby in 0..num_cbs_y {
6930 for cbx in 0..num_cbs_x {
6931 let x0 = cbx * cb_width;
6932 let y0 = cby * cb_height;
6933 let x1 = (x0 + cb_width).min(width);
6934 let y1 = (y0 + cb_height).min(height);
6935 let cbw = x1 - x0;
6936 let cbh = y1 - y0;
6937
6938 let cb_coeffs = copy_code_block_coefficients_i64(
6939 &quantized,
6940 width as usize,
6941 x0 as usize,
6942 y0 as usize,
6943 cbw as usize,
6944 cbh as usize,
6945 );
6946
6947 code_blocks.push(PreparedEncodeCodeBlock {
6948 coefficients: cb_coeffs,
6949 width: cbw,
6950 height: cbh,
6951 });
6952 }
6953 }
6954
6955 Ok(PreparedEncodeSubband {
6956 code_blocks,
6957 preencoded_ht_code_blocks: None,
6958 num_cbs_x,
6959 num_cbs_y,
6960 code_block_width: cb_width,
6961 code_block_height: cb_height,
6962 width,
6963 height,
6964 sub_band_type,
6965 total_bitplanes,
6966 block_coding_mode,
6967 ht_target_coding_passes,
6968 })
6969}
6970
6971fn prepare_subband_cpu_quantized(
6972 coefficients: &[f32],
6973 width: u32,
6974 height: u32,
6975 step_size: &QuantStepSize,
6976 bit_depth: u8,
6977 guard_bits: u8,
6978 reversible: bool,
6979 block_coding_mode: BlockCodingMode,
6980 cb_width: u32,
6981 cb_height: u32,
6982 sub_band_type: SubBandType,
6983) -> Result<PreparedEncodeSubband, &'static str> {
6984 if width == 0 || height == 0 {
6985 return Ok(PreparedEncodeSubband {
6986 code_blocks: Vec::new(),
6987 preencoded_ht_code_blocks: None,
6988 num_cbs_x: 0,
6989 num_cbs_y: 0,
6990 code_block_width: cb_width,
6991 code_block_height: cb_height,
6992 width,
6993 height,
6994 sub_band_type,
6995 total_bitplanes: 0,
6996 block_coding_mode,
6997 ht_target_coding_passes: 1,
6998 });
6999 }
7000
7001 let range_bits = subband_range_bits(bit_depth, sub_band_type);
7002 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
7003 let total_bitplanes = guard_bits
7004 .saturating_add(step_size.exponent as u8)
7005 .saturating_sub(1);
7006 let num_cbs_x = width.div_ceil(cb_width);
7007 let num_cbs_y = height.div_ceil(cb_height);
7008 let quantized = quantize::quantize_subband(coefficients, step_size, range_bits, reversible);
7009 let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
7010
7011 for cby in 0..num_cbs_y {
7012 for cbx in 0..num_cbs_x {
7013 let x0 = cbx * cb_width;
7014 let y0 = cby * cb_height;
7015 let x1 = (x0 + cb_width).min(width);
7016 let y1 = (y0 + cb_height).min(height);
7017 let cbw = x1 - x0;
7018 let cbh = y1 - y0;
7019 let cb_coeffs = copy_code_block_coefficients(
7020 &quantized,
7021 width as usize,
7022 x0 as usize,
7023 y0 as usize,
7024 cbw as usize,
7025 cbh as usize,
7026 )
7027 .into_iter()
7028 .map(i64::from)
7029 .collect();
7030
7031 code_blocks.push(PreparedEncodeCodeBlock {
7032 coefficients: cb_coeffs,
7033 width: cbw,
7034 height: cbh,
7035 });
7036 }
7037 }
7038
7039 Ok(PreparedEncodeSubband {
7040 code_blocks,
7041 preencoded_ht_code_blocks: None,
7042 num_cbs_x,
7043 num_cbs_y,
7044 code_block_width: cb_width,
7045 code_block_height: cb_height,
7046 width,
7047 height,
7048 sub_band_type,
7049 total_bitplanes,
7050 block_coding_mode,
7051 ht_target_coding_passes: 1,
7052 })
7053}
7054
7055fn code_block_shapes(
7056 width: u32,
7057 height: u32,
7058 cb_width: u32,
7059 cb_height: u32,
7060) -> Result<Vec<PreparedEncodeCodeBlock>, &'static str> {
7061 let num_cbs_x = width.div_ceil(cb_width);
7062 let num_cbs_y = height.div_ceil(cb_height);
7063 let count = (num_cbs_x as usize)
7064 .checked_mul(num_cbs_y as usize)
7065 .ok_or("code-block count overflow")?;
7066 let mut code_blocks = Vec::with_capacity(count);
7067 for cby in 0..num_cbs_y {
7068 for cbx in 0..num_cbs_x {
7069 let x0 = cbx * cb_width;
7070 let y0 = cby * cb_height;
7071 let x1 = (x0 + cb_width).min(width);
7072 let y1 = (y0 + cb_height).min(height);
7073 code_blocks.push(PreparedEncodeCodeBlock {
7074 coefficients: Vec::new(),
7075 width: x1 - x0,
7076 height: y1 - y0,
7077 });
7078 }
7079 }
7080 Ok(code_blocks)
7081}
7082
7083fn subband_range_bits(bit_depth: u8, sub_band_type: SubBandType) -> u8 {
7084 let log_gain = match sub_band_type {
7085 SubBandType::LowLow => 0,
7086 SubBandType::LowHigh | SubBandType::HighLow => 1,
7087 SubBandType::HighHigh => 2,
7088 };
7089
7090 bit_depth.saturating_add(log_gain)
7091}
7092
7093fn encode_prepared_resolution_packets(
7094 prepared_packets: Vec<PreparedResolutionPacket>,
7095 accelerator: &mut impl J2kEncodeStageAccelerator,
7096) -> Result<Vec<ResolutionPacket>, &'static str> {
7097 let subband_counts: Vec<_> = prepared_packets
7098 .iter()
7099 .map(|packet| packet.subbands.len())
7100 .collect();
7101 let prepared_subbands: Vec<_> = prepared_packets
7102 .into_iter()
7103 .flat_map(|packet| packet.subbands)
7104 .collect();
7105 let mut encoded_subbands =
7106 encode_prepared_subbands(prepared_subbands, accelerator)?.into_iter();
7107
7108 subband_counts
7109 .into_iter()
7110 .map(|subband_count| {
7111 let mut subbands = Vec::with_capacity(subband_count);
7112 for _ in 0..subband_count {
7113 subbands.push(
7114 encoded_subbands
7115 .next()
7116 .ok_or("encoded subband count mismatch")?,
7117 );
7118 }
7119 Ok(ResolutionPacket { subbands })
7120 })
7121 .collect()
7122}
7123
7124fn encode_prepared_resolution_packets_layered(
7125 prepared_packets: Vec<PreparedResolutionPacket>,
7126 num_layers: u8,
7127 progression_order: EncodeProgressionOrder,
7128 quality_layer_byte_targets: &[u64],
7129 accelerator: &mut impl J2kEncodeStageAccelerator,
7130) -> Result<(Vec<ResolutionPacket>, Vec<J2kPacketizationPacketDescriptor>), &'static str> {
7131 let layer_count = usize::from(num_layers);
7132 let mut layered_packets = Vec::with_capacity(prepared_packets.len());
7133 let mut classic_candidates = Vec::new();
7134 let mut classic_locations = Vec::new();
7135 let mut classic_block_index = 0usize;
7136 let mut ht_candidates = Vec::new();
7137 let mut ht_locations = Vec::new();
7138 let mut ht_block_index = 0usize;
7139
7140 for prepared_packet in prepared_packets {
7141 let packet_idx = layered_packets.len();
7142 let mut layered_packet = LayeredPreparedPacket {
7143 component: prepared_packet.component,
7144 resolution: prepared_packet.resolution,
7145 precinct: prepared_packet.precinct,
7146 subbands: Vec::with_capacity(prepared_packet.subbands.len()),
7147 };
7148
7149 for subband in prepared_packet.subbands {
7150 let subband_idx = layered_packet.subbands.len();
7151 let mut layered_subband = LayeredPreparedSubband {
7152 num_cbs_x: subband.num_cbs_x,
7153 num_cbs_y: subband.num_cbs_y,
7154 blocks: Vec::with_capacity(subband.code_blocks.len()),
7155 };
7156
7157 match subband.block_coding_mode {
7158 BlockCodingMode::Classic => {
7159 for block in subband.code_blocks {
7160 let block_idx = layered_subband.blocks.len();
7161 let encoded = bitplane_encode::encode_code_block_segments_with_style_i64(
7162 &block.coefficients,
7163 block.width,
7164 block.height,
7165 subband.sub_band_type,
7166 subband.total_bitplanes,
7167 &classic_multilayer_code_block_style(),
7168 );
7169 let segment_layers = if quality_layer_byte_targets.is_empty() {
7170 classic_unbudgeted_segment_layers(&encoded, num_layers)?
7171 } else {
7172 for (segment_idx, segment) in encoded.segments.iter().enumerate() {
7173 classic_candidates.push(ClassicSegmentAssignmentCandidate {
7174 block_index: classic_block_index,
7175 segment_index: segment_idx,
7176 rate: u64::from(segment.data_length),
7177 distortion_delta: segment.distortion_delta,
7178 });
7179 classic_locations.push(ClassicSegmentLocation {
7180 packet_idx,
7181 subband_idx,
7182 block_idx,
7183 segment_idx,
7184 });
7185 }
7186 vec![layer_count.saturating_sub(1); encoded.segments.len()]
7187 };
7188 layered_subband.blocks.push(LayeredPreparedBlock::Classic {
7189 encoded,
7190 segment_layers,
7191 });
7192 classic_block_index = classic_block_index
7193 .checked_add(1)
7194 .ok_or("classic PCRD block index overflow")?;
7195 }
7196 }
7197 BlockCodingMode::HighThroughput => {
7198 let encoded_blocks =
7199 encode_all_ht_code_blocks(core::slice::from_ref(&subband), accelerator)?;
7200 let block_count = encoded_blocks.len();
7201 for (block_idx, encoded) in encoded_blocks.into_iter().enumerate() {
7202 let segment_layers = if quality_layer_byte_targets.is_empty() {
7203 ht_unbudgeted_segment_layers(
7204 &encoded,
7205 num_layers,
7206 block_idx,
7207 block_count,
7208 )?
7209 } else {
7210 let segment_count = ht_segment_count(&encoded);
7211 let mut segment_layers = Vec::with_capacity(segment_count);
7212 for segment_idx in 0..segment_count {
7213 ht_candidates.push(HtSegmentAssignmentCandidate {
7214 block_index: ht_block_index,
7215 segment_index: segment_idx,
7216 rate: ht_segment_rate(&encoded, segment_idx)?,
7217 });
7218 ht_locations.push(HtSegmentLocation {
7219 packet_idx,
7220 subband_idx,
7221 block_idx: layered_subband.blocks.len(),
7222 segment_idx,
7223 });
7224 segment_layers.push(layer_count.saturating_sub(1));
7225 }
7226 segment_layers
7227 };
7228 layered_subband
7229 .blocks
7230 .push(LayeredPreparedBlock::HighThroughput {
7231 encoded,
7232 segment_layers,
7233 });
7234 ht_block_index = ht_block_index
7235 .checked_add(1)
7236 .ok_or("HTJ2K segment block index overflow")?;
7237 }
7238 }
7239 }
7240
7241 layered_packet.subbands.push(layered_subband);
7242 }
7243
7244 layered_packets.push(layered_packet);
7245 }
7246
7247 if !quality_layer_byte_targets.is_empty() {
7248 let assignments = assign_classic_segment_layers_by_slope(
7249 &classic_candidates,
7250 layer_count,
7251 quality_layer_byte_targets,
7252 )?;
7253 for (assignment_idx, layer) in assignments.into_iter().enumerate() {
7254 let location = classic_locations
7255 .get(assignment_idx)
7256 .ok_or("classic PCRD assignment location mismatch")?;
7257 let block = layered_packets
7258 .get_mut(location.packet_idx)
7259 .ok_or("classic PCRD packet index mismatch")?
7260 .subbands
7261 .get_mut(location.subband_idx)
7262 .ok_or("classic PCRD subband index mismatch")?
7263 .blocks
7264 .get_mut(location.block_idx)
7265 .ok_or("classic PCRD block index mismatch")?;
7266 let LayeredPreparedBlock::Classic { segment_layers, .. } = block else {
7267 return Err("classic PCRD assignment referenced HT block");
7268 };
7269 let segment_layer = segment_layers
7270 .get_mut(location.segment_idx)
7271 .ok_or("classic PCRD segment index mismatch")?;
7272 *segment_layer = layer;
7273 }
7274 enforce_classic_segment_layer_monotonicity(&mut layered_packets);
7275 }
7276 if !quality_layer_byte_targets.is_empty() {
7277 let assignments = assign_ht_segment_layers_by_budget(
7278 &ht_candidates,
7279 layer_count,
7280 quality_layer_byte_targets,
7281 )?;
7282 for (assignment_idx, layer) in assignments.into_iter().enumerate() {
7283 let location = ht_locations
7284 .get(assignment_idx)
7285 .ok_or("HTJ2K segment assignment location mismatch")?;
7286 let block = layered_packets
7287 .get_mut(location.packet_idx)
7288 .ok_or("HTJ2K packet index mismatch")?
7289 .subbands
7290 .get_mut(location.subband_idx)
7291 .ok_or("HTJ2K subband index mismatch")?
7292 .blocks
7293 .get_mut(location.block_idx)
7294 .ok_or("HTJ2K block index mismatch")?;
7295 let LayeredPreparedBlock::HighThroughput { segment_layers, .. } = block else {
7296 return Err("HTJ2K segment assignment referenced classic block");
7297 };
7298 let segment_layer = segment_layers
7299 .get_mut(location.segment_idx)
7300 .ok_or("HTJ2K segment index mismatch")?;
7301 *segment_layer = layer;
7302 }
7303 enforce_ht_segment_layer_monotonicity(&mut layered_packets);
7304 }
7305
7306 let mut resolution_packets = Vec::with_capacity(layered_packets.len() * layer_count);
7307 let mut descriptors = Vec::with_capacity(layered_packets.len() * layer_count);
7308 for (state_index, layered_packet) in layered_packets.into_iter().enumerate() {
7309 let mut layer_packets: Vec<_> = (0..layer_count)
7310 .map(|_| ResolutionPacket {
7311 subbands: Vec::with_capacity(layered_packet.subbands.len()),
7312 })
7313 .collect();
7314
7315 for subband in layered_packet.subbands {
7316 let mut layer_subbands: Vec<_> = (0..layer_count)
7317 .map(|_| SubbandPrecinct {
7318 code_blocks: Vec::with_capacity(subband.blocks.len()),
7319 num_cbs_x: subband.num_cbs_x,
7320 num_cbs_y: subband.num_cbs_y,
7321 })
7322 .collect();
7323
7324 for block in subband.blocks {
7325 let contributions = match block {
7326 LayeredPreparedBlock::Classic {
7327 encoded,
7328 segment_layers,
7329 } => classic_layer_contributions(encoded, num_layers, &segment_layers)?,
7330 LayeredPreparedBlock::HighThroughput {
7331 encoded,
7332 segment_layers,
7333 } => ht_layer_contributions(encoded, num_layers, &segment_layers)?,
7334 };
7335 for (layer_idx, contribution) in contributions.into_iter().enumerate() {
7336 layer_subbands[layer_idx].code_blocks.push(contribution);
7337 }
7338 }
7339
7340 for (layer_packet, layer_subband) in layer_packets.iter_mut().zip(layer_subbands) {
7341 layer_packet.subbands.push(layer_subband);
7342 }
7343 }
7344
7345 let state_index =
7346 u32::try_from(state_index).map_err(|_| "packet descriptor state index exceeds u32")?;
7347 for (layer_idx, layer_packet) in layer_packets.into_iter().enumerate() {
7348 let packet_index = u32::try_from(resolution_packets.len())
7349 .map_err(|_| "packet descriptor index exceeds u32")?;
7350 resolution_packets.push(layer_packet);
7351 descriptors.push(J2kPacketizationPacketDescriptor {
7352 packet_index,
7353 state_index,
7354 layer: u8::try_from(layer_idx).map_err(|_| "quality layer index exceeds u8")?,
7355 resolution: layered_packet.resolution,
7356 component: layered_packet.component,
7357 precinct: layered_packet.precinct,
7358 });
7359 }
7360 }
7361
7362 sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
7363
7364 Ok((resolution_packets, descriptors))
7365}
7366
7367fn sort_packet_descriptors_for_progression(
7368 descriptors: &mut [J2kPacketizationPacketDescriptor],
7369 progression_order: EncodeProgressionOrder,
7370) {
7371 match progression_order {
7372 EncodeProgressionOrder::Lrcp => descriptors.sort_by_key(|descriptor| {
7373 (
7374 descriptor.layer,
7375 descriptor.resolution,
7376 descriptor.component,
7377 descriptor.precinct,
7378 )
7379 }),
7380 EncodeProgressionOrder::Rlcp => descriptors.sort_by_key(|descriptor| {
7381 (
7382 descriptor.resolution,
7383 descriptor.layer,
7384 descriptor.component,
7385 descriptor.precinct,
7386 )
7387 }),
7388 EncodeProgressionOrder::Rpcl => descriptors.sort_by_key(|descriptor| {
7389 (
7390 descriptor.resolution,
7391 descriptor.precinct,
7392 descriptor.component,
7393 descriptor.layer,
7394 )
7395 }),
7396 EncodeProgressionOrder::Pcrl => descriptors.sort_by_key(|descriptor| {
7397 (
7398 descriptor.precinct,
7399 descriptor.component,
7400 descriptor.resolution,
7401 descriptor.layer,
7402 )
7403 }),
7404 EncodeProgressionOrder::Cprl => descriptors.sort_by_key(|descriptor| {
7405 (
7406 descriptor.component,
7407 descriptor.precinct,
7408 descriptor.resolution,
7409 descriptor.layer,
7410 )
7411 }),
7412 }
7413}
7414
7415fn classic_multilayer_code_block_style() -> CodeBlockStyle {
7416 CodeBlockStyle {
7417 termination_on_each_pass: true,
7418 ..CodeBlockStyle::default()
7419 }
7420}
7421
7422struct LayeredPreparedPacket {
7423 component: u16,
7424 resolution: u32,
7425 precinct: u64,
7426 subbands: Vec<LayeredPreparedSubband>,
7427}
7428
7429struct LayeredPreparedSubband {
7430 num_cbs_x: u32,
7431 num_cbs_y: u32,
7432 blocks: Vec<LayeredPreparedBlock>,
7433}
7434
7435enum LayeredPreparedBlock {
7436 Classic {
7437 encoded: bitplane_encode::EncodedCodeBlockWithSegments,
7438 segment_layers: Vec<usize>,
7439 },
7440 HighThroughput {
7441 encoded: bitplane_encode::EncodedCodeBlock,
7442 segment_layers: Vec<usize>,
7443 },
7444}
7445
7446#[derive(Debug, Clone, Copy, PartialEq)]
7447struct ClassicSegmentAssignmentCandidate {
7448 block_index: usize,
7449 segment_index: usize,
7450 rate: u64,
7451 distortion_delta: f64,
7452}
7453
7454#[derive(Debug, Clone, Copy)]
7455struct ClassicSegmentLocation {
7456 packet_idx: usize,
7457 subband_idx: usize,
7458 block_idx: usize,
7459 segment_idx: usize,
7460}
7461
7462#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7463struct HtSegmentAssignmentCandidate {
7464 block_index: usize,
7465 segment_index: usize,
7466 rate: u64,
7467}
7468
7469#[derive(Debug, Clone, Copy)]
7470struct HtSegmentLocation {
7471 packet_idx: usize,
7472 subband_idx: usize,
7473 block_idx: usize,
7474 segment_idx: usize,
7475}
7476
7477struct ClassicLayerBudgetAllocator {
7478 cumulative_targets: Vec<u64>,
7479 cumulative_used: Vec<u64>,
7480}
7481
7482impl ClassicLayerBudgetAllocator {
7483 fn new(cumulative_targets: &[u64], layer_count: usize) -> Result<Self, &'static str> {
7484 if cumulative_targets.is_empty() {
7485 return Ok(Self {
7486 cumulative_targets: Vec::new(),
7487 cumulative_used: Vec::new(),
7488 });
7489 }
7490 if cumulative_targets.len() != layer_count {
7491 return Err("quality layer byte target count must match quality layer count");
7492 }
7493 if cumulative_targets.windows(2).any(|pair| pair[0] > pair[1]) {
7494 return Err("quality layer byte targets must be cumulative and monotonic");
7495 }
7496 Ok(Self {
7497 cumulative_targets: cumulative_targets
7498 .iter()
7499 .map(|&target| target.saturating_add(classic_rate_target_tolerance(target)))
7500 .collect(),
7501 cumulative_used: vec![0; layer_count],
7502 })
7503 }
7504
7505 fn is_budgeted(&self) -> bool {
7506 !self.cumulative_targets.is_empty()
7507 }
7508
7509 fn assign_segment(
7510 &mut self,
7511 min_layer: usize,
7512 data_length: u64,
7513 ) -> Result<usize, &'static str> {
7514 if !self.is_budgeted() {
7515 return Ok(min_layer);
7516 }
7517
7518 let rate = data_length;
7519 let last_layer = self
7520 .cumulative_targets
7521 .len()
7522 .checked_sub(1)
7523 .ok_or("quality layer target count underflow")?;
7524 for layer_idx in min_layer..last_layer {
7525 if self.layer_can_accept(layer_idx, rate)? {
7526 self.record_segment(layer_idx, rate)?;
7527 return Ok(layer_idx);
7528 }
7529 }
7530 self.record_segment(last_layer, rate)?;
7531 Ok(last_layer)
7532 }
7533
7534 fn layer_can_accept(&self, layer_idx: usize, rate: u64) -> Result<bool, &'static str> {
7535 for cumulative_idx in layer_idx..self.cumulative_targets.len() {
7536 let used = self.cumulative_used[cumulative_idx]
7537 .checked_add(rate)
7538 .ok_or("quality layer byte budget overflow")?;
7539 if used > self.cumulative_targets[cumulative_idx] {
7540 return Ok(false);
7541 }
7542 }
7543 Ok(true)
7544 }
7545
7546 fn record_segment(&mut self, layer_idx: usize, rate: u64) -> Result<(), &'static str> {
7547 for used in &mut self.cumulative_used[layer_idx..] {
7548 *used = used
7549 .checked_add(rate)
7550 .ok_or("quality layer byte budget overflow")?;
7551 }
7552 Ok(())
7553 }
7554}
7555
7556fn classic_rate_target_tolerance(target: u64) -> u64 {
7557 (target / 100).max(512)
7558}
7559
7560fn assign_classic_segment_layers_by_slope(
7561 candidates: &[ClassicSegmentAssignmentCandidate],
7562 layer_count: usize,
7563 cumulative_targets: &[u64],
7564) -> Result<Vec<usize>, &'static str> {
7565 let mut allocator = ClassicLayerBudgetAllocator::new(cumulative_targets, layer_count)?;
7566 if candidates.is_empty() {
7567 return Ok(Vec::new());
7568 }
7569
7570 let block_count = candidates
7571 .iter()
7572 .map(|candidate| candidate.block_index)
7573 .max()
7574 .and_then(|max| max.checked_add(1))
7575 .ok_or("classic PCRD block count overflow")?;
7576 let mut block_candidates = vec![Vec::new(); block_count];
7577 for (candidate_idx, candidate) in candidates.iter().enumerate() {
7578 block_candidates
7579 .get_mut(candidate.block_index)
7580 .ok_or("classic PCRD block index mismatch")?
7581 .push(candidate_idx);
7582 }
7583 for block in &mut block_candidates {
7584 block.sort_by_key(|&idx| candidates[idx].segment_index);
7585 }
7586
7587 let mut block_min_layers = vec![0usize; block_count];
7588 let mut assignments = vec![layer_count.saturating_sub(1); candidates.len()];
7589 let mut next_block_segment = vec![0usize; block_count];
7590 let mut remaining = candidates.len();
7591 while remaining > 0 {
7592 let candidate_idx = block_candidates
7593 .iter()
7594 .enumerate()
7595 .filter_map(|(block_idx, block)| block.get(next_block_segment[block_idx]).copied())
7596 .min_by(|&left, &right| compare_classic_segment_candidates(candidates, left, right))
7597 .ok_or("classic PCRD candidate queue underflow")?;
7598 let candidate = candidates[candidate_idx];
7599 let min_layer = *block_min_layers
7600 .get(candidate.block_index)
7601 .ok_or("classic PCRD block index mismatch")?;
7602 let layer = allocator.assign_segment(min_layer, candidate.rate)?;
7603 assignments[candidate_idx] = layer;
7604 if let Some(block_layer) = block_min_layers.get_mut(candidate.block_index) {
7605 *block_layer = layer;
7606 }
7607 if let Some(next) = next_block_segment.get_mut(candidate.block_index) {
7608 *next = next
7609 .checked_add(1)
7610 .ok_or("classic PCRD segment index overflow")?;
7611 }
7612 remaining -= 1;
7613 }
7614
7615 enforce_classic_assignment_monotonicity(candidates, &mut assignments);
7616 Ok(assignments)
7617}
7618
7619fn compare_classic_segment_candidates(
7620 candidates: &[ClassicSegmentAssignmentCandidate],
7621 left: usize,
7622 right: usize,
7623) -> Ordering {
7624 let left_candidate = candidates[left];
7625 let right_candidate = candidates[right];
7626 pcrd_slope(right_candidate)
7627 .partial_cmp(&pcrd_slope(left_candidate))
7628 .unwrap_or(Ordering::Equal)
7629 .then_with(|| left_candidate.block_index.cmp(&right_candidate.block_index))
7630 .then_with(|| {
7631 left_candidate
7632 .segment_index
7633 .cmp(&right_candidate.segment_index)
7634 })
7635}
7636
7637fn pcrd_slope(candidate: ClassicSegmentAssignmentCandidate) -> f64 {
7638 if candidate.rate == 0 {
7639 return f64::INFINITY;
7640 }
7641 candidate.distortion_delta / candidate.rate as f64
7642}
7643
7644fn enforce_classic_assignment_monotonicity(
7645 candidates: &[ClassicSegmentAssignmentCandidate],
7646 assignments: &mut [usize],
7647) {
7648 let mut order: Vec<_> = (0..candidates.len()).collect();
7649 order.sort_by_key(|&idx| (candidates[idx].block_index, candidates[idx].segment_index));
7650 let mut current_block = None;
7651 let mut min_layer = 0usize;
7652 for idx in order {
7653 if current_block != Some(candidates[idx].block_index) {
7654 current_block = Some(candidates[idx].block_index);
7655 min_layer = 0;
7656 }
7657 if assignments[idx] < min_layer {
7658 assignments[idx] = min_layer;
7659 }
7660 min_layer = assignments[idx];
7661 }
7662}
7663
7664fn enforce_classic_segment_layer_monotonicity(layered_packets: &mut [LayeredPreparedPacket]) {
7665 for packet in layered_packets {
7666 for subband in &mut packet.subbands {
7667 for block in &mut subband.blocks {
7668 if let LayeredPreparedBlock::Classic { segment_layers, .. } = block {
7669 let mut min_layer = 0usize;
7670 for layer in segment_layers {
7671 if *layer < min_layer {
7672 *layer = min_layer;
7673 }
7674 min_layer = *layer;
7675 }
7676 }
7677 }
7678 }
7679 }
7680}
7681
7682fn enforce_ht_segment_layer_monotonicity(layered_packets: &mut [LayeredPreparedPacket]) {
7683 for packet in layered_packets {
7684 for subband in &mut packet.subbands {
7685 for block in &mut subband.blocks {
7686 if let LayeredPreparedBlock::HighThroughput { segment_layers, .. } = block {
7687 let mut min_layer = 0usize;
7688 for layer in segment_layers {
7689 if *layer < min_layer {
7690 *layer = min_layer;
7691 }
7692 min_layer = *layer;
7693 }
7694 }
7695 }
7696 }
7697 }
7698}
7699
7700fn assign_ht_segment_layers_by_budget(
7701 candidates: &[HtSegmentAssignmentCandidate],
7702 layer_count: usize,
7703 cumulative_targets: &[u64],
7704) -> Result<Vec<usize>, &'static str> {
7705 let mut allocator = ClassicLayerBudgetAllocator::new(cumulative_targets, layer_count)?;
7706 let mut assignments = vec![layer_count.saturating_sub(1); candidates.len()];
7707 let mut candidate_order: Vec<_> = (0..candidates.len()).collect();
7708 candidate_order
7709 .sort_by_key(|&idx| (candidates[idx].block_index, candidates[idx].segment_index));
7710 let mut block_min_layers = vec![
7711 0usize;
7712 candidates
7713 .iter()
7714 .map(|c| c.block_index)
7715 .max()
7716 .map_or(0, |idx| idx + 1)
7717 ];
7718
7719 for candidate_idx in candidate_order {
7720 let candidate = candidates
7721 .get(candidate_idx)
7722 .ok_or("HTJ2K segment candidate index mismatch")?;
7723 let min_layer = *block_min_layers
7724 .get(candidate.block_index)
7725 .ok_or("HTJ2K segment candidate block index mismatch")?;
7726 let layer = allocator.assign_segment(min_layer, candidate.rate)?;
7727 assignments[candidate_idx] = layer;
7728 if let Some(block_layer) = block_min_layers.get_mut(candidate.block_index) {
7729 *block_layer = layer;
7730 }
7731 }
7732
7733 Ok(assignments)
7734}
7735
7736fn ht_segment_count(encoded: &bitplane_encode::EncodedCodeBlock) -> usize {
7737 match encoded.num_coding_passes {
7738 0 => 0,
7739 1 => 1,
7740 _ => 2,
7741 }
7742}
7743
7744fn ht_segment_rate(
7745 encoded: &bitplane_encode::EncodedCodeBlock,
7746 segment_idx: usize,
7747) -> Result<u64, &'static str> {
7748 match segment_idx {
7749 0 if encoded.num_coding_passes > 0 => Ok(u64::from(encoded.ht_cleanup_length)),
7750 1 if encoded.num_coding_passes > 1 => Ok(u64::from(encoded.ht_refinement_length)),
7751 _ => Err("HTJ2K segment index out of range"),
7752 }
7753}
7754
7755fn ht_unbudgeted_segment_layers(
7756 encoded: &bitplane_encode::EncodedCodeBlock,
7757 num_layers: u8,
7758 block_idx: usize,
7759 block_count: usize,
7760) -> Result<Vec<usize>, &'static str> {
7761 let segment_count = ht_segment_count(encoded);
7762 if segment_count == 0 {
7763 return Ok(Vec::new());
7764 }
7765 let layer_count = usize::from(num_layers);
7766 if layer_count == 0 {
7767 return Err("HTJ2K layer allocation requires non-empty inputs");
7768 }
7769 if encoded.num_coding_passes == 1 {
7770 return Ok(vec![ht_target_layer(block_idx, block_count, layer_count)?]);
7771 }
7772
7773 let mut segment_layers = Vec::with_capacity(segment_count);
7774 let mut min_layer = 0usize;
7775 for (_, end_pass) in [(0, 1), (1, encoded.num_coding_passes)] {
7776 let mut assigned = None;
7777 for layer_idx in min_layer..layer_count {
7778 let cumulative_passes = if layer_idx + 1 == layer_count {
7779 encoded.num_coding_passes
7780 } else {
7781 layer_pass_count(encoded.num_coding_passes, layer_idx + 1, num_layers)?
7782 };
7783 if end_pass <= cumulative_passes {
7784 assigned = Some(layer_idx);
7785 break;
7786 }
7787 }
7788 let assigned =
7789 assigned.ok_or("HTJ2K quality layer split must align to segment boundaries")?;
7790 segment_layers.push(assigned);
7791 min_layer = assigned;
7792 }
7793 Ok(segment_layers)
7794}
7795
7796fn classic_unbudgeted_segment_layers(
7797 encoded: &bitplane_encode::EncodedCodeBlockWithSegments,
7798 num_layers: u8,
7799) -> Result<Vec<usize>, &'static str> {
7800 let mut segment_layers = Vec::with_capacity(encoded.segments.len());
7801 for segment in &encoded.segments {
7802 let mut assigned = None;
7803 for layer_idx in 0..usize::from(num_layers) {
7804 let previous_pass =
7805 previous_layer_pass_count(encoded.num_coding_passes, layer_idx, num_layers)?;
7806 let cumulative_passes = if layer_idx + 1 == usize::from(num_layers) {
7807 encoded.num_coding_passes
7808 } else {
7809 layer_pass_count(encoded.num_coding_passes, layer_idx + 1, num_layers)?
7810 };
7811 if segment.start_coding_pass >= previous_pass
7812 && segment.end_coding_pass <= cumulative_passes
7813 {
7814 assigned = Some(layer_idx);
7815 break;
7816 }
7817 }
7818 segment_layers.push(
7819 assigned.ok_or("classic quality layer split must align to terminated coding passes")?,
7820 );
7821 }
7822 Ok(segment_layers)
7823}
7824
7825fn classic_layer_contributions(
7826 encoded: bitplane_encode::EncodedCodeBlockWithSegments,
7827 num_layers: u8,
7828 segment_layers: &[usize],
7829) -> Result<Vec<CodeBlockPacketData>, &'static str> {
7830 let layer_count = usize::from(num_layers);
7831 if segment_layers.len() != encoded.segments.len() {
7832 return Err("classic PCRD segment assignment count mismatch");
7833 }
7834 if segment_layers.iter().any(|&layer| layer >= layer_count) {
7835 return Err("classic PCRD segment layer exceeds layer count");
7836 }
7837 let mut contributions = Vec::with_capacity(layer_count);
7838
7839 for layer_idx in 0..layer_count {
7840 let mut data = Vec::new();
7841 let mut classic_segment_lengths = Vec::new();
7842 let mut contribution_passes = 0u8;
7843
7844 for (segment_idx, segment) in encoded.segments.iter().enumerate() {
7845 if segment_layers[segment_idx] != layer_idx {
7846 continue;
7847 }
7848 let start = usize::try_from(segment.data_offset)
7849 .map_err(|_| "classic code-block segment offset overflow")?;
7850 let len = usize::try_from(segment.data_length)
7851 .map_err(|_| "classic code-block segment length overflow")?;
7852 let end = start
7853 .checked_add(len)
7854 .ok_or("classic code-block segment range overflow")?;
7855 data.extend_from_slice(
7856 encoded
7857 .data
7858 .get(start..end)
7859 .ok_or("classic code-block segment range invalid")?,
7860 );
7861 classic_segment_lengths.push(segment.data_length);
7862 contribution_passes = contribution_passes
7863 .checked_add(segment.end_coding_pass - segment.start_coding_pass)
7864 .ok_or("classic code-block contribution pass count overflow")?;
7865 }
7866
7867 contributions.push(CodeBlockPacketData {
7868 data,
7869 ht_cleanup_length: 0,
7870 ht_refinement_length: 0,
7871 num_coding_passes: contribution_passes,
7872 classic_segment_lengths,
7873 num_zero_bitplanes: encoded.num_zero_bitplanes,
7874 previously_included: false,
7875 l_block: 3,
7876 block_coding_mode: BlockCodingMode::Classic,
7877 });
7878 }
7879
7880 Ok(contributions)
7881}
7882
7883fn layer_pass_count(
7884 num_coding_passes: u8,
7885 layer_count: usize,
7886 num_layers: u8,
7887) -> Result<u8, &'static str> {
7888 let numerator = u32::from(num_coding_passes)
7889 .checked_mul(u32::try_from(layer_count).map_err(|_| "layer index overflow")?)
7890 .ok_or("quality layer pass allocation overflow")?;
7891 numerator
7892 .div_ceil(u32::from(num_layers))
7893 .try_into()
7894 .map_err(|_| "quality layer pass allocation overflow")
7895}
7896
7897fn previous_layer_pass_count(
7898 num_coding_passes: u8,
7899 layer_idx: usize,
7900 num_layers: u8,
7901) -> Result<u8, &'static str> {
7902 if layer_idx == 0 {
7903 Ok(0)
7904 } else {
7905 layer_pass_count(num_coding_passes, layer_idx, num_layers)
7906 }
7907}
7908
7909fn ht_target_layer(
7910 block_idx: usize,
7911 block_count: usize,
7912 layer_count: usize,
7913) -> Result<usize, &'static str> {
7914 if block_count == 0 || layer_count == 0 {
7915 return Err("HTJ2K layer allocation requires non-empty inputs");
7916 }
7917 Ok(block_idx
7918 .checked_mul(layer_count)
7919 .ok_or("HTJ2K layer allocation overflow")?
7920 / block_count)
7921}
7922
7923fn ht_layer_contributions(
7924 encoded: bitplane_encode::EncodedCodeBlock,
7925 num_layers: u8,
7926 segment_layers: &[usize],
7927) -> Result<Vec<CodeBlockPacketData>, &'static str> {
7928 let layer_count = usize::from(num_layers);
7929 if segment_layers.len() != ht_segment_count(&encoded) {
7930 return Err("HTJ2K segment assignment count mismatch");
7931 }
7932 if segment_layers.iter().any(|&layer| layer >= layer_count) {
7933 return Err("HTJ2K segment layer exceeds layer count");
7934 }
7935 if segment_layers
7936 .windows(2)
7937 .any(|layers| layers[1] < layers[0])
7938 {
7939 return Err("HTJ2K segment layers must be monotonic");
7940 }
7941
7942 let cleanup_len = usize::try_from(encoded.ht_cleanup_length)
7943 .map_err(|_| "HTJ2K cleanup segment length overflow")?;
7944 let refinement_len = usize::try_from(encoded.ht_refinement_length)
7945 .map_err(|_| "HTJ2K refinement segment length overflow")?;
7946 let refinement_start = cleanup_len;
7947 let refinement_end = refinement_start
7948 .checked_add(refinement_len)
7949 .ok_or("HTJ2K refinement segment range overflow")?;
7950 if encoded.num_coding_passes > 0 && cleanup_len == 0 {
7951 return Err("HTJ2K cleanup segment is missing");
7952 }
7953 if encoded.num_coding_passes > 1 && refinement_len == 0 {
7954 return Err("HTJ2K refinement segment is missing");
7955 }
7956 if refinement_end > encoded.data.len() {
7957 return Err("HTJ2K segment range invalid");
7958 }
7959
7960 let mut contributions = Vec::with_capacity(layer_count);
7961 for layer_idx in 0..layer_count {
7962 let mut data = Vec::new();
7963 let mut ht_cleanup_length = 0u32;
7964 let mut ht_refinement_length = 0u32;
7965 let mut num_coding_passes = 0u8;
7966
7967 if segment_layers.first() == Some(&layer_idx) {
7968 data.extend_from_slice(
7969 encoded
7970 .data
7971 .get(..cleanup_len)
7972 .ok_or("HTJ2K cleanup segment range invalid")?,
7973 );
7974 ht_cleanup_length = encoded.ht_cleanup_length;
7975 num_coding_passes = num_coding_passes
7976 .checked_add(1)
7977 .ok_or("HTJ2K packet contribution pass count overflow")?;
7978 }
7979
7980 if encoded.num_coding_passes > 1 && segment_layers.get(1) == Some(&layer_idx) {
7981 data.extend_from_slice(
7982 encoded
7983 .data
7984 .get(refinement_start..refinement_end)
7985 .ok_or("HTJ2K refinement segment range invalid")?,
7986 );
7987 ht_refinement_length = encoded.ht_refinement_length;
7988 num_coding_passes = num_coding_passes
7989 .checked_add(encoded.num_coding_passes - 1)
7990 .ok_or("HTJ2K packet contribution pass count overflow")?;
7991 }
7992
7993 contributions.push(CodeBlockPacketData {
7994 data,
7995 ht_cleanup_length,
7996 ht_refinement_length,
7997 num_coding_passes,
7998 classic_segment_lengths: Vec::new(),
7999 num_zero_bitplanes: encoded.num_zero_bitplanes,
8000 previously_included: false,
8001 l_block: 3,
8002 block_coding_mode: BlockCodingMode::HighThroughput,
8003 });
8004 }
8005
8006 Ok(contributions)
8007}
8008
8009fn encode_prepared_subbands(
8010 prepared_subbands: Vec<PreparedEncodeSubband>,
8011 accelerator: &mut impl J2kEncodeStageAccelerator,
8012) -> Result<Vec<SubbandPrecinct>, &'static str> {
8013 let block_coding_mode = prepared_subbands
8014 .iter()
8015 .find(|subband| !subband.code_blocks.is_empty())
8016 .map(|subband| subband.block_coding_mode);
8017 let encoded_blocks = match block_coding_mode {
8018 Some(BlockCodingMode::HighThroughput) => {
8019 encode_all_ht_code_blocks(&prepared_subbands, accelerator)?
8020 }
8021 Some(BlockCodingMode::Classic) => {
8022 encode_all_tier1_code_blocks(&prepared_subbands, accelerator)?
8023 }
8024 None => Vec::new(),
8025 };
8026
8027 let mut encoded_iter = encoded_blocks.into_iter();
8028 let mut precincts = Vec::with_capacity(prepared_subbands.len());
8029 for subband in prepared_subbands {
8030 let mut code_blocks = Vec::with_capacity(subband.code_blocks.len());
8031 for _ in 0..subband.code_blocks.len() {
8032 let encoded = encoded_iter
8033 .next()
8034 .ok_or("encoded code-block count mismatch")?;
8035 code_blocks.push(CodeBlockPacketData {
8036 data: encoded.data,
8037 ht_cleanup_length: if subband.block_coding_mode == BlockCodingMode::HighThroughput {
8038 encoded.ht_cleanup_length
8039 } else {
8040 0
8041 },
8042 ht_refinement_length: if subband.block_coding_mode
8043 == BlockCodingMode::HighThroughput
8044 {
8045 encoded.ht_refinement_length
8046 } else {
8047 0
8048 },
8049 num_coding_passes: encoded.num_coding_passes,
8050 classic_segment_lengths: Vec::new(),
8051 num_zero_bitplanes: encoded.num_zero_bitplanes,
8052 previously_included: false,
8053 l_block: 3,
8054 block_coding_mode: subband.block_coding_mode,
8055 });
8056 }
8057 precincts.push(SubbandPrecinct {
8058 code_blocks,
8059 num_cbs_x: subband.num_cbs_x,
8060 num_cbs_y: subband.num_cbs_y,
8061 });
8062 }
8063 if encoded_iter.next().is_some() {
8064 return Err("encoded code-block count mismatch");
8065 }
8066
8067 Ok(precincts)
8068}
8069
8070fn encode_all_ht_code_blocks(
8071 prepared_subbands: &[PreparedEncodeSubband],
8072 accelerator: &mut impl J2kEncodeStageAccelerator,
8073) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8074 if prepared_subbands.iter().all(|subband| {
8075 subband.code_blocks.is_empty() || subband.preencoded_ht_code_blocks.is_some()
8076 }) {
8077 let total_blocks = prepared_subbands
8078 .iter()
8079 .map(|subband| subband.code_blocks.len())
8080 .sum();
8081 let mut encoded = Vec::with_capacity(total_blocks);
8082 for subband in prepared_subbands {
8083 if let Some(blocks) = &subband.preencoded_ht_code_blocks {
8084 if blocks.len() != subband.code_blocks.len() {
8085 return Err("preencoded HT subband code-block count mismatch");
8086 }
8087 encoded.extend(
8088 blocks
8089 .iter()
8090 .cloned()
8091 .map(ht_encoded_code_block_from_accelerator),
8092 );
8093 }
8094 }
8095 return Ok(encoded);
8096 }
8097 if prepared_subbands
8098 .iter()
8099 .any(|subband| subband.preencoded_ht_code_blocks.is_some())
8100 {
8101 return Err("mixed preencoded and quantized HT subbands are unsupported");
8102 }
8103
8104 let job_coefficients = prepared_subbands
8105 .iter()
8106 .flat_map(|subband| subband.code_blocks.iter())
8107 .map(|block| downcast_i64_coefficients_to_i32(&block.coefficients))
8108 .collect::<Result<Vec<_>, _>>()?;
8109 let mut jobs = Vec::with_capacity(job_coefficients.len());
8110 let mut coefficient_idx = 0usize;
8111 for subband in prepared_subbands {
8112 for block in &subband.code_blocks {
8113 let coefficients = job_coefficients
8114 .get(coefficient_idx)
8115 .ok_or("HT coefficient storage count mismatch")?;
8116 jobs.push(crate::J2kHtCodeBlockEncodeJob {
8117 coefficients,
8118 width: block.width,
8119 height: block.height,
8120 total_bitplanes: subband.total_bitplanes,
8121 target_coding_passes: subband.ht_target_coding_passes,
8122 });
8123 coefficient_idx = coefficient_idx
8124 .checked_add(1)
8125 .ok_or("HT coefficient storage count overflow")?;
8126 }
8127 }
8128
8129 if let Some(encoded) = accelerator.encode_ht_code_blocks(&jobs)? {
8130 if encoded.len() != jobs.len() {
8131 return Err("accelerated HT code-block batch length mismatch");
8132 }
8133 return Ok(encoded
8134 .into_iter()
8135 .map(ht_encoded_code_block_from_accelerator)
8136 .collect());
8137 }
8138
8139 if accelerator.prefer_parallel_cpu_code_block_fallback() {
8140 if jobs.len() < HT_CPU_PARALLEL_FALLBACK_MIN_JOBS {
8141 return encode_all_ht_code_blocks_serial_cpu(&jobs);
8142 }
8143 return encode_all_ht_code_blocks_parallel(&jobs);
8144 }
8145
8146 jobs.iter()
8147 .map(|job| {
8148 encode_ht_code_block(
8149 job.coefficients,
8150 job.width,
8151 job.height,
8152 job.total_bitplanes,
8153 job.target_coding_passes,
8154 accelerator,
8155 )
8156 })
8157 .collect()
8158}
8159
8160fn encode_all_tier1_code_blocks(
8161 prepared_subbands: &[PreparedEncodeSubband],
8162 accelerator: &mut impl J2kEncodeStageAccelerator,
8163) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8164 let style = default_public_code_block_style();
8165 let can_use_i32_jobs = prepared_subbands
8166 .iter()
8167 .flat_map(|subband| &subband.code_blocks)
8168 .all(|block| coefficients_fit_i32(&block.coefficients));
8169 if !can_use_i32_jobs {
8170 let mut encoded = Vec::new();
8171 for subband in prepared_subbands {
8172 encoded.reserve(subband.code_blocks.len());
8173 for block in &subband.code_blocks {
8174 encoded.push(bitplane_encode::encode_code_block_i64(
8175 &block.coefficients,
8176 block.width,
8177 block.height,
8178 subband.sub_band_type,
8179 subband.total_bitplanes,
8180 ));
8181 }
8182 }
8183 return Ok(encoded);
8184 }
8185
8186 let job_coefficients = prepared_subbands
8187 .iter()
8188 .flat_map(|subband| subband.code_blocks.iter())
8189 .map(|block| downcast_i64_coefficients_to_i32(&block.coefficients))
8190 .collect::<Result<Vec<_>, _>>()?;
8191 let mut jobs = Vec::with_capacity(job_coefficients.len());
8192 let mut coefficient_idx = 0usize;
8193 for subband in prepared_subbands {
8194 let public_sub_band_type = public_sub_band_type(subband.sub_band_type);
8195 for block in &subband.code_blocks {
8196 let coefficients = job_coefficients
8197 .get(coefficient_idx)
8198 .ok_or("classic coefficient storage count mismatch")?;
8199 jobs.push(J2kTier1CodeBlockEncodeJob {
8200 coefficients,
8201 width: block.width,
8202 height: block.height,
8203 sub_band_type: public_sub_band_type,
8204 total_bitplanes: subband.total_bitplanes,
8205 style,
8206 });
8207 coefficient_idx = coefficient_idx
8208 .checked_add(1)
8209 .ok_or("classic coefficient storage count overflow")?;
8210 }
8211 }
8212
8213 if let Some(encoded) = accelerator.encode_tier1_code_blocks(&jobs)? {
8214 if encoded.len() != jobs.len() {
8215 return Err("accelerated classic code-block batch length mismatch");
8216 }
8217 return Ok(encoded
8218 .into_iter()
8219 .map(encoded_code_block_from_accelerator)
8220 .collect());
8221 }
8222
8223 if accelerator.prefer_parallel_cpu_code_block_fallback() {
8224 return encode_all_tier1_code_blocks_parallel(&jobs);
8225 }
8226
8227 let mut encoded = Vec::with_capacity(jobs.len());
8228 for job in &jobs {
8229 encoded.push(encode_tier1_code_block(
8230 job.coefficients,
8231 job.width,
8232 job.height,
8233 internal_sub_band_type(job.sub_band_type),
8234 job.total_bitplanes,
8235 accelerator,
8236 )?);
8237 }
8238 Ok(encoded)
8239}
8240
8241fn encode_all_ht_code_blocks_serial_cpu(
8242 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
8243) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8244 if jobs
8245 .iter()
8246 .any(|job| !(1..=3).contains(&job.target_coding_passes))
8247 {
8248 return Err("CPU HTJ2K code-block fallback supports at most three HT coding passes");
8249 }
8250 jobs.iter()
8251 .map(|job| {
8252 ht_block_encode::encode_code_block_with_passes(
8253 job.coefficients,
8254 job.width,
8255 job.height,
8256 job.total_bitplanes,
8257 job.target_coding_passes,
8258 )
8259 })
8260 .collect()
8261}
8262
8263#[cfg(feature = "parallel")]
8264fn encode_all_ht_code_blocks_parallel(
8265 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
8266) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8267 if jobs
8268 .iter()
8269 .any(|job| !(1..=3).contains(&job.target_coding_passes))
8270 {
8271 return Err("CPU HTJ2K code-block fallback supports at most three HT coding passes");
8272 }
8273 jobs.par_iter()
8274 .map(|job| {
8275 ht_block_encode::encode_code_block_with_passes(
8276 job.coefficients,
8277 job.width,
8278 job.height,
8279 job.total_bitplanes,
8280 job.target_coding_passes,
8281 )
8282 })
8283 .collect()
8284}
8285
8286#[cfg(not(feature = "parallel"))]
8287fn encode_all_ht_code_blocks_parallel(
8288 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
8289) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8290 if jobs
8291 .iter()
8292 .any(|job| !(1..=3).contains(&job.target_coding_passes))
8293 {
8294 return Err("CPU HTJ2K code-block fallback supports at most three HT coding passes");
8295 }
8296 jobs.iter()
8297 .map(|job| {
8298 ht_block_encode::encode_code_block_with_passes(
8299 job.coefficients,
8300 job.width,
8301 job.height,
8302 job.total_bitplanes,
8303 job.target_coding_passes,
8304 )
8305 })
8306 .collect()
8307}
8308
8309#[cfg(feature = "parallel")]
8310fn encode_all_tier1_code_blocks_parallel(
8311 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
8312) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8313 jobs.par_iter()
8314 .map(|job| {
8315 Ok(bitplane_encode::encode_code_block(
8316 job.coefficients,
8317 job.width,
8318 job.height,
8319 internal_sub_band_type(job.sub_band_type),
8320 job.total_bitplanes,
8321 ))
8322 })
8323 .collect()
8324}
8325
8326#[cfg(not(feature = "parallel"))]
8327fn encode_all_tier1_code_blocks_parallel(
8328 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
8329) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8330 jobs.iter()
8331 .map(|job| {
8332 Ok(bitplane_encode::encode_code_block(
8333 job.coefficients,
8334 job.width,
8335 job.height,
8336 internal_sub_band_type(job.sub_band_type),
8337 job.total_bitplanes,
8338 ))
8339 })
8340 .collect()
8341}
8342
8343fn encode_ht_code_block(
8344 coefficients: &[i32],
8345 width: u32,
8346 height: u32,
8347 total_bitplanes: u8,
8348 target_coding_passes: u8,
8349 accelerator: &mut impl J2kEncodeStageAccelerator,
8350) -> Result<bitplane_encode::EncodedCodeBlock, &'static str> {
8351 if let Some(encoded) = accelerator.encode_ht_code_block(crate::J2kHtCodeBlockEncodeJob {
8352 coefficients,
8353 width,
8354 height,
8355 total_bitplanes,
8356 target_coding_passes,
8357 })? {
8358 return Ok(ht_encoded_code_block_from_accelerator(encoded));
8359 }
8360
8361 ht_block_encode::encode_code_block_with_passes(
8362 coefficients,
8363 width,
8364 height,
8365 total_bitplanes,
8366 target_coding_passes,
8367 )
8368}
8369
8370fn ht_encoded_code_block_from_accelerator(
8371 encoded: crate::EncodedHtJ2kCodeBlock,
8372) -> bitplane_encode::EncodedCodeBlock {
8373 bitplane_encode::EncodedCodeBlock {
8374 data: encoded.data,
8375 num_coding_passes: encoded.num_coding_passes,
8376 num_zero_bitplanes: encoded.num_zero_bitplanes,
8377 ht_cleanup_length: encoded.cleanup_length,
8378 ht_refinement_length: encoded.refinement_length,
8379 }
8380}
8381
8382fn encode_tier1_code_block(
8383 coefficients: &[i32],
8384 width: u32,
8385 height: u32,
8386 sub_band_type: SubBandType,
8387 total_bitplanes: u8,
8388 accelerator: &mut impl J2kEncodeStageAccelerator,
8389) -> Result<bitplane_encode::EncodedCodeBlock, &'static str> {
8390 if let Some(encoded) = accelerator.encode_tier1_code_block(J2kTier1CodeBlockEncodeJob {
8391 coefficients,
8392 width,
8393 height,
8394 sub_band_type: public_sub_band_type(sub_band_type),
8395 total_bitplanes,
8396 style: default_public_code_block_style(),
8397 })? {
8398 return Ok(encoded_code_block_from_accelerator(encoded));
8399 }
8400
8401 Ok(bitplane_encode::encode_code_block(
8402 coefficients,
8403 width,
8404 height,
8405 sub_band_type,
8406 total_bitplanes,
8407 ))
8408}
8409
8410fn encoded_code_block_from_accelerator(
8411 encoded: EncodedJ2kCodeBlock,
8412) -> bitplane_encode::EncodedCodeBlock {
8413 bitplane_encode::EncodedCodeBlock {
8414 data: encoded.data,
8415 num_coding_passes: encoded.number_of_coding_passes,
8416 num_zero_bitplanes: encoded.missing_bit_planes,
8417 ht_cleanup_length: 0,
8418 ht_refinement_length: 0,
8419 }
8420}
8421
8422fn public_sub_band_type(sub_band_type: SubBandType) -> J2kSubBandType {
8423 match sub_band_type {
8424 SubBandType::LowLow => J2kSubBandType::LowLow,
8425 SubBandType::HighLow => J2kSubBandType::HighLow,
8426 SubBandType::LowHigh => J2kSubBandType::LowHigh,
8427 SubBandType::HighHigh => J2kSubBandType::HighHigh,
8428 }
8429}
8430
8431fn internal_sub_band_type(sub_band_type: J2kSubBandType) -> SubBandType {
8432 match sub_band_type {
8433 J2kSubBandType::LowLow => SubBandType::LowLow,
8434 J2kSubBandType::HighLow => SubBandType::HighLow,
8435 J2kSubBandType::LowHigh => SubBandType::LowHigh,
8436 J2kSubBandType::HighHigh => SubBandType::HighHigh,
8437 }
8438}
8439
8440fn default_public_code_block_style() -> crate::J2kCodeBlockStyle {
8441 crate::J2kCodeBlockStyle {
8442 selective_arithmetic_coding_bypass: false,
8443 reset_context_probabilities: false,
8444 termination_on_each_pass: false,
8445 vertically_causal_context: false,
8446 segmentation_symbols: false,
8447 }
8448}
8449
8450pub(crate) fn deinterleave_to_f32(
8452 pixels: &[u8],
8453 num_pixels: usize,
8454 num_components: u16,
8455 bit_depth: u8,
8456 signed: bool,
8457) -> Vec<Vec<f32>> {
8458 if num_components == 3 && bit_depth == 8 && !signed {
8459 return deinterleave_rgb8_unsigned_to_f32(pixels, num_pixels);
8460 }
8461
8462 let nc = num_components as usize;
8463 let mut components = vec![vec![0.0f32; num_pixels]; nc];
8464 let unsigned_offset = if signed {
8465 0.0
8466 } else {
8467 (1_u64 << (u32::from(bit_depth) - 1)) as f32
8468 };
8469
8470 let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth).unwrap_or(2);
8471 for (i, pixel) in pixels
8472 .chunks_exact(nc * bytes_per_sample)
8473 .take(num_pixels)
8474 .enumerate()
8475 {
8476 for (c, component) in components.iter_mut().enumerate().take(nc) {
8477 let offset = c * bytes_per_sample;
8478 let raw = read_le_sample_value(&pixel[offset..offset + bytes_per_sample], bit_depth);
8479 component[i] = if signed {
8480 sign_extend_sample(raw, bit_depth) as f32
8481 } else {
8482 raw as f32 - unsigned_offset
8483 };
8484 }
8485 }
8486
8487 components
8488}
8489
8490fn deinterleave_rgb8_unsigned_to_f32(pixels: &[u8], num_pixels: usize) -> Vec<Vec<f32>> {
8491 let mut r = Vec::with_capacity(num_pixels);
8492 let mut g = Vec::with_capacity(num_pixels);
8493 let mut b = Vec::with_capacity(num_pixels);
8494
8495 for pixel in pixels.chunks_exact(3).take(num_pixels) {
8496 r.push(f32::from(pixel[0]) - 128.0);
8497 g.push(f32::from(pixel[1]) - 128.0);
8498 b.push(f32::from(pixel[2]) - 128.0);
8499 }
8500
8501 vec![r, g, b]
8502}
8503
8504fn max_decomposition_levels(width: u32, height: u32) -> u8 {
8506 let min_dim = width.min(height);
8507 if min_dim <= 1 {
8508 return 0;
8509 }
8510 floor_f32(log2_f32(min_dim as f32)) as u8
8511}
8512
8513#[cfg(test)]
8514mod tests {
8515 use super::*;
8516 use crate::PrequantizedHtj2k97CodeBlock;
8517
8518 fn test_preencoded_subband_payload(marker: u8) -> PreencodedHtj2k97Subband {
8519 PreencodedHtj2k97Subband {
8520 sub_band_type: J2kSubBandType::LowLow,
8521 num_cbs_x: 1,
8522 num_cbs_y: 1,
8523 total_bitplanes: 8,
8524 code_blocks: vec![PreencodedHtj2k97CodeBlock {
8525 width: 1,
8526 height: 1,
8527 encoded: crate::EncodedHtJ2kCodeBlock {
8528 data: vec![marker; 8],
8529 cleanup_length: 8,
8530 refinement_length: 0,
8531 num_coding_passes: 1,
8532 num_zero_bitplanes: 0,
8533 },
8534 }],
8535 }
8536 }
8537
8538 #[test]
8539 fn prepared_subband_from_owned_preencoded_moves_payload_without_clone() {
8540 let subband = test_preencoded_subband_payload(7);
8541 let original_ptr = subband.code_blocks[0].encoded.data.as_ptr() as usize;
8542
8543 let prepared =
8544 prepared_subband_from_preencoded_owned(subband).expect("owned preencoded subband");
8545 let prepared_blocks = prepared
8546 .preencoded_ht_code_blocks
8547 .expect("preencoded payloads");
8548
8549 assert_eq!(prepared_blocks[0].data.as_ptr() as usize, original_ptr);
8550 assert!(prepared.code_blocks[0].coefficients.is_empty());
8551 }
8552
8553 #[test]
8554 fn compact_preencoded_packetization_borrows_payload_ranges() {
8555 #[derive(Default)]
8556 struct RecordingPacketizationAccelerator {
8557 payload_base: usize,
8558 observed_offsets: Vec<usize>,
8559 observed_lengths: Vec<usize>,
8560 }
8561
8562 impl crate::J2kEncodeStageAccelerator for RecordingPacketizationAccelerator {
8563 fn encode_packetization(
8564 &mut self,
8565 job: crate::J2kPacketizationEncodeJob<'_>,
8566 ) -> core::result::Result<Option<Vec<u8>>, &'static str> {
8567 for code_block in job
8568 .resolutions
8569 .iter()
8570 .flat_map(|resolution| resolution.subbands.iter())
8571 .flat_map(|subband| subband.code_blocks.iter())
8572 .filter(|code_block| !code_block.data.is_empty())
8573 {
8574 self.observed_offsets
8575 .push((code_block.data.as_ptr() as usize) - self.payload_base);
8576 self.observed_lengths.push(code_block.data.len());
8577 }
8578 Ok(Some(crate::encode_j2k_packetization_scalar(job)?))
8579 }
8580 }
8581
8582 let (preencoded, options) = sample_preencoded_htj2k97_for_test();
8583 let expected = encode_preencoded_htj2k_97(&preencoded, &options).expect("owned preencoded");
8584 let mut payload = Vec::new();
8585 let mut expected_offsets = Vec::new();
8586 let mut expected_lengths = Vec::new();
8587 let components = preencoded
8588 .components
8589 .iter()
8590 .map(|component| PreencodedHtj2k97CompactComponent {
8591 x_rsiz: component.x_rsiz,
8592 y_rsiz: component.y_rsiz,
8593 resolutions: component
8594 .resolutions
8595 .iter()
8596 .map(|resolution| PreencodedHtj2k97CompactResolution {
8597 subbands: resolution
8598 .subbands
8599 .iter()
8600 .map(|subband| PreencodedHtj2k97CompactSubband {
8601 sub_band_type: subband.sub_band_type,
8602 num_cbs_x: subband.num_cbs_x,
8603 num_cbs_y: subband.num_cbs_y,
8604 total_bitplanes: subband.total_bitplanes,
8605 code_blocks: subband
8606 .code_blocks
8607 .iter()
8608 .map(|block| {
8609 let start = payload.len();
8610 payload.extend_from_slice(&block.encoded.data);
8611 let end = payload.len();
8612 if start != end {
8613 expected_offsets.push(start);
8614 expected_lengths.push(end - start);
8615 }
8616 PreencodedHtj2k97CompactCodeBlock {
8617 width: block.width,
8618 height: block.height,
8619 payload_range: start..end,
8620 cleanup_length: block.encoded.cleanup_length,
8621 refinement_length: block.encoded.refinement_length,
8622 num_coding_passes: block.encoded.num_coding_passes,
8623 num_zero_bitplanes: block.encoded.num_zero_bitplanes,
8624 }
8625 })
8626 .collect(),
8627 })
8628 .collect(),
8629 })
8630 .collect(),
8631 })
8632 .collect();
8633 let compact = PreencodedHtj2k97CompactImage {
8634 width: preencoded.width,
8635 height: preencoded.height,
8636 bit_depth: preencoded.bit_depth,
8637 signed: preencoded.signed,
8638 payload,
8639 components,
8640 };
8641 let mut accelerator = RecordingPacketizationAccelerator {
8642 payload_base: compact.payload.as_ptr() as usize,
8643 ..Default::default()
8644 };
8645
8646 let actual = encode_preencoded_htj2k_97_compact_owned_with_accelerator(
8647 compact,
8648 &options,
8649 &mut accelerator,
8650 )
8651 .expect("compact preencoded");
8652
8653 assert_eq!(actual, expected);
8654 assert_eq!(accelerator.observed_offsets, expected_offsets);
8655 assert_eq!(accelerator.observed_lengths, expected_lengths);
8656 }
8657
8658 #[test]
8659 fn test_encode_8bit_gray() {
8660 let width = 8u32;
8661 let height = 8u32;
8662 let pixels: Vec<u8> = (0..64).collect();
8663
8664 let result = encode(
8665 &pixels,
8666 width,
8667 height,
8668 1,
8669 8,
8670 false,
8671 &EncodeOptions {
8672 num_decomposition_levels: 2,
8673 ..Default::default()
8674 },
8675 );
8676
8677 assert!(result.is_ok());
8678 let codestream = result.unwrap();
8679 assert_eq!(codestream[0], 0xFF);
8681 assert_eq!(codestream[1], 0x4F);
8682 let len = codestream.len();
8684 assert_eq!(codestream[len - 2], 0xFF);
8685 assert_eq!(codestream[len - 1], 0xD9);
8686 }
8687
8688 #[test]
8689 fn test_encode_16bit_gray() {
8690 let width = 8u32;
8691 let height = 8u32;
8692 let mut pixels = Vec::with_capacity(128);
8693 for i in 0..64u16 {
8694 let val = i * 100;
8695 pixels.extend_from_slice(&val.to_le_bytes());
8696 }
8697
8698 let result = encode(
8699 &pixels,
8700 width,
8701 height,
8702 1,
8703 16,
8704 false,
8705 &EncodeOptions {
8706 num_decomposition_levels: 2,
8707 ..Default::default()
8708 },
8709 );
8710
8711 assert!(result.is_ok());
8712 }
8713
8714 #[test]
8715 fn test_encode_rgb() {
8716 let width = 16u32;
8717 let height = 16u32;
8718 let pixels: Vec<u8> = (0..width * height * 3).map(|i| (i & 0xFF) as u8).collect();
8719
8720 let result = encode(
8721 &pixels,
8722 width,
8723 height,
8724 3,
8725 8,
8726 false,
8727 &EncodeOptions {
8728 num_decomposition_levels: 3,
8729 ..Default::default()
8730 },
8731 );
8732
8733 assert!(result.is_ok(), "RGB encode failed: {:?}", result.err());
8734 }
8735
8736 #[test]
8737 fn encode_with_accelerator_calls_lossless_stage_hooks() {
8738 #[derive(Default)]
8739 struct CountingAccelerator {
8740 forward_rct: usize,
8741 forward_dwt53: usize,
8742 tier1_code_blocks: usize,
8743 tier1_code_block_batches: usize,
8744 tier1_batched_jobs: usize,
8745 packetization: usize,
8746 packetization_resolution_count: u32,
8747 packetization_code_block_count: u32,
8748 packetization_saw_payload: bool,
8749 }
8750
8751 impl crate::J2kEncodeStageAccelerator for CountingAccelerator {
8752 fn encode_forward_rct(
8753 &mut self,
8754 _job: crate::J2kForwardRctJob<'_>,
8755 ) -> core::result::Result<bool, &'static str> {
8756 self.forward_rct += 1;
8757 Ok(false)
8758 }
8759
8760 fn encode_forward_dwt53(
8761 &mut self,
8762 _job: crate::J2kForwardDwt53Job<'_>,
8763 ) -> core::result::Result<Option<crate::J2kForwardDwt53Output>, &'static str>
8764 {
8765 self.forward_dwt53 += 1;
8766 Ok(None)
8767 }
8768
8769 fn encode_tier1_code_block(
8770 &mut self,
8771 _job: crate::J2kTier1CodeBlockEncodeJob<'_>,
8772 ) -> core::result::Result<Option<crate::EncodedJ2kCodeBlock>, &'static str>
8773 {
8774 self.tier1_code_blocks += 1;
8775 Ok(None)
8776 }
8777
8778 fn encode_tier1_code_blocks(
8779 &mut self,
8780 jobs: &[crate::J2kTier1CodeBlockEncodeJob<'_>],
8781 ) -> core::result::Result<Option<Vec<crate::EncodedJ2kCodeBlock>>, &'static str>
8782 {
8783 self.tier1_code_block_batches += 1;
8784 self.tier1_batched_jobs += jobs.len();
8785 Ok(None)
8786 }
8787
8788 fn encode_packetization(
8789 &mut self,
8790 job: crate::J2kPacketizationEncodeJob<'_>,
8791 ) -> core::result::Result<Option<Vec<u8>>, &'static str> {
8792 self.packetization += 1;
8793 self.packetization_resolution_count = job.resolution_count;
8794 self.packetization_code_block_count = job.code_block_count;
8795 self.packetization_saw_payload = job
8796 .resolutions
8797 .iter()
8798 .flat_map(|resolution| resolution.subbands.iter())
8799 .flat_map(|subband| subband.code_blocks.iter())
8800 .any(|code_block| !code_block.data.is_empty());
8801 Ok(None)
8802 }
8803 }
8804
8805 let pixels: Vec<u8> = (0..8 * 8 * 3).map(|i| (i & 0xFF) as u8).collect();
8806 let options = EncodeOptions {
8807 num_decomposition_levels: 1,
8808 reversible: true,
8809 ..EncodeOptions::default()
8810 };
8811 let mut accelerator = CountingAccelerator::default();
8812
8813 let codestream =
8814 encode_with_accelerator(&pixels, 8, 8, 3, 8, false, &options, &mut accelerator)
8815 .expect("encode with accelerator hooks");
8816
8817 assert!(codestream.starts_with(&[0xFF, 0x4F]));
8818 assert_eq!(accelerator.forward_rct, 1);
8819 assert_eq!(accelerator.forward_dwt53, 3);
8820 assert!(accelerator.tier1_code_block_batches > 0);
8821 assert_eq!(
8822 accelerator.tier1_code_blocks,
8823 accelerator.tier1_batched_jobs
8824 );
8825 assert_eq!(accelerator.packetization, 1);
8826 assert_eq!(accelerator.packetization_resolution_count, 6);
8827 assert_eq!(
8828 accelerator.packetization_code_block_count,
8829 u32::try_from(accelerator.tier1_code_blocks).expect("test code-block count fits u32")
8830 );
8831 assert!(accelerator.packetization_saw_payload);
8832 }
8833
8834 #[test]
8835 fn cpu_only_accelerator_opts_into_parallel_block_fallback_only_for_native_cpu() {
8836 #[derive(Default)]
8837 struct ExternalAccelerator;
8838
8839 impl crate::J2kEncodeStageAccelerator for ExternalAccelerator {}
8840
8841 let cpu = crate::CpuOnlyJ2kEncodeStageAccelerator;
8842 let external = ExternalAccelerator;
8843
8844 assert!(cpu.prefer_parallel_cpu_code_block_fallback());
8845 assert!(!external.prefer_parallel_cpu_code_block_fallback());
8846 }
8847
8848 #[test]
8849 fn cpu_parallel_block_fallback_matches_serial_classic_and_htj2k_output() {
8850 #[derive(Default)]
8851 struct SerialCpuFallbackAccelerator;
8852
8853 impl crate::J2kEncodeStageAccelerator for SerialCpuFallbackAccelerator {}
8854
8855 let pixels = gradient_u8(96, 80);
8856 for use_ht_block_coding in [false, true] {
8857 let options = EncodeOptions {
8858 num_decomposition_levels: 1,
8859 code_block_width_exp: 2,
8860 code_block_height_exp: 2,
8861 use_ht_block_coding,
8862 ..EncodeOptions::default()
8863 };
8864 let parallel = encode(&pixels, 96, 80, 1, 8, false, &options)
8865 .expect("parallel CPU fallback encode");
8866 let mut serial_accelerator = SerialCpuFallbackAccelerator;
8867 let serial = encode_with_accelerator(
8868 &pixels,
8869 96,
8870 80,
8871 1,
8872 8,
8873 false,
8874 &options,
8875 &mut serial_accelerator,
8876 )
8877 .expect("serial CPU fallback encode");
8878
8879 assert_eq!(parallel, serial);
8880 }
8881 }
8882
8883 #[test]
8884 fn precomputed_htj2k53_offers_ht_code_blocks_to_encode_accelerator() {
8885 let image = sample_precomputed_htj2k53_image();
8886 let options = EncodeOptions {
8887 num_decomposition_levels: 1,
8888 reversible: true,
8889 guard_bits: 2,
8890 code_block_width_exp: 2,
8891 code_block_height_exp: 2,
8892 ..EncodeOptions::default()
8893 };
8894 let mut accelerator = CountingHtEncodeAccelerator::default();
8895
8896 let encoded =
8897 encode_precomputed_htj2k_53_with_accelerator(&image, &options, &mut accelerator)
8898 .expect("precomputed 5/3 encode accepts encode accelerator");
8899
8900 assert!(encoded.starts_with(&[0xff, 0x4f]));
8901 assert_eq!(accelerator.forward_dwt53, 0);
8902 assert_eq!(accelerator.forward_dwt97, 0);
8903 assert_eq!(accelerator.ht_batches, 1);
8904 assert!(accelerator.ht_jobs > 0);
8905 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
8906 }
8907
8908 #[test]
8909 fn precomputed_htj2k97_offers_ht_code_blocks_to_encode_accelerator() {
8910 let image = sample_precomputed_htj2k97_image();
8911 let options = EncodeOptions {
8912 num_decomposition_levels: 1,
8913 reversible: false,
8914 guard_bits: 2,
8915 code_block_width_exp: 2,
8916 code_block_height_exp: 2,
8917 ..EncodeOptions::default()
8918 };
8919 let mut accelerator = CountingHtEncodeAccelerator::default();
8920
8921 let encoded =
8922 encode_precomputed_htj2k_97_with_accelerator(&image, &options, &mut accelerator)
8923 .expect("precomputed 9/7 encode accepts encode accelerator");
8924
8925 assert!(encoded.starts_with(&[0xff, 0x4f]));
8926 assert_eq!(accelerator.forward_dwt53, 0);
8927 assert_eq!(accelerator.forward_dwt97, 0);
8928 assert_eq!(accelerator.ht_batches, 1);
8929 assert!(accelerator.ht_jobs > 0);
8930 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
8931 }
8932
8933 #[test]
8934 fn precomputed_dwt_geometry_validation_rejects_recursive_mismatch_for_both_filters() {
8935 let mut dwt53 = sample_precomputed_htj2k53_image();
8936 dwt53.components[0].dwt.levels[0].low_width += 1;
8937 assert_eq!(
8938 validate_precomputed_dwt_geometry(&dwt53),
8939 Err("precomputed DWT recursive geometry mismatch")
8940 );
8941
8942 let mut dwt97 = sample_precomputed_htj2k97_image();
8943 dwt97.components[0].dwt.levels[0].low_width += 1;
8944 assert_eq!(
8945 validate_precomputed_dwt97_geometry(&dwt97),
8946 Err("precomputed DWT recursive geometry mismatch")
8947 );
8948 }
8949
8950 #[test]
8951 fn prequantized_htj2k97_offers_ht_code_blocks_to_encode_accelerator() {
8952 let image = sample_precomputed_htj2k97_image();
8953 let options = EncodeOptions {
8954 num_decomposition_levels: 1,
8955 reversible: false,
8956 guard_bits: 2,
8957 code_block_width_exp: 2,
8958 code_block_height_exp: 2,
8959 ..EncodeOptions::default()
8960 };
8961 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
8962 .expect("test prequantized image");
8963 let mut accelerator = CountingHtEncodeAccelerator::default();
8964
8965 let encoded = encode_prequantized_htj2k_97_with_accelerator(
8966 &prequantized,
8967 &options,
8968 &mut accelerator,
8969 )
8970 .expect("prequantized 9/7 encode accepts encode accelerator");
8971
8972 assert!(encoded.starts_with(&[0xff, 0x4f]));
8973 assert_eq!(accelerator.forward_dwt53, 0);
8974 assert_eq!(accelerator.forward_dwt97, 0);
8975 assert_eq!(accelerator.ht_batches, 1);
8976 assert!(accelerator.ht_jobs > 0);
8977 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
8978 }
8979
8980 #[test]
8981 fn precomputed_htj2k97_batch_offers_all_ht_code_blocks_in_one_accelerator_call() {
8982 let image = sample_precomputed_htj2k97_image();
8983 let options = EncodeOptions {
8984 num_decomposition_levels: 1,
8985 reversible: false,
8986 guard_bits: 2,
8987 code_block_width_exp: 2,
8988 code_block_height_exp: 2,
8989 ..EncodeOptions::default()
8990 };
8991 let mut accelerator = CountingHtEncodeAccelerator::default();
8992
8993 let encoded = encode_precomputed_htj2k_97_batch_with_accelerator(
8994 &[image.clone(), image],
8995 &options,
8996 &mut accelerator,
8997 )
8998 .expect("batch precomputed 9/7 encode accepts encode accelerator");
8999
9000 assert_eq!(encoded.len(), 2);
9001 assert!(encoded
9002 .iter()
9003 .all(|codestream| codestream.starts_with(&[0xff, 0x4f])));
9004 assert_eq!(accelerator.forward_dwt53, 0);
9005 assert_eq!(accelerator.forward_dwt97, 0);
9006 assert_eq!(accelerator.ht_batches, 1);
9007 assert!(accelerator.ht_jobs > 0);
9008 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
9009 }
9010
9011 #[derive(Default)]
9012 struct CountingHtEncodeAccelerator {
9013 forward_dwt53: usize,
9014 forward_dwt97: usize,
9015 ht_batches: usize,
9016 ht_jobs: usize,
9017 ht_single_blocks: usize,
9018 }
9019
9020 impl crate::J2kEncodeStageAccelerator for CountingHtEncodeAccelerator {
9021 fn encode_forward_dwt53(
9022 &mut self,
9023 _job: crate::J2kForwardDwt53Job<'_>,
9024 ) -> core::result::Result<Option<crate::J2kForwardDwt53Output>, &'static str> {
9025 self.forward_dwt53 += 1;
9026 Ok(None)
9027 }
9028
9029 fn encode_forward_dwt97(
9030 &mut self,
9031 _job: crate::J2kForwardDwt97Job<'_>,
9032 ) -> core::result::Result<Option<crate::J2kForwardDwt97Output>, &'static str> {
9033 self.forward_dwt97 += 1;
9034 Ok(None)
9035 }
9036
9037 fn encode_ht_code_blocks(
9038 &mut self,
9039 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
9040 ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str> {
9041 self.ht_batches += 1;
9042 self.ht_jobs += jobs.len();
9043 Ok(None)
9044 }
9045
9046 fn encode_ht_code_block(
9047 &mut self,
9048 _job: crate::J2kHtCodeBlockEncodeJob<'_>,
9049 ) -> core::result::Result<Option<crate::EncodedHtJ2kCodeBlock>, &'static str> {
9050 self.ht_single_blocks += 1;
9051 Ok(None)
9052 }
9053 }
9054
9055 #[test]
9056 fn prepare_subband_uses_fused_ht_subband_without_host_quantized_codeblocks() {
9057 #[derive(Default)]
9058 struct FusedHtSubbandAccelerator {
9059 subband_calls: usize,
9060 quantize_calls: usize,
9061 ht_batch_calls: usize,
9062 }
9063
9064 impl crate::J2kEncodeStageAccelerator for FusedHtSubbandAccelerator {
9065 fn encode_ht_subband(
9066 &mut self,
9067 job: crate::J2kHtSubbandEncodeJob<'_>,
9068 ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str>
9069 {
9070 self.subband_calls += 1;
9071 let count = (job.width.div_ceil(job.code_block_width) as usize)
9072 .checked_mul(job.height.div_ceil(job.code_block_height) as usize)
9073 .ok_or("test code-block count overflow")?;
9074 Ok(Some(
9075 (0..count)
9076 .map(|idx| crate::EncodedHtJ2kCodeBlock {
9077 data: vec![u8::try_from(idx).expect("test block index fits"), 0],
9078 cleanup_length: 2,
9079 refinement_length: 0,
9080 num_coding_passes: 1,
9081 num_zero_bitplanes: 0,
9082 })
9083 .collect(),
9084 ))
9085 }
9086
9087 fn encode_quantize_subband(
9088 &mut self,
9089 _job: crate::J2kQuantizeSubbandJob<'_>,
9090 ) -> core::result::Result<Option<Vec<i32>>, &'static str> {
9091 self.quantize_calls += 1;
9092 Ok(None)
9093 }
9094
9095 fn encode_ht_code_blocks(
9096 &mut self,
9097 _jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
9098 ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str>
9099 {
9100 self.ht_batch_calls += 1;
9101 Ok(None)
9102 }
9103 }
9104
9105 let coefficients = vec![0.0; 16];
9106 let mut accelerator = FusedHtSubbandAccelerator::default();
9107 let prepared = prepare_subband(
9108 &coefficients,
9109 4,
9110 4,
9111 &QuantStepSize {
9112 exponent: 8,
9113 mantissa: 0,
9114 },
9115 8,
9116 2,
9117 true,
9118 BlockCodingMode::HighThroughput,
9119 2,
9120 2,
9121 SubBandType::LowLow,
9122 0,
9123 &[],
9124 1,
9125 1,
9126 &mut accelerator,
9127 )
9128 .expect("fused HT subband prepare");
9129
9130 assert_eq!(accelerator.subband_calls, 1);
9131 assert_eq!(accelerator.quantize_calls, 0);
9132 assert!(prepared.preencoded_ht_code_blocks.is_some());
9133 assert!(prepared
9134 .code_blocks
9135 .iter()
9136 .all(|block| block.coefficients.is_empty()));
9137
9138 let precincts = encode_prepared_subbands(vec![prepared], &mut accelerator)
9139 .expect("preencoded HT subband packet data");
9140
9141 assert_eq!(accelerator.ht_batch_calls, 0);
9142 assert_eq!(precincts[0].code_blocks.len(), 4);
9143 assert_eq!(precincts[0].code_blocks[2].data, vec![2, 0]);
9144 }
9145
9146 #[test]
9147 fn ht_target_coding_passes_tracks_ht_quality_layers() {
9148 let mut options = EncodeOptions {
9149 use_ht_block_coding: true,
9150 reversible: false,
9151 num_layers: 1,
9152 ..EncodeOptions::default()
9153 };
9154
9155 assert_eq!(ht_target_coding_passes_for_options(&options), 1);
9156
9157 options.num_layers = 2;
9158 assert_eq!(ht_target_coding_passes_for_options(&options), 2);
9159
9160 options.num_layers = 3;
9161 assert_eq!(ht_target_coding_passes_for_options(&options), 3);
9162
9163 options.num_layers = 4;
9164 assert_eq!(ht_target_coding_passes_for_options(&options), 3);
9165
9166 options.reversible = true;
9167 assert_eq!(ht_target_coding_passes_for_options(&options), 3);
9168
9169 options.reversible = false;
9170 options.use_ht_block_coding = false;
9171 assert_eq!(ht_target_coding_passes_for_options(&options), 1);
9172 }
9173
9174 #[test]
9175 fn packet_header_validation_allows_chunked_ppm_and_ppt_payloads() {
9176 const MARKER_PAYLOAD_LIMIT: usize = u16::MAX as usize - 3;
9177 let ppm_headers = vec![vec![0_u8; MARKER_PAYLOAD_LIMIT - 2], vec![1_u8; 1]];
9178 let ppt_headers = vec![vec![2_u8; MARKER_PAYLOAD_LIMIT + 1]];
9179
9180 validate_packet_header_marker_payloads(true, false, &[&ppm_headers])
9181 .expect("chunked PPM payload should validate");
9182 validate_packet_header_marker_payloads(false, true, &[&ppt_headers])
9183 .expect("chunked PPT payload should validate");
9184 }
9185
9186 #[test]
9187 fn ht_cpu_fallback_encodes_two_pass_sigprop_refinement() {
9188 let coefficients: Vec<i32> = (0usize..64 * 64)
9189 .map(|index| {
9190 let value = ((((index * 31) ^ (index / 3)) & 0x00ff) as i32 - 127) * 2;
9191 if index.is_multiple_of(11) {
9192 0
9193 } else {
9194 value
9195 }
9196 })
9197 .collect();
9198 let jobs = [crate::J2kHtCodeBlockEncodeJob {
9199 coefficients: &coefficients,
9200 width: 64,
9201 height: 64,
9202 total_bitplanes: 10,
9203 target_coding_passes: 2,
9204 }];
9205
9206 let encoded = encode_all_ht_code_blocks_serial_cpu(&jobs).expect("two-pass CPU HT encode");
9207
9208 assert_eq!(encoded.len(), 1);
9209 assert_eq!(encoded[0].num_coding_passes, 2);
9210 assert_eq!(encoded[0].ht_refinement_length, 48);
9211 assert_eq!(
9212 encoded[0].data.len(),
9213 encoded[0].ht_cleanup_length as usize + encoded[0].ht_refinement_length as usize
9214 );
9215 assert!(encoded[0].data[encoded[0].ht_cleanup_length as usize..]
9216 .iter()
9217 .all(|byte| *byte == 0));
9218
9219 let segments = crate::j2c::ht_block_decode::HtCodeBlockSegments::from_combined_payload(
9220 &encoded[0].data,
9221 encoded[0].ht_cleanup_length,
9222 encoded[0].ht_refinement_length,
9223 )
9224 .expect("split HT segments");
9225 let mut decoded = vec![0u32; coefficients.len()];
9226 crate::j2c::ht_block_decode::decode_segments_validated(
9227 &segments,
9228 encoded[0].num_zero_bitplanes,
9229 10,
9230 encoded[0].num_coding_passes,
9231 false,
9232 true,
9233 &mut decoded,
9234 64,
9235 64,
9236 64,
9237 )
9238 .expect("decode two-pass HT block");
9239 let decoded_i32 = decoded
9240 .into_iter()
9241 .map(|value| crate::j2c::ht_block_decode::coefficient_to_i32(value, 10))
9242 .collect::<Vec<_>>();
9243 let max_abs_delta = decoded_i32
9244 .iter()
9245 .zip(&coefficients)
9246 .map(|(actual, expected)| actual.abs_diff(*expected))
9247 .max()
9248 .unwrap_or(0);
9249
9250 assert!(
9251 max_abs_delta <= 1,
9252 "two-pass HT sigprop decode must stay within one coefficient LSB"
9253 );
9254 }
9255
9256 #[test]
9257 fn ht_cpu_fallback_sigprop_refinement_encodes_new_significance_bits() {
9258 let mut coefficients = vec![0_i32; 8 * 8];
9259 for row in 0..8 {
9260 coefficients[row * 8] = 3;
9261 coefficients[row * 8 + 1] = 1;
9262 coefficients[row * 8 + 2] = -1;
9263 }
9264 let jobs = [crate::J2kHtCodeBlockEncodeJob {
9265 coefficients: &coefficients,
9266 width: 8,
9267 height: 8,
9268 total_bitplanes: 4,
9269 target_coding_passes: 2,
9270 }];
9271
9272 let encoded = encode_all_ht_code_blocks_serial_cpu(&jobs).expect("two-pass CPU HT encode");
9273
9274 assert_eq!(encoded[0].num_coding_passes, 2);
9275 assert!(encoded[0].ht_refinement_length > 0);
9276 assert!(
9277 encoded[0].data[encoded[0].ht_cleanup_length as usize..]
9278 .iter()
9279 .any(|byte| *byte != 0),
9280 "sigprop refinement should encode new significance/sign bits"
9281 );
9282
9283 let segments = crate::j2c::ht_block_decode::HtCodeBlockSegments::from_combined_payload(
9284 &encoded[0].data,
9285 encoded[0].ht_cleanup_length,
9286 encoded[0].ht_refinement_length,
9287 )
9288 .expect("split HT segments");
9289 let mut decoded = vec![0u32; coefficients.len()];
9290 crate::j2c::ht_block_decode::decode_segments_validated(
9291 &segments,
9292 encoded[0].num_zero_bitplanes,
9293 4,
9294 encoded[0].num_coding_passes,
9295 false,
9296 true,
9297 &mut decoded,
9298 8,
9299 8,
9300 8,
9301 )
9302 .expect("decode two-pass HT block");
9303 let decoded_i32 = decoded
9304 .into_iter()
9305 .map(|value| crate::j2c::ht_block_decode::coefficient_to_i32(value, 4))
9306 .collect::<Vec<_>>();
9307
9308 assert_eq!(decoded_i32, coefficients);
9309 }
9310
9311 #[test]
9312 fn ht_cpu_fallback_encodes_three_pass_magref_refinement() {
9313 let mut coefficients = vec![0_i32; 8 * 8];
9314 for row in 0..8 {
9315 let base = row * 8;
9316 coefficients[base] = 2;
9317 coefficients[base + 1] = 3;
9318 coefficients[base + 2] = 1;
9319 coefficients[base + 3] = -1;
9320 coefficients[base + 4] = -2;
9321 coefficients[base + 5] = -3;
9322 }
9323 let jobs = [crate::J2kHtCodeBlockEncodeJob {
9324 coefficients: &coefficients,
9325 width: 8,
9326 height: 8,
9327 total_bitplanes: 4,
9328 target_coding_passes: 3,
9329 }];
9330
9331 let encoded =
9332 encode_all_ht_code_blocks_serial_cpu(&jobs).expect("three-pass CPU HT encode");
9333
9334 assert_eq!(encoded[0].num_coding_passes, 3);
9335 assert!(encoded[0].ht_refinement_length > 0);
9336
9337 let segments = crate::j2c::ht_block_decode::HtCodeBlockSegments::from_combined_payload(
9338 &encoded[0].data,
9339 encoded[0].ht_cleanup_length,
9340 encoded[0].ht_refinement_length,
9341 )
9342 .expect("split HT segments");
9343 let mut decoded = vec![0u32; coefficients.len()];
9344 crate::j2c::ht_block_decode::decode_segments_validated(
9345 &segments,
9346 encoded[0].num_zero_bitplanes,
9347 4,
9348 encoded[0].num_coding_passes,
9349 false,
9350 true,
9351 &mut decoded,
9352 8,
9353 8,
9354 8,
9355 )
9356 .expect("decode three-pass HT block");
9357 let decoded_i32 = decoded
9358 .into_iter()
9359 .map(|value| crate::j2c::ht_block_decode::coefficient_to_i32(value, 4))
9360 .collect::<Vec<_>>();
9361
9362 assert_eq!(decoded_i32, coefficients);
9363 }
9364
9365 #[test]
9366 fn ht_cpu_fallback_rejects_unsupported_refinement_pass_count() {
9367 let coefficients = vec![1_i32; 64 * 64];
9368 let jobs = [crate::J2kHtCodeBlockEncodeJob {
9369 coefficients: &coefficients,
9370 width: 64,
9371 height: 64,
9372 total_bitplanes: 2,
9373 target_coding_passes: 4,
9374 }];
9375
9376 let err = encode_all_ht_code_blocks_serial_cpu(&jobs)
9377 .expect_err("CPU HT encode must reject unsupported pass requests");
9378
9379 assert!(err.contains("at most three HT coding passes"));
9380 }
9381
9382 #[test]
9383 fn ht_cpu_parallel_fallback_threshold_matches_parallel_output() {
9384 assert_eq!(HT_CPU_PARALLEL_FALLBACK_MIN_JOBS, 4);
9385
9386 let blocks: Vec<Vec<i32>> = (0..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS)
9387 .map(|seed| {
9388 (0usize..64 * 64)
9389 .map(|index| {
9390 let value = (((index * 31) ^ (seed * 17)) & 0x01ff) as i32 - 255;
9391 if (index + seed).is_multiple_of(11) {
9392 0
9393 } else {
9394 value
9395 }
9396 })
9397 .collect()
9398 })
9399 .collect();
9400 let jobs: Vec<_> = blocks
9401 .iter()
9402 .map(|coefficients| crate::J2kHtCodeBlockEncodeJob {
9403 coefficients,
9404 width: 64,
9405 height: 64,
9406 total_bitplanes: 10,
9407 target_coding_passes: 1,
9408 })
9409 .collect();
9410
9411 let serial =
9412 encode_all_ht_code_blocks_serial_cpu(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS - 1])
9413 .expect("serial tiny HT encode");
9414 let parallel =
9415 encode_all_ht_code_blocks_parallel(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS])
9416 .expect("parallel HT encode");
9417 let serial_threshold =
9418 encode_all_ht_code_blocks_serial_cpu(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS])
9419 .expect("serial threshold HT encode");
9420
9421 assert_eq!(serial.len(), HT_CPU_PARALLEL_FALLBACK_MIN_JOBS - 1);
9422 assert_eq!(parallel.len(), HT_CPU_PARALLEL_FALLBACK_MIN_JOBS);
9423 assert_eq!(serial_threshold.len(), parallel.len());
9424 for (serial, parallel) in serial_threshold.iter().zip(¶llel) {
9425 assert_eq!(serial.data, parallel.data);
9426 assert_eq!(serial.num_coding_passes, parallel.num_coding_passes);
9427 assert_eq!(serial.num_zero_bitplanes, parallel.num_zero_bitplanes);
9428 }
9429 }
9430
9431 #[test]
9432 fn code_block_extraction_copies_partial_edge_blocks_rowwise() {
9433 let quantized: Vec<i32> = (0..20).collect();
9434
9435 let block = copy_code_block_coefficients(&quantized, 5, 3, 1, 2, 3);
9436
9437 assert_eq!(block, vec![8, 9, 13, 14, 18, 19]);
9438 }
9439
9440 #[test]
9441 fn test_encode_lossy() {
9442 let pixels: Vec<u8> = (0..64).collect();
9443
9444 let result = encode(
9445 &pixels,
9446 8,
9447 8,
9448 1,
9449 8,
9450 false,
9451 &EncodeOptions {
9452 num_decomposition_levels: 2,
9453 reversible: false,
9454 guard_bits: 2,
9455 ..Default::default()
9456 },
9457 );
9458
9459 assert!(result.is_ok());
9460 }
9461
9462 #[test]
9463 fn prequantized_htj2k97_matches_precomputed_dwt97_codestream() {
9464 let image = sample_precomputed_htj2k97_image();
9465 let options = EncodeOptions {
9466 num_decomposition_levels: 1,
9467 reversible: false,
9468 guard_bits: 2,
9469 code_block_width_exp: 2,
9470 code_block_height_exp: 2,
9471 ..EncodeOptions::default()
9472 };
9473
9474 let precomputed =
9475 encode_precomputed_htj2k_97(&image, &options).expect("precomputed DWT encode");
9476 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
9477 .expect("test prequantized image");
9478 let direct =
9479 encode_prequantized_htj2k_97(&prequantized, &options).expect("prequantized encode");
9480
9481 assert_eq!(direct, precomputed);
9482 }
9483
9484 #[test]
9485 fn preencoded_htj2k97_matches_prequantized_codestream() {
9486 let image = sample_precomputed_htj2k97_image();
9487 let options = EncodeOptions {
9488 num_decomposition_levels: 1,
9489 reversible: false,
9490 guard_bits: 2,
9491 code_block_width_exp: 2,
9492 code_block_height_exp: 2,
9493 ..EncodeOptions::default()
9494 };
9495 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
9496 .expect("test prequantized image");
9497 let expected =
9498 encode_prequantized_htj2k_97(&prequantized, &options).expect("prequantized encode");
9499 let preencoded = preencoded_htj2k97_image_from_prequantized_for_test(&prequantized)
9500 .expect("test preencoded image");
9501 let actual = encode_preencoded_htj2k_97(&preencoded, &options).expect("preencoded encode");
9502
9503 assert_eq!(actual, expected);
9504 }
9505
9506 #[test]
9507 fn preencoded_htj2k97_preserves_refinement_segments_in_packet_body() {
9508 let options = EncodeOptions {
9509 num_decomposition_levels: 0,
9510 reversible: false,
9511 guard_bits: 2,
9512 code_block_width_exp: 2,
9513 code_block_height_exp: 2,
9514 ..EncodeOptions::default()
9515 };
9516 let guard_bits = options.guard_bits.max(2);
9517 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
9518 8,
9519 0,
9520 false,
9521 guard_bits,
9522 options.irreversible_quantization_scale,
9523 options.irreversible_quantization_subband_scales,
9524 );
9525 let total_bitplanes = guard_bits
9526 .saturating_add(step_sizes[0].exponent as u8)
9527 .saturating_sub(1);
9528 let payload = [0x12, 0x34, 0x56, 0x78];
9529 let image = PreencodedHtj2k97Image {
9530 width: 1,
9531 height: 1,
9532 bit_depth: 8,
9533 signed: false,
9534 components: vec![PreencodedHtj2k97Component {
9535 x_rsiz: 1,
9536 y_rsiz: 1,
9537 resolutions: vec![PreencodedHtj2k97Resolution {
9538 subbands: vec![PreencodedHtj2k97Subband {
9539 sub_band_type: J2kSubBandType::LowLow,
9540 num_cbs_x: 1,
9541 num_cbs_y: 1,
9542 total_bitplanes,
9543 code_blocks: vec![PreencodedHtj2k97CodeBlock {
9544 width: 1,
9545 height: 1,
9546 encoded: EncodedHtJ2kCodeBlock {
9547 data: payload.to_vec(),
9548 cleanup_length: 2,
9549 refinement_length: 2,
9550 num_coding_passes: 3,
9551 num_zero_bitplanes: 0,
9552 },
9553 }],
9554 }],
9555 }],
9556 }],
9557 };
9558
9559 let codestream =
9560 encode_preencoded_htj2k_97(&image, &options).expect("preencoded refinement encode");
9561 let eoc = codestream
9562 .windows(2)
9563 .rposition(|marker| marker == [0xff, crate::j2c::codestream::markers::EOC])
9564 .expect("EOC marker");
9565
9566 assert_eq!(&codestream[eoc - payload.len()..eoc], payload);
9567 }
9568
9569 #[test]
9570 fn preencoded_htj2k97_rejects_empty_block_with_wrong_zero_bitplanes() {
9571 let (mut image, options) = sample_preencoded_htj2k97_for_test();
9572 let block = &mut image.components[0].resolutions[0].subbands[0].code_blocks[0];
9573 block.encoded = EncodedHtJ2kCodeBlock {
9574 data: Vec::new(),
9575 cleanup_length: 0,
9576 refinement_length: 0,
9577 num_coding_passes: 0,
9578 num_zero_bitplanes: 0,
9579 };
9580
9581 let error = encode_preencoded_htj2k_97(&image, &options)
9582 .expect_err("invalid all-zero block metadata must be rejected");
9583
9584 assert_eq!(error, "empty HTJ2K code-block zero-bitplane count mismatch");
9585 }
9586
9587 #[test]
9588 fn preencoded_htj2k97_rejects_coded_block_with_too_many_zero_bitplanes() {
9589 let (mut image, options) = sample_preencoded_htj2k97_for_test();
9590 let subband = &mut image.components[0].resolutions[0].subbands[0];
9591 subband.code_blocks[0].encoded.num_zero_bitplanes = subband.total_bitplanes;
9592
9593 let error = encode_preencoded_htj2k_97(&image, &options)
9594 .expect_err("coded block with no coded bitplanes must be rejected");
9595
9596 assert_eq!(error, "HTJ2K code-block zero-bitplane count out of range");
9597 }
9598
9599 #[cfg(feature = "std")]
9600 #[test]
9601 fn preencoded_htj2k97_rejects_too_many_coding_passes_without_panic() {
9602 let (mut image, options) = sample_preencoded_htj2k97_for_test();
9603 image.components[0].resolutions[0].subbands[0].code_blocks[0]
9604 .encoded
9605 .num_coding_passes = 165;
9606
9607 let result = std::panic::catch_unwind(|| encode_preencoded_htj2k_97(&image, &options));
9608
9609 assert!(result.is_ok(), "invalid coding pass count must not panic");
9610 assert_eq!(
9611 result.expect("catch_unwind returned checked result"),
9612 Err("HTJ2K code-block coding pass count out of range")
9613 );
9614 }
9615
9616 #[test]
9617 fn prequantized_htj2k97_accepts_empty_high_subbands() {
9618 let options = EncodeOptions {
9619 num_decomposition_levels: 1,
9620 reversible: false,
9621 guard_bits: 2,
9622 code_block_width_exp: 2,
9623 code_block_height_exp: 2,
9624 ..EncodeOptions::default()
9625 };
9626 let image = PrequantizedHtj2k97Image {
9627 width: 1,
9628 height: 1,
9629 bit_depth: 8,
9630 signed: false,
9631 components: vec![PrequantizedHtj2k97Component {
9632 x_rsiz: 1,
9633 y_rsiz: 1,
9634 resolutions: vec![
9635 PrequantizedHtj2k97Resolution {
9636 subbands: vec![PrequantizedHtj2k97Subband {
9637 sub_band_type: J2kSubBandType::LowLow,
9638 num_cbs_x: 1,
9639 num_cbs_y: 1,
9640 total_bitplanes: 11,
9641 code_blocks: vec![PrequantizedHtj2k97CodeBlock {
9642 coefficients: vec![0],
9643 width: 1,
9644 height: 1,
9645 }],
9646 }],
9647 },
9648 PrequantizedHtj2k97Resolution {
9649 subbands: vec![
9650 empty_prequantized_subband(J2kSubBandType::HighLow),
9651 empty_prequantized_subband(J2kSubBandType::LowHigh),
9652 empty_prequantized_subband(J2kSubBandType::HighHigh),
9653 ],
9654 },
9655 ],
9656 }],
9657 };
9658
9659 let encoded =
9660 encode_prequantized_htj2k_97(&image, &options).expect("empty high subbands encode");
9661
9662 assert!(encoded.starts_with(&[0xff, 0x4f]));
9663 }
9664
9665 fn empty_prequantized_subband(sub_band_type: J2kSubBandType) -> PrequantizedHtj2k97Subband {
9666 PrequantizedHtj2k97Subband {
9667 sub_band_type,
9668 num_cbs_x: 0,
9669 num_cbs_y: 0,
9670 total_bitplanes: 0,
9671 code_blocks: Vec::new(),
9672 }
9673 }
9674
9675 fn sample_precomputed_htj2k97_image() -> PrecomputedHtj2k97Image {
9676 let width = 17u32;
9677 let height = 13u32;
9678 let low_width = width.div_ceil(2);
9679 let low_height = height.div_ceil(2);
9680 let high_width = width / 2;
9681 let high_height = height / 2;
9682
9683 PrecomputedHtj2k97Image {
9684 width,
9685 height,
9686 bit_depth: 8,
9687 signed: false,
9688 components: vec![PrecomputedHtj2k97Component {
9689 x_rsiz: 1,
9690 y_rsiz: 1,
9691 dwt: J2kForwardDwt97Output {
9692 ll: sample_f32_coefficients(low_width * low_height, 0.25),
9693 ll_width: low_width,
9694 ll_height: low_height,
9695 levels: vec![J2kForwardDwt97Level {
9696 hl: sample_f32_coefficients(high_width * low_height, -0.75),
9697 lh: sample_f32_coefficients(low_width * high_height, 1.25),
9698 hh: sample_f32_coefficients(high_width * high_height, -1.5),
9699 width,
9700 height,
9701 low_width,
9702 low_height,
9703 high_width,
9704 high_height,
9705 }],
9706 },
9707 }],
9708 }
9709 }
9710
9711 fn sample_precomputed_htj2k53_image() -> PrecomputedHtj2k53Image {
9712 let width = 17u32;
9713 let height = 13u32;
9714 let low_width = width.div_ceil(2);
9715 let low_height = height.div_ceil(2);
9716 let high_width = width / 2;
9717 let high_height = height / 2;
9718
9719 PrecomputedHtj2k53Image {
9720 width,
9721 height,
9722 bit_depth: 8,
9723 signed: false,
9724 components: vec![PrecomputedHtj2k53Component {
9725 x_rsiz: 1,
9726 y_rsiz: 1,
9727 dwt: J2kForwardDwt53Output {
9728 ll: sample_f32_coefficients(low_width * low_height, 0.0),
9729 ll_width: low_width,
9730 ll_height: low_height,
9731 levels: vec![J2kForwardDwt53Level {
9732 hl: sample_f32_coefficients(high_width * low_height, -2.0),
9733 lh: sample_f32_coefficients(low_width * high_height, 2.0),
9734 hh: sample_f32_coefficients(high_width * high_height, -4.0),
9735 width,
9736 height,
9737 low_width,
9738 low_height,
9739 high_width,
9740 high_height,
9741 }],
9742 },
9743 }],
9744 }
9745 }
9746
9747 fn sample_f32_coefficients(len: u32, offset: f32) -> Vec<f32> {
9748 (0..len)
9749 .map(|idx| ((idx % 17) as f32 - 8.0) * 0.5 + offset)
9750 .collect()
9751 }
9752
9753 fn prequantized_htj2k97_image_from_precomputed_for_test(
9754 image: &PrecomputedHtj2k97Image,
9755 options: &EncodeOptions,
9756 ) -> Result<PrequantizedHtj2k97Image, &'static str> {
9757 let guard_bits = options.guard_bits.max(2);
9758 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
9759 image.bit_depth,
9760 1,
9761 false,
9762 guard_bits,
9763 options.irreversible_quantization_scale,
9764 options.irreversible_quantization_subband_scales,
9765 );
9766 let cb_width = 1u32 << (options.code_block_width_exp + 2);
9767 let cb_height = 1u32 << (options.code_block_height_exp + 2);
9768
9769 let components = image
9770 .components
9771 .iter()
9772 .map(|component| {
9773 let mut resolutions = Vec::with_capacity(component.dwt.levels.len() + 1);
9774 resolutions.push(PrequantizedHtj2k97Resolution {
9775 subbands: vec![prequantized_subband_for_test(
9776 &component.dwt.ll,
9777 component.dwt.ll_width,
9778 component.dwt.ll_height,
9779 SubBandType::LowLow,
9780 &step_sizes[0],
9781 image.bit_depth,
9782 guard_bits,
9783 cb_width,
9784 cb_height,
9785 )?],
9786 });
9787
9788 for (level_index, level) in component.dwt.levels.iter().enumerate() {
9789 let step_base = 1 + level_index * 3;
9790 resolutions.push(PrequantizedHtj2k97Resolution {
9791 subbands: vec![
9792 prequantized_subband_for_test(
9793 &level.hl,
9794 level.high_width,
9795 level.low_height,
9796 SubBandType::HighLow,
9797 &step_sizes[step_base],
9798 image.bit_depth,
9799 guard_bits,
9800 cb_width,
9801 cb_height,
9802 )?,
9803 prequantized_subband_for_test(
9804 &level.lh,
9805 level.low_width,
9806 level.high_height,
9807 SubBandType::LowHigh,
9808 &step_sizes[step_base + 1],
9809 image.bit_depth,
9810 guard_bits,
9811 cb_width,
9812 cb_height,
9813 )?,
9814 prequantized_subband_for_test(
9815 &level.hh,
9816 level.high_width,
9817 level.high_height,
9818 SubBandType::HighHigh,
9819 &step_sizes[step_base + 2],
9820 image.bit_depth,
9821 guard_bits,
9822 cb_width,
9823 cb_height,
9824 )?,
9825 ],
9826 });
9827 }
9828
9829 Ok(PrequantizedHtj2k97Component {
9830 x_rsiz: component.x_rsiz,
9831 y_rsiz: component.y_rsiz,
9832 resolutions,
9833 })
9834 })
9835 .collect::<Result<Vec<_>, &'static str>>()?;
9836
9837 Ok(PrequantizedHtj2k97Image {
9838 width: image.width,
9839 height: image.height,
9840 bit_depth: image.bit_depth,
9841 signed: image.signed,
9842 components,
9843 })
9844 }
9845
9846 #[allow(clippy::too_many_arguments)]
9847 fn prequantized_subband_for_test(
9848 coefficients: &[f32],
9849 width: u32,
9850 height: u32,
9851 sub_band_type: SubBandType,
9852 step_size: &QuantStepSize,
9853 bit_depth: u8,
9854 guard_bits: u8,
9855 cb_width: u32,
9856 cb_height: u32,
9857 ) -> Result<PrequantizedHtj2k97Subband, &'static str> {
9858 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
9859 let prepared = prepare_subband(
9860 coefficients,
9861 width,
9862 height,
9863 step_size,
9864 bit_depth,
9865 guard_bits,
9866 false,
9867 BlockCodingMode::HighThroughput,
9868 cb_width,
9869 cb_height,
9870 sub_band_type,
9871 0,
9872 &[],
9873 1,
9874 1,
9875 &mut accelerator,
9876 )?;
9877
9878 Ok(PrequantizedHtj2k97Subband {
9879 sub_band_type: public_sub_band_type(sub_band_type),
9880 num_cbs_x: prepared.num_cbs_x,
9881 num_cbs_y: prepared.num_cbs_y,
9882 total_bitplanes: prepared.total_bitplanes,
9883 code_blocks: prepared
9884 .code_blocks
9885 .into_iter()
9886 .map(|block| {
9887 Ok(PrequantizedHtj2k97CodeBlock {
9888 coefficients: downcast_i64_coefficients_to_i32(&block.coefficients)?,
9889 width: block.width,
9890 height: block.height,
9891 })
9892 })
9893 .collect::<Result<Vec<_>, &'static str>>()?,
9894 })
9895 }
9896
9897 fn preencoded_htj2k97_image_from_prequantized_for_test(
9898 image: &PrequantizedHtj2k97Image,
9899 ) -> Result<PreencodedHtj2k97Image, &'static str> {
9900 let components = image
9901 .components
9902 .iter()
9903 .map(|component| {
9904 Ok(PreencodedHtj2k97Component {
9905 x_rsiz: component.x_rsiz,
9906 y_rsiz: component.y_rsiz,
9907 resolutions: component
9908 .resolutions
9909 .iter()
9910 .map(|resolution| {
9911 Ok(PreencodedHtj2k97Resolution {
9912 subbands: resolution
9913 .subbands
9914 .iter()
9915 .map(preencoded_subband_from_prequantized_for_test)
9916 .collect::<Result<Vec<_>, &'static str>>()?,
9917 })
9918 })
9919 .collect::<Result<Vec<_>, &'static str>>()?,
9920 })
9921 })
9922 .collect::<Result<Vec<_>, &'static str>>()?;
9923
9924 Ok(PreencodedHtj2k97Image {
9925 width: image.width,
9926 height: image.height,
9927 bit_depth: image.bit_depth,
9928 signed: image.signed,
9929 components,
9930 })
9931 }
9932
9933 fn sample_preencoded_htj2k97_for_test() -> (PreencodedHtj2k97Image, EncodeOptions) {
9934 let image = sample_precomputed_htj2k97_image();
9935 let options = EncodeOptions {
9936 num_decomposition_levels: 1,
9937 reversible: false,
9938 guard_bits: 2,
9939 code_block_width_exp: 2,
9940 code_block_height_exp: 2,
9941 ..EncodeOptions::default()
9942 };
9943 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
9944 .expect("test prequantized image");
9945 let preencoded = preencoded_htj2k97_image_from_prequantized_for_test(&prequantized)
9946 .expect("test preencoded image");
9947 (preencoded, options)
9948 }
9949
9950 fn preencoded_subband_from_prequantized_for_test(
9951 subband: &PrequantizedHtj2k97Subband,
9952 ) -> Result<PreencodedHtj2k97Subband, &'static str> {
9953 let code_blocks = subband
9954 .code_blocks
9955 .iter()
9956 .map(|block| {
9957 let encoded = ht_block_encode::encode_code_block(
9958 &block.coefficients,
9959 block.width,
9960 block.height,
9961 subband.total_bitplanes,
9962 )?;
9963 Ok(PreencodedHtj2k97CodeBlock {
9964 width: block.width,
9965 height: block.height,
9966 encoded: EncodedHtJ2kCodeBlock {
9967 data: encoded.data,
9968 cleanup_length: encoded.ht_cleanup_length,
9969 refinement_length: encoded.ht_refinement_length,
9970 num_coding_passes: encoded.num_coding_passes,
9971 num_zero_bitplanes: encoded.num_zero_bitplanes,
9972 },
9973 })
9974 })
9975 .collect::<Result<Vec<_>, &'static str>>()?;
9976
9977 Ok(PreencodedHtj2k97Subband {
9978 sub_band_type: subband.sub_band_type,
9979 num_cbs_x: subband.num_cbs_x,
9980 num_cbs_y: subband.num_cbs_y,
9981 total_bitplanes: subband.total_bitplanes,
9982 code_blocks,
9983 })
9984 }
9985
9986 fn assert_htj2k_lossless_roundtrip(
9987 pixels: &[u8],
9988 width: u32,
9989 height: u32,
9990 bit_depth: u8,
9991 num_decomposition_levels: u8,
9992 ) {
9993 let codestream = encode_htj2k(
9994 pixels,
9995 width,
9996 height,
9997 1,
9998 bit_depth,
9999 false,
10000 &EncodeOptions {
10001 num_decomposition_levels,
10002 ..Default::default()
10003 },
10004 )
10005 .expect("HTJ2K encode");
10006
10007 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10008 let cod_offset = codestream
10009 .windows(2)
10010 .position(|window| window == [0xFF, 0x52])
10011 .expect("COD marker");
10012 assert_eq!(codestream[cod_offset + 12], 0x40);
10013
10014 let image = Image::new(
10015 &codestream,
10016 &DecodeSettings {
10017 resolve_palette_indices: true,
10018 strict: true,
10019 target_resolution: None,
10020 },
10021 )
10022 .expect("parse HT codestream");
10023 let decoded = image.decode_native().expect("decode HT codestream");
10024
10025 assert_eq!(decoded.width, width);
10026 assert_eq!(decoded.height, height);
10027 assert_eq!(decoded.bit_depth, bit_depth);
10028 assert_eq!(decoded.data, pixels);
10029 }
10030
10031 fn gradient_u8(width: u32, height: u32) -> Vec<u8> {
10032 let mut pixels = Vec::with_capacity((width * height) as usize);
10033 for y in 0..height {
10034 for x in 0..width {
10035 pixels.push(((x * 17 + y * 31) % 256) as u8);
10036 }
10037 }
10038 pixels
10039 }
10040
10041 fn lossy_htj2k_roundtrip_u8(
10042 pixels: &[u8],
10043 width: u32,
10044 height: u32,
10045 num_decomposition_levels: u8,
10046 ) -> (Vec<u8>, usize) {
10047 let codestream = encode_htj2k(
10048 pixels,
10049 width,
10050 height,
10051 1,
10052 8,
10053 false,
10054 &EncodeOptions {
10055 num_decomposition_levels,
10056 reversible: false,
10057 guard_bits: 2,
10058 ..Default::default()
10059 },
10060 )
10061 .expect("lossy HT encode");
10062
10063 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10064
10065 let image = Image::new(
10066 &codestream,
10067 &DecodeSettings {
10068 resolve_palette_indices: true,
10069 strict: true,
10070 target_resolution: None,
10071 },
10072 )
10073 .expect("parse lossy HT codestream");
10074 let decoded = image.decode_native().expect("decode lossy HT codestream");
10075
10076 assert_eq!(decoded.width, width);
10077 assert_eq!(decoded.height, height);
10078 assert_eq!(decoded.bit_depth, 8);
10079
10080 (decoded.data, codestream.len())
10081 }
10082
10083 fn max_abs_error(expected: &[u8], actual: &[u8]) -> u8 {
10084 expected
10085 .iter()
10086 .zip(actual)
10087 .map(|(&expected, &actual)| expected.abs_diff(actual))
10088 .max()
10089 .unwrap_or(0)
10090 }
10091
10092 fn psnr_db(expected: &[u8], actual: &[u8]) -> f64 {
10093 let mse = expected
10094 .iter()
10095 .zip(actual)
10096 .map(|(&expected, &actual)| {
10097 let diff = f64::from(expected) - f64::from(actual);
10098 diff * diff
10099 })
10100 .sum::<f64>()
10101 / expected.len() as f64;
10102
10103 if mse == 0.0 {
10104 f64::INFINITY
10105 } else {
10106 20.0 * 255.0f64.log10() - 10.0 * mse.log10()
10107 }
10108 }
10109
10110 fn assert_not_flat_128(decoded: &[u8]) {
10111 assert!(
10112 decoded.iter().any(|&sample| sample != 128),
10113 "lossy decode collapsed to flat 128"
10114 );
10115 }
10116
10117 #[test]
10118 fn test_encode_high_throughput_zero_image_roundtrip() {
10119 let width = 4u32;
10120 let height = 4u32;
10121 let sample = 2048u16.to_le_bytes();
10122 let mut pixels = Vec::with_capacity((width * height * 2) as usize);
10123 for _ in 0..(width * height) {
10124 pixels.extend_from_slice(&sample);
10125 }
10126
10127 let codestream = encode(
10128 &pixels,
10129 width,
10130 height,
10131 1,
10132 12,
10133 false,
10134 &EncodeOptions {
10135 num_decomposition_levels: 2,
10136 use_ht_block_coding: true,
10137 ..Default::default()
10138 },
10139 )
10140 .expect("HT all-zero encode");
10141
10142 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10143 let cod_offset = codestream
10144 .windows(2)
10145 .position(|window| window == [0xFF, 0x52])
10146 .expect("COD marker");
10147 assert_eq!(codestream[cod_offset + 12], 0x40);
10148
10149 let image =
10150 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10151 let decoded = image.decode_native().expect("decode HT codestream");
10152
10153 assert_eq!(decoded.width, width);
10154 assert_eq!(decoded.height, height);
10155 assert_eq!(decoded.bit_depth, 12);
10156 assert_eq!(decoded.data, pixels);
10157 }
10158
10159 #[test]
10160 fn test_encode_high_throughput_nonzero_roundtrip() {
10161 let width = 1u32;
10162 let height = 1u32;
10163 let pixels = 2049u16.to_le_bytes().to_vec();
10164
10165 let codestream = encode_htj2k(
10166 &pixels,
10167 width,
10168 height,
10169 1,
10170 12,
10171 false,
10172 &EncodeOptions {
10173 num_decomposition_levels: 0,
10174 ..Default::default()
10175 },
10176 )
10177 .expect("HT non-zero encode");
10178
10179 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10180 let image =
10181 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10182 let decoded = image.decode_native().expect("decode HT codestream");
10183
10184 assert_eq!(decoded.width, width);
10185 assert_eq!(decoded.height, height);
10186 assert_eq!(decoded.bit_depth, 12);
10187 assert_eq!(decoded.data, pixels);
10188 }
10189
10190 #[test]
10191 fn test_encode_high_throughput_varied_12bit_roundtrip() {
10192 let mut pixels = Vec::with_capacity(32);
10193 for i in 0u16..16 {
10194 pixels.extend_from_slice(&((i * 257) & 0x0FFF).to_le_bytes());
10195 }
10196
10197 let codestream = encode_htj2k(
10198 &pixels,
10199 4,
10200 4,
10201 1,
10202 12,
10203 false,
10204 &EncodeOptions {
10205 num_decomposition_levels: 1,
10206 ..Default::default()
10207 },
10208 )
10209 .expect("HT varied encode");
10210
10211 let image =
10212 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10213 let decoded = image.decode_native().expect("decode HT codestream");
10214
10215 assert_eq!(decoded.width, 4);
10216 assert_eq!(decoded.height, 4);
10217 assert_eq!(decoded.bit_depth, 12);
10218 assert_eq!(decoded.data, pixels);
10219 }
10220
10221 #[test]
10222 fn test_encode_high_throughput_gradient_8bit_roundtrip() {
10223 let pixels: Vec<u8> = (0..64).collect();
10224
10225 let codestream = encode_htj2k(
10226 &pixels,
10227 8,
10228 8,
10229 1,
10230 8,
10231 false,
10232 &EncodeOptions {
10233 num_decomposition_levels: 3,
10234 ..Default::default()
10235 },
10236 )
10237 .expect("HT gradient encode");
10238
10239 let image =
10240 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10241 let decoded = image.decode_native().expect("decode HT codestream");
10242
10243 assert_eq!(decoded.width, 8);
10244 assert_eq!(decoded.height, 8);
10245 assert_eq!(decoded.bit_depth, 8);
10246 assert_eq!(decoded.data, pixels);
10247 }
10248
10249 #[test]
10250 fn test_encode_high_throughput_varied_12bit_large_roundtrip() {
10251 let width = 16u32;
10252 let height = 8u32;
10253 let mut pixels = Vec::with_capacity((width * height * 2) as usize);
10254 for y in 0u16..height as u16 {
10255 for x in 0u16..width as u16 {
10256 let value = (x * 257 + y * 17) & 0x0FFF;
10257 pixels.extend_from_slice(&value.to_le_bytes());
10258 }
10259 }
10260
10261 assert_htj2k_lossless_roundtrip(&pixels, width, height, 12, 4);
10262 }
10263
10264 #[test]
10265 fn test_encode_high_throughput_ramp_16bit_roundtrip() {
10266 let width = 48u32;
10267 let height = 24u32;
10268 let mut pixels = Vec::with_capacity((width * height * 2) as usize);
10269 for y in 0u16..height as u16 {
10270 for x in 0u16..width as u16 {
10271 let value = x * 521 + y * 997;
10272 pixels.extend_from_slice(&value.to_le_bytes());
10273 }
10274 }
10275
10276 assert_htj2k_lossless_roundtrip(&pixels, width, height, 16, 4);
10277 }
10278
10279 #[test]
10280 fn test_encode_high_throughput_lossy_large_gradient_is_parseable() {
10281 let pixels = gradient_u8(128, 128);
10282
10283 let (decoded, codestream_len) = lossy_htj2k_roundtrip_u8(&pixels, 128, 128, 5);
10284
10285 assert!(codestream_len > 110);
10286 assert_not_flat_128(&decoded);
10287 assert!(
10288 psnr_db(&pixels, &decoded) >= 30.0,
10289 "psnr={} max_abs={}",
10290 psnr_db(&pixels, &decoded),
10291 max_abs_error(&pixels, &decoded)
10292 );
10293 }
10294
10295 #[test]
10296 fn test_encode_high_throughput_lossy_constant_extremes_are_not_midgray() {
10297 for sample in [0u8, 255] {
10298 let pixels = vec![sample; 64 * 64];
10299 let (decoded, codestream_len) = lossy_htj2k_roundtrip_u8(&pixels, 64, 64, 4);
10300
10301 assert!(codestream_len > 110);
10302 assert_not_flat_128(&decoded);
10303 assert!(
10304 max_abs_error(&pixels, &decoded) <= 2,
10305 "sample={sample} max_abs={} decoded_min={} decoded_max={}",
10306 max_abs_error(&pixels, &decoded),
10307 decoded.iter().min().unwrap(),
10308 decoded.iter().max().unwrap()
10309 );
10310 }
10311 }
10312
10313 #[test]
10314 fn test_encode_invalid_dimensions() {
10315 let result = encode(&[], 0, 0, 1, 8, false, &EncodeOptions::default());
10316 assert!(result.is_err());
10317 }
10318
10319 #[test]
10320 fn test_encode_too_short() {
10321 let pixels = vec![0u8; 10]; let result = encode(&pixels, 8, 8, 1, 8, false, &EncodeOptions::default());
10323 assert!(result.is_err());
10324 }
10325
10326 #[test]
10327 fn test_deinterleave_rgb() {
10328 let pixels = vec![
10329 10u8, 20, 30, 40, 50, 60, ];
10332 let comps = deinterleave_to_f32(&pixels, 2, 3, 8, false);
10333 assert_eq!(comps[0], vec![-118.0, -88.0]); assert_eq!(comps[1], vec![-108.0, -78.0]); assert_eq!(comps[2], vec![-98.0, -68.0]); }
10337
10338 #[test]
10339 fn deinterleave_rgb8_unsigned_fast_path_matches_generic_output() {
10340 let pixels = (0..96)
10341 .map(|value| ((value * 19 + value / 3) & 0xff) as u8)
10342 .collect::<Vec<_>>();
10343
10344 let expected = deinterleave_to_f32(&pixels, 32, 3, 8, false);
10345 let actual = deinterleave_rgb8_unsigned_to_f32(&pixels, 32);
10346
10347 assert_eq!(actual, expected);
10348 }
10349
10350 #[test]
10351 fn test_encode_decode_roundtrip_gray_8bit() {
10352 use crate::{DecodeSettings, Image};
10353
10354 let original: Vec<u8> = vec![42u8; 64]; let encoded = encode(
10357 &original,
10358 8,
10359 8,
10360 1,
10361 8,
10362 false,
10363 &EncodeOptions {
10364 num_decomposition_levels: 0,
10365 reversible: true,
10366 ..Default::default()
10367 },
10368 )
10369 .expect("encode failed");
10370
10371 let settings = DecodeSettings {
10372 resolve_palette_indices: false,
10373 strict: false,
10374 target_resolution: None,
10375 };
10376 let image = Image::new(&encoded, &settings).expect("parse failed");
10377 let decoded = image.decode_native().expect("decode failed");
10378
10379 assert_eq!(decoded.width, 8);
10380 assert_eq!(decoded.height, 8);
10381 assert_eq!(decoded.data, original, "round-trip mismatch");
10382 }
10383
10384 #[test]
10385 fn test_encode_decode_roundtrip_gray_8bit_single_dwt_level() {
10386 use crate::{DecodeSettings, Image};
10387
10388 let original: Vec<u8> = (0..64 * 64)
10389 .map(|value| ((value * 37 + value / 7) & 0xFF) as u8)
10390 .collect();
10391 let encoded = encode(
10392 &original,
10393 64,
10394 64,
10395 1,
10396 8,
10397 false,
10398 &EncodeOptions {
10399 num_decomposition_levels: 1,
10400 reversible: true,
10401 ..Default::default()
10402 },
10403 )
10404 .expect("encode failed");
10405
10406 let image = Image::new(&encoded, &DecodeSettings::default()).expect("parse failed");
10407 let decoded = image.decode_native().expect("decode failed");
10408
10409 assert_eq!(decoded.width, 64);
10410 assert_eq!(decoded.height, 64);
10411 assert_eq!(decoded.data, original, "round-trip mismatch");
10412 }
10413
10414 #[cfg(feature = "std")]
10419 #[test]
10420 fn encode_htj2k_is_byte_deterministic() {
10421 const WIDTH: u32 = 96;
10422 const HEIGHT: u32 = 80;
10423 const NUM_COMPONENTS: u8 = 3;
10424 const BIT_DEPTH: u8 = 8;
10425 const REPETITIONS: usize = 8;
10426
10427 let pixel_count = (WIDTH * HEIGHT) as usize * usize::from(NUM_COMPONENTS);
10429 let pixels: Vec<u8> = (0..pixel_count)
10430 .map(|i| {
10431 let v = i
10432 .wrapping_mul(6364136223846793005)
10433 .wrapping_add(1442695040888963407);
10434 (v >> 56) as u8
10435 })
10436 .collect();
10437
10438 let options = EncodeOptions {
10439 use_ht_block_coding: true,
10440 reversible: true,
10441 num_decomposition_levels: 5,
10442 validate_high_throughput_codestream: true,
10443 ..EncodeOptions::default()
10444 };
10445
10446 let baseline = encode_htj2k(
10447 &pixels,
10448 WIDTH,
10449 HEIGHT,
10450 NUM_COMPONENTS.into(),
10451 BIT_DEPTH,
10452 false,
10453 &options,
10454 )
10455 .expect("encode_htj2k baseline failed");
10456
10457 assert!(
10458 !baseline.is_empty(),
10459 "baseline codestream must not be empty"
10460 );
10461
10462 for i in 0..REPETITIONS {
10463 let result = encode_htj2k(
10464 &pixels,
10465 WIDTH,
10466 HEIGHT,
10467 NUM_COMPONENTS.into(),
10468 BIT_DEPTH,
10469 false,
10470 &options,
10471 )
10472 .unwrap_or_else(|e| panic!("encode_htj2k repetition {i} failed: {e}"));
10473 assert_eq!(
10474 result,
10475 baseline,
10476 "encode_htj2k repetition {i} produced different bytes \
10477 (len baseline={}, len result={})",
10478 baseline.len(),
10479 result.len()
10480 );
10481 }
10482
10483 println!(
10484 "encode_htj2k_is_byte_deterministic: {} bytes, {} repetitions all identical",
10485 baseline.len(),
10486 REPETITIONS
10487 );
10488 }
10489
10490 #[cfg(feature = "std")]
10493 #[test]
10494 fn native_htj2k_roundtrips_two_component_lossless() {
10495 const WIDTH: u32 = 32;
10496 const HEIGHT: u32 = 24;
10497 const NUM_COMPONENTS: u8 = 2;
10498 const BIT_DEPTH: u8 = 8;
10499
10500 let pixel_count = WIDTH as usize * HEIGHT as usize * usize::from(NUM_COMPONENTS);
10503 let pixels: Vec<u8> = (0..pixel_count)
10504 .map(|i| ((i.wrapping_mul(251).wrapping_add(i / 7)) & 0xFF) as u8)
10505 .collect();
10506
10507 let codestream = encode_htj2k(
10508 &pixels,
10509 WIDTH,
10510 HEIGHT,
10511 NUM_COMPONENTS.into(),
10512 BIT_DEPTH,
10513 false,
10514 &EncodeOptions::default(),
10515 )
10516 .expect("native 2-component HTJ2K encode failed");
10517
10518 let image = Image::new(
10519 &codestream,
10520 &DecodeSettings {
10521 resolve_palette_indices: true,
10522 strict: true,
10523 target_resolution: None,
10524 },
10525 )
10526 .expect("native 2-component HTJ2K parse failed");
10527 let decoded = image
10528 .decode_native()
10529 .expect("native 2-component HTJ2K decode failed");
10530
10531 assert_eq!(decoded.width, WIDTH, "width mismatch");
10532 assert_eq!(decoded.height, HEIGHT, "height mismatch");
10533 assert_eq!(decoded.bit_depth, BIT_DEPTH, "bit_depth mismatch");
10534 assert_eq!(
10535 decoded.num_components,
10536 u16::from(NUM_COMPONENTS),
10537 "component count mismatch"
10538 );
10539 assert_eq!(
10540 decoded.data, pixels,
10541 "2-component HTJ2K lossless round-trip mismatch"
10542 );
10543
10544 println!(
10545 "native_htj2k_roundtrips_two_component_lossless: {} bytes codestream, {} pixel bytes",
10546 codestream.len(),
10547 pixels.len()
10548 );
10549 }
10550
10551 #[cfg(feature = "std")]
10555 #[test]
10556 fn native_htj2k_roundtrips_four_component_lossless() {
10557 const WIDTH: u32 = 32;
10558 const HEIGHT: u32 = 24;
10559 const NUM_COMPONENTS: u8 = 4;
10560 const BIT_DEPTH: u8 = 8;
10561
10562 let pixel_count = WIDTH as usize * HEIGHT as usize * usize::from(NUM_COMPONENTS);
10564 let pixels: Vec<u8> = (0..pixel_count)
10565 .map(|i| ((i.wrapping_mul(197).wrapping_add(i / 13)) & 0xFF) as u8)
10566 .collect();
10567
10568 let codestream = encode_htj2k(
10569 &pixels,
10570 WIDTH,
10571 HEIGHT,
10572 NUM_COMPONENTS.into(),
10573 BIT_DEPTH,
10574 false,
10575 &EncodeOptions::default(),
10576 )
10577 .expect("native 4-component HTJ2K encode failed");
10578
10579 let image = Image::new(
10580 &codestream,
10581 &DecodeSettings {
10582 resolve_palette_indices: true,
10583 strict: true,
10584 target_resolution: None,
10585 },
10586 )
10587 .expect("native 4-component HTJ2K parse failed");
10588 let decoded = image
10589 .decode_native()
10590 .expect("native 4-component HTJ2K decode failed");
10591
10592 assert_eq!(decoded.width, WIDTH, "width mismatch");
10593 assert_eq!(decoded.height, HEIGHT, "height mismatch");
10594 assert_eq!(decoded.bit_depth, BIT_DEPTH, "bit_depth mismatch");
10595 assert_eq!(
10596 decoded.num_components,
10597 u16::from(NUM_COMPONENTS),
10598 "component count mismatch"
10599 );
10600 assert_eq!(
10601 decoded.data, pixels,
10602 "4-component HTJ2K lossless round-trip mismatch"
10603 );
10604
10605 println!(
10606 "native_htj2k_roundtrips_four_component_lossless: {} bytes codestream, {} pixel bytes",
10607 codestream.len(),
10608 pixels.len()
10609 );
10610 }
10611
10612 #[test]
10613 fn classic_pcrd_assigns_limited_budget_by_distortion_slope() {
10614 let candidates = vec![
10615 ClassicSegmentAssignmentCandidate {
10616 block_index: 0,
10617 segment_index: 0,
10618 rate: 500,
10619 distortion_delta: 500.0,
10620 },
10621 ClassicSegmentAssignmentCandidate {
10622 block_index: 1,
10623 segment_index: 0,
10624 rate: 700,
10625 distortion_delta: 7_000.0,
10626 },
10627 ClassicSegmentAssignmentCandidate {
10628 block_index: 2,
10629 segment_index: 0,
10630 rate: 600,
10631 distortion_delta: 3_000.0,
10632 },
10633 ];
10634
10635 let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 3_000])
10636 .expect("PCRD assignment");
10637
10638 assert_eq!(
10639 assignments,
10640 vec![1, 0, 1],
10641 "the highest slope contribution should consume the constrained first-layer budget"
10642 );
10643 }
10644
10645 #[test]
10646 fn classic_pcrd_allows_byte_target_tolerance_for_first_legal_truncation() {
10647 let candidates = vec![ClassicSegmentAssignmentCandidate {
10648 block_index: 0,
10649 segment_index: 0,
10650 rate: 300,
10651 distortion_delta: 1_000.0,
10652 }];
10653
10654 let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 1_000])
10655 .expect("PCRD assignment");
10656
10657 assert_eq!(assignments, vec![0]);
10658 }
10659
10660 #[test]
10661 fn classic_pcrd_does_not_spend_budget_on_non_prefix_segments() {
10662 let candidates = vec![
10663 ClassicSegmentAssignmentCandidate {
10664 block_index: 0,
10665 segment_index: 0,
10666 rate: 1_000,
10667 distortion_delta: 1_000.0,
10668 },
10669 ClassicSegmentAssignmentCandidate {
10670 block_index: 0,
10671 segment_index: 1,
10672 rate: 500,
10673 distortion_delta: 10_000.0,
10674 },
10675 ClassicSegmentAssignmentCandidate {
10676 block_index: 1,
10677 segment_index: 0,
10678 rate: 300,
10679 distortion_delta: 600.0,
10680 },
10681 ];
10682
10683 let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 2_000])
10684 .expect("PCRD assignment");
10685
10686 assert_eq!(
10687 assignments,
10688 vec![1, 1, 0],
10689 "first-layer budget must go to the best legal prefix contribution"
10690 );
10691 }
10692
10693 #[test]
10694 fn ht_layer_assignment_uses_segment_budget_before_block_index() {
10695 let candidates = vec![
10696 HtSegmentAssignmentCandidate {
10697 block_index: 0,
10698 segment_index: 0,
10699 rate: 900,
10700 },
10701 HtSegmentAssignmentCandidate {
10702 block_index: 1,
10703 segment_index: 0,
10704 rate: 200,
10705 },
10706 HtSegmentAssignmentCandidate {
10707 block_index: 2,
10708 segment_index: 0,
10709 rate: 200,
10710 },
10711 ];
10712
10713 let assignments = assign_ht_segment_layers_by_budget(&candidates, 2, &[256, 2_000])
10714 .expect("HTJ2K segment assignment");
10715
10716 assert_eq!(
10717 assignments,
10718 vec![1, 0, 0],
10719 "HTJ2K early layers should be filled by segment byte budget, not block index"
10720 );
10721 }
10722
10723 #[test]
10724 fn ht_layer_assignment_keeps_refinement_after_cleanup() {
10725 let candidates = vec![
10726 HtSegmentAssignmentCandidate {
10727 block_index: 0,
10728 segment_index: 0,
10729 rate: 200,
10730 },
10731 HtSegmentAssignmentCandidate {
10732 block_index: 0,
10733 segment_index: 1,
10734 rate: 50,
10735 },
10736 ];
10737
10738 let assignments = assign_ht_segment_layers_by_budget(&candidates, 2, &[256, 2_000])
10739 .expect("HTJ2K segment assignment");
10740
10741 assert_eq!(
10742 assignments,
10743 vec![0, 0],
10744 "a refinement segment may share the cleanup layer but must not precede it"
10745 );
10746 }
10747
10748 #[test]
10749 fn ht_layer_contributions_split_cleanup_and_refinement_across_layers() {
10750 let encoded = bitplane_encode::EncodedCodeBlock {
10751 data: vec![0x11, 0x22, 0x33, 0x44, 0x55],
10752 num_coding_passes: 3,
10753 num_zero_bitplanes: 2,
10754 ht_cleanup_length: 3,
10755 ht_refinement_length: 2,
10756 };
10757
10758 let contributions = ht_layer_contributions(encoded, 2, &[0, 1]).expect("split HT layers");
10759
10760 assert_eq!(contributions.len(), 2);
10761 assert_eq!(contributions[0].data, vec![0x11, 0x22, 0x33]);
10762 assert_eq!(contributions[0].ht_cleanup_length, 3);
10763 assert_eq!(contributions[0].ht_refinement_length, 0);
10764 assert_eq!(contributions[0].num_coding_passes, 1);
10765 assert_eq!(contributions[1].data, vec![0x44, 0x55]);
10766 assert_eq!(contributions[1].ht_cleanup_length, 0);
10767 assert_eq!(contributions[1].ht_refinement_length, 2);
10768 assert_eq!(contributions[1].num_coding_passes, 2);
10769 }
10770
10771 #[test]
10772 fn htj2k_lossy_quality_layers_decode_split_refinement_layer() {
10773 let width = 32;
10774 let height = 32;
10775 let pixels = gradient_u8(width, height);
10776 let codestream = encode_htj2k(
10777 &pixels,
10778 width,
10779 height,
10780 1,
10781 8,
10782 false,
10783 &EncodeOptions {
10784 num_decomposition_levels: 0,
10785 reversible: false,
10786 guard_bits: 2,
10787 num_layers: 2,
10788 ..Default::default()
10789 },
10790 )
10791 .expect("HTJ2K layered encode");
10792
10793 let image = Image::new(
10794 &codestream,
10795 &DecodeSettings {
10796 resolve_palette_indices: true,
10797 strict: true,
10798 target_resolution: None,
10799 },
10800 )
10801 .expect("parse layered HT codestream");
10802 let decoded = image.decode_native().expect("decode layered HT codestream");
10803
10804 assert_eq!(decoded.width, width);
10805 assert_eq!(decoded.height, height);
10806 assert_eq!(decoded.bit_depth, 8);
10807 assert_not_flat_128(&decoded.data);
10808 assert!(
10809 psnr_db(&pixels, &decoded.data) >= 30.0,
10810 "psnr={} max_abs={}",
10811 psnr_db(&pixels, &decoded.data),
10812 max_abs_error(&pixels, &decoded.data)
10813 );
10814 }
10815}