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, 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};
44use crate::{DecodeSettings, Image};
45
46const HT_CPU_PARALLEL_FALLBACK_MIN_JOBS: usize = 4;
47
48#[derive(Debug, Clone)]
50pub struct EncodeOptions {
51 pub num_decomposition_levels: u8,
53 pub reversible: bool,
55 pub code_block_width_exp: u8,
57 pub code_block_height_exp: u8,
59 pub guard_bits: u8,
61 pub use_ht_block_coding: bool,
63 pub progression_order: EncodeProgressionOrder,
65 pub write_tlm: bool,
67 pub write_plt: bool,
69 pub write_plm: bool,
71 pub write_sop: bool,
73 pub write_eph: bool,
75 pub use_mct: bool,
77 pub num_layers: u8,
79 pub quality_layer_byte_targets: Vec<u64>,
81 pub validate_high_throughput_codestream: bool,
83 pub irreversible_quantization_scale: f32,
88 pub irreversible_quantization_subband_scales: IrreversibleQuantizationSubbandScales,
91 pub component_sampling: Option<Vec<(u8, u8)>>,
97 pub tile_size: Option<(u32, u32)>,
99 pub precinct_exponents: Vec<(u8, u8)>,
101}
102
103impl Default for EncodeOptions {
104 fn default() -> Self {
105 Self {
106 num_decomposition_levels: 5,
107 reversible: true,
108 code_block_width_exp: 4,
109 code_block_height_exp: 4,
110 guard_bits: 1,
111 use_ht_block_coding: false,
112 progression_order: EncodeProgressionOrder::Lrcp,
113 write_tlm: false,
114 write_plt: false,
115 write_plm: false,
116 write_sop: false,
117 write_eph: false,
118 use_mct: true,
119 num_layers: 1,
120 quality_layer_byte_targets: Vec::new(),
121 validate_high_throughput_codestream: true,
122 irreversible_quantization_scale: 1.0,
123 irreversible_quantization_subband_scales:
124 IrreversibleQuantizationSubbandScales::default(),
125 component_sampling: None,
126 tile_size: None,
127 precinct_exponents: Vec::new(),
128 }
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
134pub enum EncodeProgressionOrder {
135 #[default]
137 Lrcp,
138 Rlcp,
140 Rpcl,
142 Pcrl,
144 Cprl,
146}
147
148pub fn encode(
162 pixels: &[u8],
163 width: u32,
164 height: u32,
165 num_components: u8,
166 bit_depth: u8,
167 signed: bool,
168 options: &EncodeOptions,
169) -> Result<Vec<u8>, &'static str> {
170 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
171 encode_with_accelerator(
172 pixels,
173 width,
174 height,
175 num_components,
176 bit_depth,
177 signed,
178 options,
179 &mut accelerator,
180 )
181}
182
183pub fn encode_with_accelerator(
189 pixels: &[u8],
190 width: u32,
191 height: u32,
192 num_components: u8,
193 bit_depth: u8,
194 signed: bool,
195 options: &EncodeOptions,
196 accelerator: &mut impl J2kEncodeStageAccelerator,
197) -> Result<Vec<u8>, &'static str> {
198 let block_coding_mode = block_coding_mode(options);
199 let codestream = encode_impl(
200 pixels,
201 width,
202 height,
203 num_components,
204 bit_depth,
205 signed,
206 options,
207 block_coding_mode,
208 accelerator,
209 )?;
210
211 if block_coding_mode == BlockCodingMode::HighThroughput
212 && options.validate_high_throughput_codestream
213 {
214 validate_htj2k_codestream(
215 &codestream,
216 pixels,
217 width,
218 height,
219 num_components,
220 bit_depth,
221 signed,
222 options.reversible,
223 )?;
224 }
225
226 Ok(codestream)
227}
228
229pub fn encode_htj2k(
233 pixels: &[u8],
234 width: u32,
235 height: u32,
236 num_components: u8,
237 bit_depth: u8,
238 signed: bool,
239 options: &EncodeOptions,
240) -> Result<Vec<u8>, &'static str> {
241 let mut options = options.clone();
242 options.use_ht_block_coding = true;
243 encode(
244 pixels,
245 width,
246 height,
247 num_components,
248 bit_depth,
249 signed,
250 &options,
251 )
252}
253
254pub fn encode_precomputed_htj2k_53(
264 image: &PrecomputedHtj2k53Image,
265 options: &EncodeOptions,
266) -> Result<Vec<u8>, &'static str> {
267 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
268 encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, false, &mut accelerator)
269}
270
271pub fn encode_precomputed_htj2k_53_with_accelerator(
274 image: &PrecomputedHtj2k53Image,
275 options: &EncodeOptions,
276 accelerator: &mut impl J2kEncodeStageAccelerator,
277) -> Result<Vec<u8>, &'static str> {
278 encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, false, accelerator)
279}
280
281pub fn encode_precomputed_htj2k_53_with_mct(
287 image: &PrecomputedHtj2k53Image,
288 options: &EncodeOptions,
289 use_mct: bool,
290) -> Result<Vec<u8>, &'static str> {
291 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
292 encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, use_mct, &mut accelerator)
293}
294
295pub fn encode_precomputed_htj2k_53_with_mct_and_accelerator(
299 image: &PrecomputedHtj2k53Image,
300 options: &EncodeOptions,
301 use_mct: bool,
302 accelerator: &mut impl J2kEncodeStageAccelerator,
303) -> Result<Vec<u8>, &'static str> {
304 if image.width == 0 || image.height == 0 {
305 return Err("invalid dimensions");
306 }
307 if image.components.is_empty() || image.components.len() > 4 {
308 return Err("unsupported component count");
309 }
310 if image.bit_depth == 0 || image.bit_depth > 16 {
311 return Err("unsupported bit depth");
312 }
313 if image
314 .components
315 .iter()
316 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
317 {
318 return Err("component sampling factors must be non-zero");
319 }
320 validate_precomputed_dwt_geometry(image)?;
321
322 let num_components =
323 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
324 let num_levels = precomputed_level_count(&image.components)?;
325 let mut precomputed_options = options.clone();
326 precomputed_options.num_decomposition_levels = num_levels;
327 precomputed_options.reversible = true;
328 precomputed_options.use_ht_block_coding = true;
329 precomputed_options.use_mct = use_mct;
330 precomputed_options.validate_high_throughput_codestream = false;
331 precomputed_options.component_sampling = Some(
332 image
333 .components
334 .iter()
335 .map(|component| (component.x_rsiz, component.y_rsiz))
336 .collect(),
337 );
338
339 let dummy_pixels =
340 zero_pixel_buffer(image.width, image.height, num_components, image.bit_depth)?;
341 let mut precomputed_accelerator = PrecomputedDwtAccelerator {
342 outputs: image
343 .components
344 .iter()
345 .map(|component| component.dwt.clone())
346 .collect(),
347 encode_accelerator: accelerator,
348 };
349
350 encode_with_accelerator(
351 &dummy_pixels,
352 image.width,
353 image.height,
354 num_components,
355 image.bit_depth,
356 image.signed,
357 &precomputed_options,
358 &mut precomputed_accelerator,
359 )
360}
361
362pub fn encode_precomputed_htj2k_97(
372 image: &PrecomputedHtj2k97Image,
373 options: &EncodeOptions,
374) -> Result<Vec<u8>, &'static str> {
375 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
376 encode_precomputed_htj2k_97_with_accelerator(image, options, &mut accelerator)
377}
378
379pub fn encode_precomputed_htj2k_97_with_accelerator(
382 image: &PrecomputedHtj2k97Image,
383 options: &EncodeOptions,
384 accelerator: &mut impl J2kEncodeStageAccelerator,
385) -> Result<Vec<u8>, &'static str> {
386 if image.width == 0 || image.height == 0 {
387 return Err("invalid dimensions");
388 }
389 if image.components.is_empty() || image.components.len() > 4 {
390 return Err("unsupported component count");
391 }
392 if image.bit_depth == 0 || image.bit_depth > 16 {
393 return Err("unsupported bit depth");
394 }
395 validate_irreversible_quantization_profile(options)?;
396 if image
397 .components
398 .iter()
399 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
400 {
401 return Err("component sampling factors must be non-zero");
402 }
403 validate_precomputed_dwt97_geometry(image)?;
404
405 let num_components =
406 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
407 let num_levels = precomputed_97_level_count(&image.components)?;
408 let mut precomputed_options = options.clone();
409 precomputed_options.num_decomposition_levels = num_levels;
410 precomputed_options.reversible = false;
411 precomputed_options.use_ht_block_coding = true;
412 precomputed_options.use_mct = false;
413 precomputed_options.validate_high_throughput_codestream = false;
414 precomputed_options.component_sampling = Some(
415 image
416 .components
417 .iter()
418 .map(|component| (component.x_rsiz, component.y_rsiz))
419 .collect(),
420 );
421
422 let dummy_pixels =
423 zero_pixel_buffer(image.width, image.height, num_components, image.bit_depth)?;
424 let mut precomputed_accelerator = PrecomputedDwt97Accelerator {
425 outputs: image
426 .components
427 .iter()
428 .map(|component| component.dwt.clone())
429 .collect(),
430 encode_accelerator: accelerator,
431 };
432
433 encode_with_accelerator(
434 &dummy_pixels,
435 image.width,
436 image.height,
437 num_components,
438 image.bit_depth,
439 image.signed,
440 &precomputed_options,
441 &mut precomputed_accelerator,
442 )
443}
444
445pub fn encode_precomputed_htj2k_97_batch_with_accelerator(
448 images: &[PrecomputedHtj2k97Image],
449 options: &EncodeOptions,
450 accelerator: &mut impl J2kEncodeStageAccelerator,
451) -> Result<Vec<Vec<u8>>, &'static str> {
452 if images.is_empty() {
453 return Ok(Vec::new());
454 }
455 if options.num_layers != 1 {
456 return Err("batch precomputed 9/7 encode currently supports one quality layer");
457 }
458
459 let mut prepared_images = prepare_precomputed_htj2k97_images_for_batch(images, options)?;
460 let mut all_packets = Vec::new();
461 for prepared in &mut prepared_images {
462 prepared.packet_count = prepared.prepared_packets.len();
463 all_packets.append(&mut prepared.prepared_packets);
464 }
465
466 let mut encoded_packets =
467 encode_prepared_resolution_packets(all_packets, accelerator)?.into_iter();
468 let mut codestreams = Vec::with_capacity(prepared_images.len());
469 for prepared in prepared_images {
470 let mut resolution_packets = Vec::with_capacity(prepared.packet_count);
471 for _ in 0..prepared.packet_count {
472 resolution_packets.push(
473 encoded_packets
474 .next()
475 .ok_or("encoded packet count mismatch")?,
476 );
477 }
478 let scalar_packet_descriptors = scalar_packet_descriptors(&prepared.packet_descriptors);
479 let packetized_tile =
480 packet_encode::form_tile_bitstream_with_descriptors_lengths_and_markers(
481 &mut resolution_packets,
482 &scalar_packet_descriptors,
483 packet_encode::PacketMarkerOptions {
484 write_sop: prepared.params.write_sop,
485 write_eph: prepared.params.write_eph,
486 },
487 )?;
488 codestreams.push(codestream_write::write_codestream_with_packet_lengths(
489 &prepared.params,
490 &packetized_tile.data,
491 &prepared.quant_params,
492 &packetized_tile.packet_lengths,
493 ));
494 }
495 if encoded_packets.next().is_some() {
496 return Err("encoded packet count mismatch");
497 }
498
499 Ok(codestreams)
500}
501
502#[cfg(feature = "parallel")]
503fn prepare_precomputed_htj2k97_images_for_batch(
504 images: &[PrecomputedHtj2k97Image],
505 options: &EncodeOptions,
506) -> Result<Vec<PreparedPrecomputedHtj2k97Image>, &'static str> {
507 images
508 .par_iter()
509 .map(|image| prepare_precomputed_htj2k97_image_for_batch(image, options))
510 .collect()
511}
512
513#[cfg(not(feature = "parallel"))]
514fn prepare_precomputed_htj2k97_images_for_batch(
515 images: &[PrecomputedHtj2k97Image],
516 options: &EncodeOptions,
517) -> Result<Vec<PreparedPrecomputedHtj2k97Image>, &'static str> {
518 images
519 .iter()
520 .map(|image| prepare_precomputed_htj2k97_image_for_batch(image, options))
521 .collect()
522}
523
524pub fn encode_prequantized_htj2k_97(
527 image: &PrequantizedHtj2k97Image,
528 options: &EncodeOptions,
529) -> Result<Vec<u8>, &'static str> {
530 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
531 encode_prequantized_htj2k_97_with_accelerator(image, options, &mut accelerator)
532}
533
534pub fn encode_prequantized_htj2k_97_with_accelerator(
537 image: &PrequantizedHtj2k97Image,
538 options: &EncodeOptions,
539 accelerator: &mut impl J2kEncodeStageAccelerator,
540) -> Result<Vec<u8>, &'static str> {
541 if image.width == 0 || image.height == 0 {
542 return Err("invalid dimensions");
543 }
544 if image.components.is_empty() || image.components.len() > 4 {
545 return Err("unsupported component count");
546 }
547 if image.bit_depth == 0 || image.bit_depth > 16 {
548 return Err("unsupported bit depth");
549 }
550 validate_irreversible_quantization_profile(options)?;
551 if image
552 .components
553 .iter()
554 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
555 {
556 return Err("component sampling factors must be non-zero");
557 }
558
559 let num_components =
560 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
561 let num_levels = prequantized_97_level_count(&image.components)?;
562 let guard_bits = options.guard_bits.max(2);
563 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
564 image.bit_depth,
565 num_levels,
566 false,
567 guard_bits,
568 options.irreversible_quantization_scale,
569 options.irreversible_quantization_subband_scales,
570 );
571 validate_prequantized_htj2k97_image(image, guard_bits, &step_sizes)?;
572
573 let mut prequantized_options = options.clone();
574 prequantized_options.num_decomposition_levels = num_levels;
575 prequantized_options.reversible = false;
576 prequantized_options.use_ht_block_coding = true;
577 prequantized_options.use_mct = false;
578 prequantized_options.validate_high_throughput_codestream = false;
579 prequantized_options.component_sampling = Some(
580 image
581 .components
582 .iter()
583 .map(|component| (component.x_rsiz, component.y_rsiz))
584 .collect(),
585 );
586
587 let component_resolution_packets = image
588 .components
589 .iter()
590 .enumerate()
591 .map(|(component_idx, component)| {
592 prepared_resolution_packets_from_prequantized_component(component_idx, component)
593 })
594 .collect::<Result<Vec<_>, &'static str>>()?;
595 let prepared_resolution_packets =
596 ordered_prepared_resolution_packets(component_resolution_packets, &prequantized_options)?;
597 let packet_descriptors = packet_descriptors_for_order(
598 &prepared_resolution_packets,
599 1,
600 prequantized_options.progression_order,
601 )?;
602 let mut resolution_packets =
603 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
604 let packetization_resolutions = public_packetization_resolutions(&resolution_packets);
605 let packetization_job = J2kPacketizationEncodeJob {
606 resolution_count: resolution_packets.len() as u32,
607 num_layers: 1,
608 num_components,
609 code_block_count: count_code_blocks(&resolution_packets)?,
610 progression_order: public_packetization_progression_order(
611 prequantized_options.progression_order,
612 ),
613 packet_descriptors: &packet_descriptors,
614 resolutions: &packetization_resolutions,
615 };
616 let tile_data = accelerator
617 .encode_packetization(packetization_job)?
618 .unwrap_or_else(|| {
619 packet_encode::form_tile_bitstream(&mut resolution_packets, 1, num_components)
620 });
621
622 let quant_params: Vec<(u16, u16)> = step_sizes
623 .iter()
624 .map(|s| (s.exponent, s.mantissa))
625 .collect();
626 let params = EncodeParams {
627 width: image.width,
628 height: image.height,
629 tile_width: image.width,
630 tile_height: image.height,
631 num_components,
632 bit_depth: image.bit_depth,
633 signed: image.signed,
634 num_decomposition_levels: num_levels,
635 reversible: false,
636 code_block_width_exp: prequantized_options.code_block_width_exp,
637 code_block_height_exp: prequantized_options.code_block_height_exp,
638 num_layers: 1,
639 use_mct: false,
640 guard_bits,
641 block_coding_mode: BlockCodingMode::HighThroughput,
642 progression_order: prequantized_options.progression_order,
643 write_tlm: prequantized_options.write_tlm,
644 write_plt: prequantized_options.write_plt,
645 write_plm: prequantized_options.write_plm,
646 write_sop: prequantized_options.write_sop,
647 write_eph: prequantized_options.write_eph,
648 terminate_coding_passes: false,
649 component_sampling: prequantized_options
650 .component_sampling
651 .clone()
652 .ok_or("component sampling missing")?,
653 precinct_exponents: precinct_exponents_for_options(&prequantized_options, num_levels)?,
654 };
655
656 Ok(codestream_write::write_codestream(
657 ¶ms,
658 &tile_data,
659 &quant_params,
660 ))
661}
662
663pub fn encode_preencoded_htj2k_97(
666 image: &PreencodedHtj2k97Image,
667 options: &EncodeOptions,
668) -> Result<Vec<u8>, &'static str> {
669 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
670 encode_preencoded_htj2k_97_with_accelerator(image, options, &mut accelerator)
671}
672
673pub fn encode_preencoded_htj2k_97_with_accelerator(
676 image: &PreencodedHtj2k97Image,
677 options: &EncodeOptions,
678 accelerator: &mut impl J2kEncodeStageAccelerator,
679) -> Result<Vec<u8>, &'static str> {
680 if image.width == 0 || image.height == 0 {
681 return Err("invalid dimensions");
682 }
683 if image.components.is_empty() || image.components.len() > 4 {
684 return Err("unsupported component count");
685 }
686 if image.bit_depth == 0 || image.bit_depth > 16 {
687 return Err("unsupported bit depth");
688 }
689 validate_irreversible_quantization_profile(options)?;
690 if image
691 .components
692 .iter()
693 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
694 {
695 return Err("component sampling factors must be non-zero");
696 }
697
698 let num_components =
699 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
700 let num_levels = preencoded_97_level_count(&image.components)?;
701 let guard_bits = options.guard_bits.max(2);
702 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
703 image.bit_depth,
704 num_levels,
705 false,
706 guard_bits,
707 options.irreversible_quantization_scale,
708 options.irreversible_quantization_subband_scales,
709 );
710 validate_preencoded_htj2k97_image(image, guard_bits, &step_sizes)?;
711
712 let mut preencoded_options = options.clone();
713 preencoded_options.num_decomposition_levels = num_levels;
714 preencoded_options.reversible = false;
715 preencoded_options.use_ht_block_coding = true;
716 preencoded_options.use_mct = false;
717 preencoded_options.validate_high_throughput_codestream = false;
718 preencoded_options.component_sampling = Some(
719 image
720 .components
721 .iter()
722 .map(|component| (component.x_rsiz, component.y_rsiz))
723 .collect(),
724 );
725
726 let component_resolution_packets = image
727 .components
728 .iter()
729 .enumerate()
730 .map(|(component_idx, component)| {
731 prepared_resolution_packets_from_preencoded_component(component_idx, component)
732 })
733 .collect::<Result<Vec<_>, &'static str>>()?;
734 let prepared_resolution_packets =
735 ordered_prepared_resolution_packets(component_resolution_packets, &preencoded_options)?;
736 let packet_descriptors = packet_descriptors_for_order(
737 &prepared_resolution_packets,
738 1,
739 preencoded_options.progression_order,
740 )?;
741 let mut resolution_packets =
742 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
743 let packetization_resolutions = public_packetization_resolutions(&resolution_packets);
744 let packetization_job = J2kPacketizationEncodeJob {
745 resolution_count: resolution_packets.len() as u32,
746 num_layers: 1,
747 num_components,
748 code_block_count: count_code_blocks(&resolution_packets)?,
749 progression_order: public_packetization_progression_order(
750 preencoded_options.progression_order,
751 ),
752 packet_descriptors: &packet_descriptors,
753 resolutions: &packetization_resolutions,
754 };
755 let tile_data = accelerator
756 .encode_packetization(packetization_job)?
757 .unwrap_or_else(|| {
758 packet_encode::form_tile_bitstream(&mut resolution_packets, 1, num_components)
759 });
760
761 let quant_params: Vec<(u16, u16)> = step_sizes
762 .iter()
763 .map(|s| (s.exponent, s.mantissa))
764 .collect();
765 let params = EncodeParams {
766 width: image.width,
767 height: image.height,
768 tile_width: image.width,
769 tile_height: image.height,
770 num_components,
771 bit_depth: image.bit_depth,
772 signed: image.signed,
773 num_decomposition_levels: num_levels,
774 reversible: false,
775 code_block_width_exp: preencoded_options.code_block_width_exp,
776 code_block_height_exp: preencoded_options.code_block_height_exp,
777 num_layers: 1,
778 use_mct: false,
779 guard_bits,
780 block_coding_mode: BlockCodingMode::HighThroughput,
781 progression_order: preencoded_options.progression_order,
782 write_tlm: preencoded_options.write_tlm,
783 write_plt: preencoded_options.write_plt,
784 write_plm: preencoded_options.write_plm,
785 write_sop: preencoded_options.write_sop,
786 write_eph: preencoded_options.write_eph,
787 terminate_coding_passes: false,
788 component_sampling: preencoded_options
789 .component_sampling
790 .clone()
791 .ok_or("component sampling missing")?,
792 precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
793 };
794
795 Ok(codestream_write::write_codestream(
796 ¶ms,
797 &tile_data,
798 &quant_params,
799 ))
800}
801
802pub fn encode_preencoded_htj2k_97_owned_with_accelerator(
806 image: PreencodedHtj2k97Image,
807 options: &EncodeOptions,
808 accelerator: &mut impl J2kEncodeStageAccelerator,
809) -> Result<Vec<u8>, &'static str> {
810 if image.width == 0 || image.height == 0 {
811 return Err("invalid dimensions");
812 }
813 if image.components.is_empty() || image.components.len() > 4 {
814 return Err("unsupported component count");
815 }
816 if image.bit_depth == 0 || image.bit_depth > 16 {
817 return Err("unsupported bit depth");
818 }
819 validate_irreversible_quantization_profile(options)?;
820 if image
821 .components
822 .iter()
823 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
824 {
825 return Err("component sampling factors must be non-zero");
826 }
827
828 let width = image.width;
829 let height = image.height;
830 let bit_depth = image.bit_depth;
831 let signed = image.signed;
832 let num_components =
833 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
834 let num_levels = preencoded_97_level_count(&image.components)?;
835 let guard_bits = options.guard_bits.max(2);
836 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
837 bit_depth,
838 num_levels,
839 false,
840 guard_bits,
841 options.irreversible_quantization_scale,
842 options.irreversible_quantization_subband_scales,
843 );
844 validate_preencoded_htj2k97_image(&image, guard_bits, &step_sizes)?;
845
846 let component_sampling = image
847 .components
848 .iter()
849 .map(|component| (component.x_rsiz, component.y_rsiz))
850 .collect::<Vec<_>>();
851 let mut preencoded_options = options.clone();
852 preencoded_options.num_decomposition_levels = num_levels;
853 preencoded_options.reversible = false;
854 preencoded_options.use_ht_block_coding = true;
855 preencoded_options.use_mct = false;
856 preencoded_options.validate_high_throughput_codestream = false;
857 preencoded_options.component_sampling = Some(component_sampling);
858
859 let component_resolution_packets = image
860 .components
861 .into_iter()
862 .enumerate()
863 .map(|(component_idx, component)| {
864 prepared_resolution_packets_from_preencoded_component_owned(component_idx, component)
865 })
866 .collect::<Result<Vec<_>, &'static str>>()?;
867 let prepared_resolution_packets =
868 ordered_prepared_resolution_packets(component_resolution_packets, &preencoded_options)?;
869 let packet_descriptors = packet_descriptors_for_order(
870 &prepared_resolution_packets,
871 1,
872 preencoded_options.progression_order,
873 )?;
874 let mut resolution_packets =
875 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
876 let packetization_resolutions = public_packetization_resolutions(&resolution_packets);
877 let packetization_job = J2kPacketizationEncodeJob {
878 resolution_count: resolution_packets.len() as u32,
879 num_layers: 1,
880 num_components,
881 code_block_count: count_code_blocks(&resolution_packets)?,
882 progression_order: public_packetization_progression_order(
883 preencoded_options.progression_order,
884 ),
885 packet_descriptors: &packet_descriptors,
886 resolutions: &packetization_resolutions,
887 };
888 let tile_data = accelerator
889 .encode_packetization(packetization_job)?
890 .unwrap_or_else(|| {
891 packet_encode::form_tile_bitstream(&mut resolution_packets, 1, num_components)
892 });
893
894 let quant_params: Vec<(u16, u16)> = step_sizes
895 .iter()
896 .map(|s| (s.exponent, s.mantissa))
897 .collect();
898 let params = EncodeParams {
899 width,
900 height,
901 tile_width: width,
902 tile_height: height,
903 num_components,
904 bit_depth,
905 signed,
906 num_decomposition_levels: num_levels,
907 reversible: false,
908 code_block_width_exp: preencoded_options.code_block_width_exp,
909 code_block_height_exp: preencoded_options.code_block_height_exp,
910 num_layers: 1,
911 use_mct: false,
912 guard_bits,
913 block_coding_mode: BlockCodingMode::HighThroughput,
914 progression_order: preencoded_options.progression_order,
915 write_tlm: preencoded_options.write_tlm,
916 write_plt: preencoded_options.write_plt,
917 write_plm: preencoded_options.write_plm,
918 write_sop: preencoded_options.write_sop,
919 write_eph: preencoded_options.write_eph,
920 terminate_coding_passes: false,
921 component_sampling: preencoded_options
922 .component_sampling
923 .clone()
924 .ok_or("component sampling missing")?,
925 precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
926 };
927
928 Ok(codestream_write::write_codestream(
929 ¶ms,
930 &tile_data,
931 &quant_params,
932 ))
933}
934
935pub fn encode_preencoded_htj2k_97_compact_owned_with_accelerator(
939 image: PreencodedHtj2k97CompactImage,
940 options: &EncodeOptions,
941 accelerator: &mut impl J2kEncodeStageAccelerator,
942) -> Result<Vec<u8>, &'static str> {
943 if image.width == 0 || image.height == 0 {
944 return Err("invalid dimensions");
945 }
946 if image.components.is_empty() || image.components.len() > 4 {
947 return Err("unsupported component count");
948 }
949 if image.bit_depth == 0 || image.bit_depth > 16 {
950 return Err("unsupported bit depth");
951 }
952 validate_irreversible_quantization_profile(options)?;
953 if image
954 .components
955 .iter()
956 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
957 {
958 return Err("component sampling factors must be non-zero");
959 }
960
961 let width = image.width;
962 let height = image.height;
963 let bit_depth = image.bit_depth;
964 let signed = image.signed;
965 let num_components =
966 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
967 let num_levels = preencoded_compact_97_level_count(&image.components)?;
968 let guard_bits = options.guard_bits.max(2);
969 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
970 bit_depth,
971 num_levels,
972 false,
973 guard_bits,
974 options.irreversible_quantization_scale,
975 options.irreversible_quantization_subband_scales,
976 );
977 validate_preencoded_compact_htj2k97_image(&image, guard_bits, &step_sizes)?;
978
979 let component_sampling = image
980 .components
981 .iter()
982 .map(|component| (component.x_rsiz, component.y_rsiz))
983 .collect::<Vec<_>>();
984 let mut preencoded_options = options.clone();
985 preencoded_options.num_decomposition_levels = num_levels;
986 preencoded_options.reversible = false;
987 preencoded_options.use_ht_block_coding = true;
988 preencoded_options.use_mct = false;
989 preencoded_options.validate_high_throughput_codestream = false;
990 preencoded_options.component_sampling = Some(component_sampling);
991
992 let PreencodedHtj2k97CompactImage {
993 payload,
994 components,
995 ..
996 } = image;
997 let component_resolution_packets = components
998 .iter()
999 .enumerate()
1000 .map(|(component_idx, component)| {
1001 prepared_resolution_packets_from_preencoded_compact_component(
1002 component_idx,
1003 component,
1004 &payload,
1005 )
1006 })
1007 .collect::<Result<Vec<_>, &'static str>>()?;
1008 let prepared_resolution_packets = ordered_prepared_compact_resolution_packets(
1009 component_resolution_packets,
1010 &preencoded_options,
1011 )?;
1012 let packet_descriptors = packet_descriptors_for_compact_order(
1013 &prepared_resolution_packets,
1014 1,
1015 preencoded_options.progression_order,
1016 )?;
1017 let packetization_resolutions =
1018 public_packetization_resolutions_from_compact(&prepared_resolution_packets);
1019 let packetization_job = J2kPacketizationEncodeJob {
1020 resolution_count: packetization_resolutions.len() as u32,
1021 num_layers: 1,
1022 num_components,
1023 code_block_count: count_compact_code_blocks(&prepared_resolution_packets)?,
1024 progression_order: public_packetization_progression_order(
1025 preencoded_options.progression_order,
1026 ),
1027 packet_descriptors: &packet_descriptors,
1028 resolutions: &packetization_resolutions,
1029 };
1030 let tile_data = accelerator
1031 .encode_packetization(packetization_job)?
1032 .map_or_else(
1033 || crate::encode_j2k_packetization_scalar(packetization_job),
1034 Ok,
1035 )?;
1036
1037 let quant_params: Vec<(u16, u16)> = step_sizes
1038 .iter()
1039 .map(|s| (s.exponent, s.mantissa))
1040 .collect();
1041 let params = EncodeParams {
1042 width,
1043 height,
1044 tile_width: width,
1045 tile_height: height,
1046 num_components,
1047 bit_depth,
1048 signed,
1049 num_decomposition_levels: num_levels,
1050 reversible: false,
1051 code_block_width_exp: preencoded_options.code_block_width_exp,
1052 code_block_height_exp: preencoded_options.code_block_height_exp,
1053 num_layers: 1,
1054 use_mct: false,
1055 guard_bits,
1056 block_coding_mode: BlockCodingMode::HighThroughput,
1057 progression_order: preencoded_options.progression_order,
1058 write_tlm: preencoded_options.write_tlm,
1059 write_plt: preencoded_options.write_plt,
1060 write_plm: preencoded_options.write_plm,
1061 write_sop: preencoded_options.write_sop,
1062 write_eph: preencoded_options.write_eph,
1063 terminate_coding_passes: false,
1064 component_sampling: preencoded_options
1065 .component_sampling
1066 .clone()
1067 .ok_or("component sampling missing")?,
1068 precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
1069 };
1070
1071 Ok(codestream_write::write_codestream(
1072 ¶ms,
1073 &tile_data,
1074 &quant_params,
1075 ))
1076}
1077
1078fn validate_precomputed_dwt_geometry(image: &PrecomputedHtj2k53Image) -> Result<(), &'static str> {
1079 for component in &image.components {
1080 let component_width = image.width.div_ceil(u32::from(component.x_rsiz));
1081 let component_height = image.height.div_ceil(u32::from(component.y_rsiz));
1082 validate_precomputed_component_dwt_geometry(
1083 &component.dwt,
1084 component_width,
1085 component_height,
1086 )?;
1087 }
1088
1089 Ok(())
1090}
1091
1092fn validate_precomputed_dwt97_geometry(
1093 image: &PrecomputedHtj2k97Image,
1094) -> Result<(), &'static str> {
1095 for component in &image.components {
1096 let component_width = image.width.div_ceil(u32::from(component.x_rsiz));
1097 let component_height = image.height.div_ceil(u32::from(component.y_rsiz));
1098 validate_precomputed_component_dwt_geometry(
1099 &component.dwt,
1100 component_width,
1101 component_height,
1102 )?;
1103 }
1104
1105 Ok(())
1106}
1107
1108fn validate_precomputed_component_dwt_geometry(
1109 dwt: &impl PrecomputedDwtGeometryView,
1110 component_width: u32,
1111 component_height: u32,
1112) -> Result<(), &'static str> {
1113 if dwt.level_count() == 0 {
1114 return Err("precomputed DWT must contain at least one decomposition level");
1115 }
1116 if let Some(highest_level) = dwt.last_level_geometry() {
1117 if highest_level.width != component_width || highest_level.height != component_height {
1118 return Err("precomputed DWT component dimensions mismatch");
1119 }
1120 }
1121
1122 let mut expected_width = component_width;
1123 let mut expected_height = component_height;
1124 for level_index in (0..dwt.level_count()).rev() {
1125 let level = dwt.level_geometry(level_index);
1126 let low_width = expected_width.div_ceil(2);
1127 let low_height = expected_height.div_ceil(2);
1128 let high_width = expected_width / 2;
1129 let high_height = expected_height / 2;
1130
1131 if level.width != expected_width
1132 || level.height != expected_height
1133 || level.low_width != low_width
1134 || level.low_height != low_height
1135 || level.high_width != high_width
1136 || level.high_height != high_height
1137 {
1138 return Err("precomputed DWT recursive geometry mismatch");
1139 }
1140 validate_band_len(level.hl_len, high_width, low_height)?;
1141 validate_band_len(level.lh_len, low_width, high_height)?;
1142 validate_band_len(level.hh_len, high_width, high_height)?;
1143
1144 expected_width = low_width;
1145 expected_height = low_height;
1146 }
1147
1148 if dwt.ll_width() != expected_width || dwt.ll_height() != expected_height {
1149 return Err("precomputed DWT component dimensions mismatch");
1150 }
1151 validate_band_len(dwt.ll_len(), expected_width, expected_height)
1152}
1153
1154#[derive(Debug, Clone, Copy)]
1155struct PrecomputedDwtLevelGeometry {
1156 width: u32,
1157 height: u32,
1158 low_width: u32,
1159 low_height: u32,
1160 high_width: u32,
1161 high_height: u32,
1162 hl_len: usize,
1163 lh_len: usize,
1164 hh_len: usize,
1165}
1166
1167trait PrecomputedDwtGeometryView {
1168 fn ll_len(&self) -> usize;
1169 fn ll_width(&self) -> u32;
1170 fn ll_height(&self) -> u32;
1171 fn level_count(&self) -> usize;
1172 fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry;
1173
1174 fn last_level_geometry(&self) -> Option<PrecomputedDwtLevelGeometry> {
1175 self.level_count()
1176 .checked_sub(1)
1177 .map(|index| self.level_geometry(index))
1178 }
1179}
1180
1181impl PrecomputedDwtGeometryView for J2kForwardDwt53Output {
1182 fn ll_len(&self) -> usize {
1183 self.ll.len()
1184 }
1185
1186 fn ll_width(&self) -> u32 {
1187 self.ll_width
1188 }
1189
1190 fn ll_height(&self) -> u32 {
1191 self.ll_height
1192 }
1193
1194 fn level_count(&self) -> usize {
1195 self.levels.len()
1196 }
1197
1198 fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry {
1199 let level = &self.levels[index];
1200 PrecomputedDwtLevelGeometry {
1201 width: level.width,
1202 height: level.height,
1203 low_width: level.low_width,
1204 low_height: level.low_height,
1205 high_width: level.high_width,
1206 high_height: level.high_height,
1207 hl_len: level.hl.len(),
1208 lh_len: level.lh.len(),
1209 hh_len: level.hh.len(),
1210 }
1211 }
1212}
1213
1214impl PrecomputedDwtGeometryView for J2kForwardDwt97Output {
1215 fn ll_len(&self) -> usize {
1216 self.ll.len()
1217 }
1218
1219 fn ll_width(&self) -> u32 {
1220 self.ll_width
1221 }
1222
1223 fn ll_height(&self) -> u32 {
1224 self.ll_height
1225 }
1226
1227 fn level_count(&self) -> usize {
1228 self.levels.len()
1229 }
1230
1231 fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry {
1232 let level = &self.levels[index];
1233 PrecomputedDwtLevelGeometry {
1234 width: level.width,
1235 height: level.height,
1236 low_width: level.low_width,
1237 low_height: level.low_height,
1238 high_width: level.high_width,
1239 high_height: level.high_height,
1240 hl_len: level.hl.len(),
1241 lh_len: level.lh.len(),
1242 hh_len: level.hh.len(),
1243 }
1244 }
1245}
1246
1247fn precomputed_level_count(components: &[PrecomputedHtj2k53Component]) -> Result<u8, &'static str> {
1248 let first = components
1249 .first()
1250 .ok_or("unsupported component count")?
1251 .dwt
1252 .levels
1253 .len();
1254 if components
1255 .iter()
1256 .any(|component| component.dwt.levels.len() != first)
1257 {
1258 return Err("precomputed components must use the same decomposition level count");
1259 }
1260 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
1261}
1262
1263fn precomputed_97_level_count(
1264 components: &[PrecomputedHtj2k97Component],
1265) -> Result<u8, &'static str> {
1266 let first = components
1267 .first()
1268 .ok_or("unsupported component count")?
1269 .dwt
1270 .levels
1271 .len();
1272 if components
1273 .iter()
1274 .any(|component| component.dwt.levels.len() != first)
1275 {
1276 return Err("precomputed components must use the same decomposition level count");
1277 }
1278 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
1279}
1280
1281fn prequantized_97_level_count(
1282 components: &[PrequantizedHtj2k97Component],
1283) -> Result<u8, &'static str> {
1284 let first = components
1285 .first()
1286 .ok_or("unsupported component count")?
1287 .resolutions
1288 .len()
1289 .checked_sub(1)
1290 .ok_or("prequantized components must contain at least one decomposition level")?;
1291 if components
1292 .iter()
1293 .any(|component| component.resolutions.len() != first + 1)
1294 {
1295 return Err("prequantized components must use the same decomposition level count");
1296 }
1297 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
1298}
1299
1300fn preencoded_97_level_count(
1301 components: &[PreencodedHtj2k97Component],
1302) -> Result<u8, &'static str> {
1303 let first = components
1304 .first()
1305 .ok_or("unsupported component count")?
1306 .resolutions
1307 .len()
1308 .checked_sub(1)
1309 .ok_or("preencoded components must contain at least one decomposition level")?;
1310 if components
1311 .iter()
1312 .any(|component| component.resolutions.len() != first + 1)
1313 {
1314 return Err("preencoded components must use the same decomposition level count");
1315 }
1316 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
1317}
1318
1319fn preencoded_compact_97_level_count(
1320 components: &[PreencodedHtj2k97CompactComponent],
1321) -> Result<u8, &'static str> {
1322 let first = components
1323 .first()
1324 .ok_or("unsupported component count")?
1325 .resolutions
1326 .len()
1327 .checked_sub(1)
1328 .ok_or("preencoded components must contain at least one decomposition level")?;
1329 if components
1330 .iter()
1331 .any(|component| component.resolutions.len() != first + 1)
1332 {
1333 return Err("preencoded components must use the same decomposition level count");
1334 }
1335 u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
1336}
1337
1338fn validate_prequantized_htj2k97_image(
1339 image: &PrequantizedHtj2k97Image,
1340 guard_bits: u8,
1341 step_sizes: &[QuantStepSize],
1342) -> Result<(), &'static str> {
1343 for component in &image.components {
1344 if component.resolutions.is_empty() {
1345 return Err("prequantized components must contain at least one resolution");
1346 }
1347 validate_prequantized_resolution(
1348 &component.resolutions[0],
1349 &[J2kSubBandType::LowLow],
1350 guard_bits,
1351 &step_sizes[0..1],
1352 )?;
1353 for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
1354 let step_base = 1 + (level_index - 1) * 3;
1355 validate_prequantized_resolution(
1356 resolution,
1357 &[
1358 J2kSubBandType::HighLow,
1359 J2kSubBandType::LowHigh,
1360 J2kSubBandType::HighHigh,
1361 ],
1362 guard_bits,
1363 &step_sizes[step_base..step_base + 3],
1364 )?;
1365 }
1366 }
1367
1368 Ok(())
1369}
1370
1371fn validate_preencoded_htj2k97_image(
1372 image: &PreencodedHtj2k97Image,
1373 guard_bits: u8,
1374 step_sizes: &[QuantStepSize],
1375) -> Result<(), &'static str> {
1376 for component in &image.components {
1377 if component.resolutions.is_empty() {
1378 return Err("preencoded components must contain at least one resolution");
1379 }
1380 validate_preencoded_resolution(
1381 &component.resolutions[0],
1382 &[J2kSubBandType::LowLow],
1383 guard_bits,
1384 &step_sizes[0..1],
1385 )?;
1386 for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
1387 let step_base = 1 + (level_index - 1) * 3;
1388 validate_preencoded_resolution(
1389 resolution,
1390 &[
1391 J2kSubBandType::HighLow,
1392 J2kSubBandType::LowHigh,
1393 J2kSubBandType::HighHigh,
1394 ],
1395 guard_bits,
1396 &step_sizes[step_base..step_base + 3],
1397 )?;
1398 }
1399 }
1400
1401 Ok(())
1402}
1403
1404fn validate_preencoded_compact_htj2k97_image(
1405 image: &PreencodedHtj2k97CompactImage,
1406 guard_bits: u8,
1407 step_sizes: &[QuantStepSize],
1408) -> Result<(), &'static str> {
1409 for component in &image.components {
1410 if component.resolutions.is_empty() {
1411 return Err("preencoded components must contain at least one resolution");
1412 }
1413 validate_preencoded_compact_resolution(
1414 &component.resolutions[0],
1415 &[J2kSubBandType::LowLow],
1416 guard_bits,
1417 &step_sizes[0..1],
1418 image.payload.len(),
1419 )?;
1420 for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
1421 let step_base = 1 + (level_index - 1) * 3;
1422 validate_preencoded_compact_resolution(
1423 resolution,
1424 &[
1425 J2kSubBandType::HighLow,
1426 J2kSubBandType::LowHigh,
1427 J2kSubBandType::HighHigh,
1428 ],
1429 guard_bits,
1430 &step_sizes[step_base..step_base + 3],
1431 image.payload.len(),
1432 )?;
1433 }
1434 }
1435
1436 Ok(())
1437}
1438
1439fn validate_prequantized_resolution(
1440 resolution: &PrequantizedHtj2k97Resolution,
1441 expected_subbands: &[J2kSubBandType],
1442 guard_bits: u8,
1443 step_sizes: &[QuantStepSize],
1444) -> Result<(), &'static str> {
1445 if resolution.subbands.len() != expected_subbands.len() {
1446 return Err("prequantized resolution subband count mismatch");
1447 }
1448 for ((subband, expected_subband), step_size) in resolution
1449 .subbands
1450 .iter()
1451 .zip(expected_subbands)
1452 .zip(step_sizes)
1453 {
1454 if subband.sub_band_type != *expected_subband {
1455 return Err("prequantized resolution subband order mismatch");
1456 }
1457 let expected_blocks = subband
1458 .num_cbs_x
1459 .checked_mul(subband.num_cbs_y)
1460 .ok_or("prequantized code-block count overflow")?;
1461 if expected_blocks == 0 {
1462 if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
1463 return Err("empty prequantized subbands must not contain code-block data");
1464 }
1465 continue;
1466 }
1467 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
1468 let expected_total_bitplanes = guard_bits
1469 .saturating_add(step_size.exponent as u8)
1470 .saturating_sub(1);
1471 if subband.total_bitplanes != expected_total_bitplanes {
1472 return Err("prequantized subband bitplane count mismatch");
1473 }
1474 if usize::try_from(expected_blocks).map_err(|_| "prequantized code-block count overflow")?
1475 != subband.code_blocks.len()
1476 {
1477 return Err("prequantized code-block count mismatch");
1478 }
1479 for block in &subband.code_blocks {
1480 if block.width == 0 || block.height == 0 {
1481 return Err("prequantized code-block dimensions must be non-zero");
1482 }
1483 validate_band_len(block.coefficients.len(), block.width, block.height)?;
1484 }
1485 }
1486
1487 Ok(())
1488}
1489
1490fn validate_preencoded_resolution(
1491 resolution: &PreencodedHtj2k97Resolution,
1492 expected_subbands: &[J2kSubBandType],
1493 guard_bits: u8,
1494 step_sizes: &[QuantStepSize],
1495) -> Result<(), &'static str> {
1496 if resolution.subbands.len() != expected_subbands.len() {
1497 return Err("preencoded resolution subband count mismatch");
1498 }
1499 for ((subband, expected_subband), step_size) in resolution
1500 .subbands
1501 .iter()
1502 .zip(expected_subbands)
1503 .zip(step_sizes)
1504 {
1505 if subband.sub_band_type != *expected_subband {
1506 return Err("preencoded resolution subband order mismatch");
1507 }
1508 let expected_blocks = subband
1509 .num_cbs_x
1510 .checked_mul(subband.num_cbs_y)
1511 .ok_or("preencoded code-block count overflow")?;
1512 if expected_blocks == 0 {
1513 if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
1514 return Err("empty preencoded subbands must not contain code-block data");
1515 }
1516 continue;
1517 }
1518 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
1519 let expected_total_bitplanes = guard_bits
1520 .saturating_add(step_size.exponent as u8)
1521 .saturating_sub(1);
1522 if subband.total_bitplanes != expected_total_bitplanes {
1523 return Err("preencoded subband bitplane count mismatch");
1524 }
1525 if usize::try_from(expected_blocks).map_err(|_| "preencoded code-block count overflow")?
1526 != subband.code_blocks.len()
1527 {
1528 return Err("preencoded code-block count mismatch");
1529 }
1530 for block in &subband.code_blocks {
1531 if block.width == 0 || block.height == 0 {
1532 return Err("preencoded code-block dimensions must be non-zero");
1533 }
1534 validate_preencoded_code_block_payload(&block.encoded, subband.total_bitplanes)?;
1535 }
1536 }
1537
1538 Ok(())
1539}
1540
1541fn validate_preencoded_compact_resolution(
1542 resolution: &PreencodedHtj2k97CompactResolution,
1543 expected_subbands: &[J2kSubBandType],
1544 guard_bits: u8,
1545 step_sizes: &[QuantStepSize],
1546 payload_len: usize,
1547) -> Result<(), &'static str> {
1548 if resolution.subbands.len() != expected_subbands.len() {
1549 return Err("preencoded resolution subband count mismatch");
1550 }
1551 for ((subband, expected_subband), step_size) in resolution
1552 .subbands
1553 .iter()
1554 .zip(expected_subbands)
1555 .zip(step_sizes)
1556 {
1557 if subband.sub_band_type != *expected_subband {
1558 return Err("preencoded resolution subband order mismatch");
1559 }
1560 let expected_blocks = subband
1561 .num_cbs_x
1562 .checked_mul(subband.num_cbs_y)
1563 .ok_or("preencoded code-block count overflow")?;
1564 if expected_blocks == 0 {
1565 if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
1566 return Err("empty preencoded subbands must not contain code-block data");
1567 }
1568 continue;
1569 }
1570 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
1571 let expected_total_bitplanes = guard_bits
1572 .saturating_add(step_size.exponent as u8)
1573 .saturating_sub(1);
1574 if subband.total_bitplanes != expected_total_bitplanes {
1575 return Err("preencoded subband bitplane count mismatch");
1576 }
1577 if usize::try_from(expected_blocks).map_err(|_| "preencoded code-block count overflow")?
1578 != subband.code_blocks.len()
1579 {
1580 return Err("preencoded code-block count mismatch");
1581 }
1582 for block in &subband.code_blocks {
1583 if block.width == 0 || block.height == 0 {
1584 return Err("preencoded code-block dimensions must be non-zero");
1585 }
1586 validate_preencoded_compact_code_block_payload(
1587 block,
1588 payload_len,
1589 subband.total_bitplanes,
1590 )?;
1591 }
1592 }
1593
1594 Ok(())
1595}
1596
1597fn validate_preencoded_code_block_payload(
1598 block: &EncodedHtJ2kCodeBlock,
1599 total_bitplanes: u8,
1600) -> Result<(), &'static str> {
1601 let data_len = u32::try_from(block.data.len()).map_err(|_| "HTJ2K payload too large")?;
1602 if block.num_coding_passes == 0 {
1603 if data_len != 0 || block.cleanup_length != 0 || block.refinement_length != 0 {
1604 return Err("empty HTJ2K code-block payload metadata mismatch");
1605 }
1606 if block.num_zero_bitplanes != total_bitplanes {
1607 return Err("empty HTJ2K code-block zero-bitplane count mismatch");
1608 }
1609 return Ok(());
1610 }
1611 if block.num_coding_passes > 164 {
1612 return Err("HTJ2K code-block coding pass count out of range");
1613 }
1614 if block.num_zero_bitplanes >= total_bitplanes {
1615 return Err("HTJ2K code-block zero-bitplane count out of range");
1616 }
1617 let segment_len = block
1618 .cleanup_length
1619 .checked_add(block.refinement_length)
1620 .ok_or("HTJ2K payload segment length overflow")?;
1621 if segment_len != data_len {
1622 return Err("HTJ2K payload segment length mismatch");
1623 }
1624 Ok(())
1625}
1626
1627fn validate_preencoded_compact_code_block_payload(
1628 block: &PreencodedHtj2k97CompactCodeBlock,
1629 payload_len: usize,
1630 total_bitplanes: u8,
1631) -> Result<(), &'static str> {
1632 if block.payload_range.start > block.payload_range.end || block.payload_range.end > payload_len
1633 {
1634 return Err("HTJ2K payload range out of bounds");
1635 }
1636 let data_len = u32::try_from(block.payload_range.end - block.payload_range.start)
1637 .map_err(|_| "HTJ2K payload too large")?;
1638 if block.num_coding_passes == 0 {
1639 if data_len != 0 || block.cleanup_length != 0 || block.refinement_length != 0 {
1640 return Err("empty HTJ2K code-block payload metadata mismatch");
1641 }
1642 if block.num_zero_bitplanes != total_bitplanes {
1643 return Err("empty HTJ2K code-block zero-bitplane count mismatch");
1644 }
1645 return Ok(());
1646 }
1647 if block.num_coding_passes > 164 {
1648 return Err("HTJ2K code-block coding pass count out of range");
1649 }
1650 if block.num_zero_bitplanes >= total_bitplanes {
1651 return Err("HTJ2K code-block zero-bitplane count out of range");
1652 }
1653 let segment_len = block
1654 .cleanup_length
1655 .checked_add(block.refinement_length)
1656 .ok_or("HTJ2K payload segment length overflow")?;
1657 if segment_len != data_len {
1658 return Err("HTJ2K payload segment length mismatch");
1659 }
1660 Ok(())
1661}
1662
1663fn prepared_resolution_packets_from_prequantized_component(
1664 component_idx: usize,
1665 component: &PrequantizedHtj2k97Component,
1666) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
1667 let component_idx = u8::try_from(component_idx).map_err(|_| "component index exceeds u8")?;
1668 component
1669 .resolutions
1670 .iter()
1671 .enumerate()
1672 .map(|(resolution_idx, resolution)| {
1673 Ok(PreparedResolutionPacket {
1674 component: component_idx,
1675 resolution: u32::try_from(resolution_idx)
1676 .map_err(|_| "resolution index exceeds u32")?,
1677 precinct: 0,
1678 subbands: resolution
1679 .subbands
1680 .iter()
1681 .map(prepared_subband_from_prequantized)
1682 .collect::<Result<Vec<_>, &'static str>>()?,
1683 })
1684 })
1685 .collect()
1686}
1687
1688fn prepared_subband_from_prequantized(
1689 subband: &PrequantizedHtj2k97Subband,
1690) -> Result<PreparedEncodeSubband, &'static str> {
1691 Ok(PreparedEncodeSubband {
1692 code_blocks: subband
1693 .code_blocks
1694 .iter()
1695 .map(|block| PreparedEncodeCodeBlock {
1696 coefficients: block.coefficients.clone(),
1697 width: block.width,
1698 height: block.height,
1699 })
1700 .collect(),
1701 preencoded_ht_code_blocks: None,
1702 num_cbs_x: subband.num_cbs_x,
1703 num_cbs_y: subband.num_cbs_y,
1704 code_block_width: subband
1705 .code_blocks
1706 .iter()
1707 .map(|block| block.width)
1708 .max()
1709 .unwrap_or(0),
1710 code_block_height: subband
1711 .code_blocks
1712 .iter()
1713 .map(|block| block.height)
1714 .max()
1715 .unwrap_or(0),
1716 width: precomputed_subband_width(
1717 subband.num_cbs_x,
1718 subband.code_blocks.iter().map(|block| block.width),
1719 ),
1720 height: precomputed_subband_height(
1721 subband.num_cbs_x,
1722 subband.num_cbs_y,
1723 subband.code_blocks.iter().map(|block| block.height),
1724 ),
1725 sub_band_type: internal_sub_band_type(subband.sub_band_type),
1726 total_bitplanes: subband.total_bitplanes,
1727 block_coding_mode: BlockCodingMode::HighThroughput,
1728 })
1729}
1730
1731fn precomputed_subband_width(width_in_blocks: u32, widths: impl Iterator<Item = u32>) -> u32 {
1732 if width_in_blocks == 0 {
1733 return 0;
1734 }
1735
1736 widths.take(width_in_blocks as usize).sum()
1737}
1738
1739fn precomputed_subband_height(
1740 width_in_blocks: u32,
1741 height_in_blocks: u32,
1742 heights: impl Iterator<Item = u32>,
1743) -> u32 {
1744 if width_in_blocks == 0 || height_in_blocks == 0 {
1745 return 0;
1746 }
1747
1748 heights
1749 .step_by(width_in_blocks as usize)
1750 .take(height_in_blocks as usize)
1751 .sum()
1752}
1753
1754fn prepared_resolution_packets_from_preencoded_component(
1755 component_idx: usize,
1756 component: &PreencodedHtj2k97Component,
1757) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
1758 let component_idx = u8::try_from(component_idx).map_err(|_| "component index exceeds u8")?;
1759 component
1760 .resolutions
1761 .iter()
1762 .enumerate()
1763 .map(|(resolution_idx, resolution)| {
1764 Ok(PreparedResolutionPacket {
1765 component: component_idx,
1766 resolution: u32::try_from(resolution_idx)
1767 .map_err(|_| "resolution index exceeds u32")?,
1768 precinct: 0,
1769 subbands: resolution
1770 .subbands
1771 .iter()
1772 .map(prepared_subband_from_preencoded)
1773 .collect::<Result<Vec<_>, &'static str>>()?,
1774 })
1775 })
1776 .collect()
1777}
1778
1779fn prepared_resolution_packets_from_preencoded_component_owned(
1780 component_idx: usize,
1781 component: PreencodedHtj2k97Component,
1782) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
1783 let component_idx = u8::try_from(component_idx).map_err(|_| "component index exceeds u8")?;
1784 component
1785 .resolutions
1786 .into_iter()
1787 .enumerate()
1788 .map(|(resolution_idx, resolution)| {
1789 Ok(PreparedResolutionPacket {
1790 component: component_idx,
1791 resolution: u32::try_from(resolution_idx)
1792 .map_err(|_| "resolution index exceeds u32")?,
1793 precinct: 0,
1794 subbands: resolution
1795 .subbands
1796 .into_iter()
1797 .map(prepared_subband_from_preencoded_owned)
1798 .collect::<Result<Vec<_>, &'static str>>()?,
1799 })
1800 })
1801 .collect()
1802}
1803
1804fn prepared_resolution_packets_from_preencoded_compact_component<'a>(
1805 component_idx: usize,
1806 component: &'a PreencodedHtj2k97CompactComponent,
1807 payload: &'a [u8],
1808) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
1809 let component_idx = u8::try_from(component_idx).map_err(|_| "component index exceeds u8")?;
1810 component
1811 .resolutions
1812 .iter()
1813 .enumerate()
1814 .map(|(resolution_idx, resolution)| {
1815 Ok(PreparedCompactResolutionPacket {
1816 component: component_idx,
1817 resolution: u32::try_from(resolution_idx)
1818 .map_err(|_| "resolution index exceeds u32")?,
1819 precinct: 0,
1820 subbands: resolution
1821 .subbands
1822 .iter()
1823 .map(|subband| prepared_subband_from_preencoded_compact(subband, payload))
1824 .collect::<Result<Vec<_>, &'static str>>()?,
1825 })
1826 })
1827 .collect()
1828}
1829
1830fn prepared_subband_from_preencoded(
1831 subband: &PreencodedHtj2k97Subband,
1832) -> Result<PreparedEncodeSubband, &'static str> {
1833 Ok(PreparedEncodeSubband {
1834 code_blocks: subband
1835 .code_blocks
1836 .iter()
1837 .map(|block| PreparedEncodeCodeBlock {
1838 coefficients: Vec::new(),
1839 width: block.width,
1840 height: block.height,
1841 })
1842 .collect(),
1843 preencoded_ht_code_blocks: Some(
1844 subband
1845 .code_blocks
1846 .iter()
1847 .map(|block| block.encoded.clone())
1848 .collect(),
1849 ),
1850 num_cbs_x: subband.num_cbs_x,
1851 num_cbs_y: subband.num_cbs_y,
1852 code_block_width: subband
1853 .code_blocks
1854 .iter()
1855 .map(|block| block.width)
1856 .max()
1857 .unwrap_or(0),
1858 code_block_height: subband
1859 .code_blocks
1860 .iter()
1861 .map(|block| block.height)
1862 .max()
1863 .unwrap_or(0),
1864 width: precomputed_subband_width(
1865 subband.num_cbs_x,
1866 subband.code_blocks.iter().map(|block| block.width),
1867 ),
1868 height: precomputed_subband_height(
1869 subband.num_cbs_x,
1870 subband.num_cbs_y,
1871 subband.code_blocks.iter().map(|block| block.height),
1872 ),
1873 sub_band_type: internal_sub_band_type(subband.sub_band_type),
1874 total_bitplanes: subband.total_bitplanes,
1875 block_coding_mode: BlockCodingMode::HighThroughput,
1876 })
1877}
1878
1879fn prepared_subband_from_preencoded_owned(
1880 subband: PreencodedHtj2k97Subband,
1881) -> Result<PreparedEncodeSubband, &'static str> {
1882 let code_block_width = subband
1883 .code_blocks
1884 .iter()
1885 .map(|block| block.width)
1886 .max()
1887 .unwrap_or(0);
1888 let code_block_height = subband
1889 .code_blocks
1890 .iter()
1891 .map(|block| block.height)
1892 .max()
1893 .unwrap_or(0);
1894 let width = precomputed_subband_width(
1895 subband.num_cbs_x,
1896 subband.code_blocks.iter().map(|block| block.width),
1897 );
1898 let height = precomputed_subband_height(
1899 subband.num_cbs_x,
1900 subband.num_cbs_y,
1901 subband.code_blocks.iter().map(|block| block.height),
1902 );
1903 let code_blocks = subband
1904 .code_blocks
1905 .into_iter()
1906 .map(|block| {
1907 let PreencodedHtj2k97CodeBlock {
1908 width,
1909 height,
1910 encoded,
1911 } = block;
1912 (
1913 PreparedEncodeCodeBlock {
1914 coefficients: Vec::new(),
1915 width,
1916 height,
1917 },
1918 encoded,
1919 )
1920 })
1921 .collect::<Vec<_>>();
1922 let (code_blocks, preencoded_ht_code_blocks): (Vec<_>, Vec<_>) =
1923 code_blocks.into_iter().unzip();
1924
1925 Ok(PreparedEncodeSubband {
1926 code_blocks,
1927 preencoded_ht_code_blocks: Some(preencoded_ht_code_blocks),
1928 num_cbs_x: subband.num_cbs_x,
1929 num_cbs_y: subband.num_cbs_y,
1930 code_block_width,
1931 code_block_height,
1932 width,
1933 height,
1934 sub_band_type: internal_sub_band_type(subband.sub_band_type),
1935 total_bitplanes: subband.total_bitplanes,
1936 block_coding_mode: BlockCodingMode::HighThroughput,
1937 })
1938}
1939
1940fn prepared_subband_from_preencoded_compact<'a>(
1941 subband: &'a PreencodedHtj2k97CompactSubband,
1942 payload: &'a [u8],
1943) -> Result<PreparedCompactSubband<'a>, &'static str> {
1944 let code_blocks = subband
1945 .code_blocks
1946 .iter()
1947 .map(|block| {
1948 Ok(PreparedCompactCodeBlock {
1949 data: compact_payload_slice(payload, &block.payload_range)?,
1950 cleanup_length: block.cleanup_length,
1951 refinement_length: block.refinement_length,
1952 num_coding_passes: block.num_coding_passes,
1953 num_zero_bitplanes: block.num_zero_bitplanes,
1954 })
1955 })
1956 .collect::<Result<Vec<_>, &'static str>>()?;
1957
1958 Ok(PreparedCompactSubband {
1959 code_blocks,
1960 num_cbs_x: subband.num_cbs_x,
1961 num_cbs_y: subband.num_cbs_y,
1962 })
1963}
1964
1965fn compact_payload_slice<'a>(
1966 payload: &'a [u8],
1967 range: &Range<usize>,
1968) -> Result<&'a [u8], &'static str> {
1969 if range.start > range.end || range.end > payload.len() {
1970 return Err("HTJ2K payload range out of bounds");
1971 }
1972 Ok(&payload[range.clone()])
1973}
1974
1975fn zero_pixel_buffer(
1976 width: u32,
1977 height: u32,
1978 num_components: u8,
1979 bit_depth: u8,
1980) -> Result<Vec<u8>, &'static str> {
1981 let bytes_per_sample = if bit_depth <= 8 { 1usize } else { 2usize };
1982 let len = width as usize;
1983 let len = len
1984 .checked_mul(height as usize)
1985 .and_then(|value| value.checked_mul(usize::from(num_components)))
1986 .and_then(|value| value.checked_mul(bytes_per_sample))
1987 .ok_or("pixel buffer dimensions overflow")?;
1988 Ok(vec![0; len])
1989}
1990
1991struct PrecomputedDwtAccelerator<'a, A: J2kEncodeStageAccelerator> {
1992 outputs: Vec<J2kForwardDwt53Output>,
1993 encode_accelerator: &'a mut A,
1994}
1995
1996struct PrecomputedDwt97Accelerator<'a, A: J2kEncodeStageAccelerator> {
1997 outputs: Vec<J2kForwardDwt97Output>,
1998 encode_accelerator: &'a mut A,
1999}
2000
2001impl<A: J2kEncodeStageAccelerator> J2kEncodeStageAccelerator for PrecomputedDwtAccelerator<'_, A> {
2002 fn dispatch_report(&self) -> crate::J2kEncodeDispatchReport {
2003 self.encode_accelerator.dispatch_report()
2004 }
2005
2006 fn encode_forward_dwt53(
2007 &mut self,
2008 _job: J2kForwardDwt53Job<'_>,
2009 ) -> Result<Option<J2kForwardDwt53Output>, &'static str> {
2010 if self.outputs.is_empty() {
2011 return Err("precomputed DWT output exhausted");
2012 }
2013
2014 Ok(Some(self.outputs.remove(0)))
2015 }
2016
2017 fn encode_quantize_subband(
2018 &mut self,
2019 job: J2kQuantizeSubbandJob<'_>,
2020 ) -> Result<Option<Vec<i32>>, &'static str> {
2021 self.encode_accelerator.encode_quantize_subband(job)
2022 }
2023
2024 fn encode_tier1_code_block(
2025 &mut self,
2026 job: J2kTier1CodeBlockEncodeJob<'_>,
2027 ) -> Result<Option<EncodedJ2kCodeBlock>, &'static str> {
2028 self.encode_accelerator.encode_tier1_code_block(job)
2029 }
2030
2031 fn encode_tier1_code_blocks(
2032 &mut self,
2033 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
2034 ) -> Result<Option<Vec<EncodedJ2kCodeBlock>>, &'static str> {
2035 self.encode_accelerator.encode_tier1_code_blocks(jobs)
2036 }
2037
2038 fn encode_ht_code_block(
2039 &mut self,
2040 job: crate::J2kHtCodeBlockEncodeJob<'_>,
2041 ) -> Result<Option<EncodedHtJ2kCodeBlock>, &'static str> {
2042 self.encode_accelerator.encode_ht_code_block(job)
2043 }
2044
2045 fn encode_ht_code_blocks(
2046 &mut self,
2047 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
2048 ) -> Result<Option<Vec<EncodedHtJ2kCodeBlock>>, &'static str> {
2049 self.encode_accelerator.encode_ht_code_blocks(jobs)
2050 }
2051
2052 fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
2053 self.encode_accelerator
2054 .prefer_parallel_cpu_code_block_fallback()
2055 }
2056
2057 fn prefer_parallel_cpu_tile_encode(&self) -> bool {
2058 self.encode_accelerator.prefer_parallel_cpu_tile_encode()
2059 }
2060
2061 fn encode_packetization(
2062 &mut self,
2063 job: J2kPacketizationEncodeJob<'_>,
2064 ) -> Result<Option<Vec<u8>>, &'static str> {
2065 self.encode_accelerator.encode_packetization(job)
2066 }
2067}
2068
2069impl<A: J2kEncodeStageAccelerator> J2kEncodeStageAccelerator
2070 for PrecomputedDwt97Accelerator<'_, A>
2071{
2072 fn dispatch_report(&self) -> crate::J2kEncodeDispatchReport {
2073 self.encode_accelerator.dispatch_report()
2074 }
2075
2076 fn encode_forward_dwt97(
2077 &mut self,
2078 _job: J2kForwardDwt97Job<'_>,
2079 ) -> Result<Option<J2kForwardDwt97Output>, &'static str> {
2080 if self.outputs.is_empty() {
2081 return Err("precomputed DWT output exhausted");
2082 }
2083
2084 Ok(Some(self.outputs.remove(0)))
2085 }
2086
2087 fn encode_quantize_subband(
2088 &mut self,
2089 job: J2kQuantizeSubbandJob<'_>,
2090 ) -> Result<Option<Vec<i32>>, &'static str> {
2091 self.encode_accelerator.encode_quantize_subband(job)
2092 }
2093
2094 fn encode_tier1_code_block(
2095 &mut self,
2096 job: J2kTier1CodeBlockEncodeJob<'_>,
2097 ) -> Result<Option<EncodedJ2kCodeBlock>, &'static str> {
2098 self.encode_accelerator.encode_tier1_code_block(job)
2099 }
2100
2101 fn encode_tier1_code_blocks(
2102 &mut self,
2103 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
2104 ) -> Result<Option<Vec<EncodedJ2kCodeBlock>>, &'static str> {
2105 self.encode_accelerator.encode_tier1_code_blocks(jobs)
2106 }
2107
2108 fn encode_ht_code_block(
2109 &mut self,
2110 job: crate::J2kHtCodeBlockEncodeJob<'_>,
2111 ) -> Result<Option<EncodedHtJ2kCodeBlock>, &'static str> {
2112 self.encode_accelerator.encode_ht_code_block(job)
2113 }
2114
2115 fn encode_ht_code_blocks(
2116 &mut self,
2117 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
2118 ) -> Result<Option<Vec<EncodedHtJ2kCodeBlock>>, &'static str> {
2119 self.encode_accelerator.encode_ht_code_blocks(jobs)
2120 }
2121
2122 fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
2123 self.encode_accelerator
2124 .prefer_parallel_cpu_code_block_fallback()
2125 }
2126
2127 fn prefer_parallel_cpu_tile_encode(&self) -> bool {
2128 self.encode_accelerator.prefer_parallel_cpu_tile_encode()
2129 }
2130
2131 fn encode_packetization(
2132 &mut self,
2133 job: J2kPacketizationEncodeJob<'_>,
2134 ) -> Result<Option<Vec<u8>>, &'static str> {
2135 self.encode_accelerator.encode_packetization(job)
2136 }
2137}
2138
2139fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
2140 if options.use_ht_block_coding {
2141 BlockCodingMode::HighThroughput
2142 } else {
2143 BlockCodingMode::Classic
2144 }
2145}
2146
2147fn validate_irreversible_quantization_scale(scale: f32) -> Result<(), &'static str> {
2148 if scale.is_finite() && scale > 0.0 {
2149 Ok(())
2150 } else {
2151 Err("irreversible quantization scale must be finite and greater than zero")
2152 }
2153}
2154
2155fn validate_irreversible_quantization_profile(options: &EncodeOptions) -> Result<(), &'static str> {
2156 validate_irreversible_quantization_scale(options.irreversible_quantization_scale)?;
2157 if quantize::subband_scales_all_valid(options.irreversible_quantization_subband_scales) {
2158 Ok(())
2159 } else {
2160 Err("irreversible quantization subband scales must be finite and greater than zero")
2161 }
2162}
2163
2164fn validate_htj2k_codestream(
2165 codestream: &[u8],
2166 pixels: &[u8],
2167 width: u32,
2168 height: u32,
2169 num_components: u8,
2170 bit_depth: u8,
2171 signed: bool,
2172 reversible: bool,
2173) -> Result<(), &'static str> {
2174 let image = Image::new(codestream, &DecodeSettings::default())
2175 .map_err(|_| "generated HTJ2K codestream failed self-validation")?;
2176 let decoded = image
2177 .decode_native()
2178 .map_err(|_| "generated HTJ2K codestream failed self-validation")?;
2179
2180 if decoded.width != width
2181 || decoded.height != height
2182 || decoded.bit_depth != bit_depth
2183 || decoded.num_components != num_components
2184 {
2185 return Err("generated HTJ2K codestream failed self-validation");
2186 }
2187
2188 if reversible && !native_samples_equal(pixels, &decoded.data, bit_depth, signed) {
2189 return Err("generated HTJ2K codestream did not roundtrip");
2190 }
2191
2192 Ok(())
2193}
2194
2195fn native_samples_equal(expected: &[u8], actual: &[u8], bit_depth: u8, signed: bool) -> bool {
2196 if expected.len() != actual.len() {
2197 return false;
2198 }
2199
2200 let bytes_per_sample = if bit_depth <= 8 { 1 } else { 2 };
2201 let sample_count = expected.len() / bytes_per_sample;
2202 (0..sample_count).all(|sample_index| {
2203 decode_native_sample(expected, sample_index, bit_depth, signed)
2204 == decode_native_sample(actual, sample_index, bit_depth, signed)
2205 })
2206}
2207
2208fn decode_native_sample(bytes: &[u8], sample_index: usize, bit_depth: u8, signed: bool) -> i32 {
2209 let byte_offset = sample_index * if bit_depth <= 8 { 1 } else { 2 };
2210 let mask = (1u32 << u32::from(bit_depth)) - 1;
2211 let raw = if bit_depth <= 8 {
2212 u32::from(bytes[byte_offset])
2213 } else {
2214 u32::from(u16::from_le_bytes([
2215 bytes[byte_offset],
2216 bytes[byte_offset + 1],
2217 ]))
2218 } & mask;
2219
2220 if signed {
2221 let shift = 32 - u32::from(bit_depth);
2222 ((raw << shift) as i32) >> shift
2223 } else {
2224 raw as i32
2225 }
2226}
2227
2228fn encode_impl(
2229 pixels: &[u8],
2230 width: u32,
2231 height: u32,
2232 num_components: u8,
2233 bit_depth: u8,
2234 signed: bool,
2235 options: &EncodeOptions,
2236 block_coding_mode: BlockCodingMode,
2237 accelerator: &mut impl J2kEncodeStageAccelerator,
2238) -> Result<Vec<u8>, &'static str> {
2239 if width == 0 || height == 0 {
2240 return Err("invalid dimensions");
2241 }
2242 if num_components == 0 || num_components > 4 {
2243 return Err("unsupported component count");
2244 }
2245 if bit_depth == 0 || bit_depth > 16 {
2246 return Err("unsupported bit depth");
2247 }
2248 if options.num_layers == 0 || options.num_layers > 32 {
2249 return Err("unsupported quality layer count");
2250 }
2251 if !options.quality_layer_byte_targets.is_empty()
2252 && options.quality_layer_byte_targets.len() != usize::from(options.num_layers)
2253 {
2254 return Err("quality layer byte target count must match quality layer count");
2255 }
2256 if !options.reversible {
2257 validate_irreversible_quantization_profile(options)?;
2258 }
2259
2260 let num_pixels = (width as usize)
2261 .checked_mul(height as usize)
2262 .ok_or("image dimensions overflow")?;
2263 let bytes_per_sample = if bit_depth <= 8 { 1 } else { 2 };
2264 let expected_len = num_pixels
2265 .checked_mul(num_components as usize)
2266 .and_then(|len| len.checked_mul(bytes_per_sample))
2267 .ok_or("image dimensions overflow")?;
2268 if pixels.len() < expected_len {
2269 return Err("pixel data too short");
2270 }
2271 let component_sampling = component_sampling_for_options(options, num_components)?;
2272 if let Some((tile_width, tile_height)) = options.tile_size {
2273 if tile_width == 0 || tile_height == 0 {
2274 return Err("invalid tile dimensions");
2275 }
2276 if component_sampling
2277 .iter()
2278 .any(|sampling| *sampling != (1, 1))
2279 {
2280 return Err("multi-tile encode with component sampling is not implemented");
2281 }
2282 if tile_width < width || tile_height < height {
2283 return encode_multitile_impl(
2284 pixels,
2285 width,
2286 height,
2287 num_components,
2288 bit_depth,
2289 signed,
2290 options,
2291 block_coding_mode,
2292 accelerator,
2293 tile_width,
2294 tile_height,
2295 );
2296 }
2297 }
2298
2299 let profile_enabled = profile::profile_stages_enabled();
2300 let total_start = profile::profile_now(profile_enabled);
2301
2302 let use_mct = options.use_mct && num_components >= 3;
2303 let num_levels = options.num_decomposition_levels.min(
2304 max_decomposition_levels(width, height),
2306 );
2307 let guard_bits = if options.reversible {
2308 if use_mct {
2309 options.guard_bits.max(2)
2310 } else {
2311 options.guard_bits
2312 }
2313 } else {
2314 options.guard_bits.max(2)
2315 };
2316 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
2317 bit_depth,
2318 num_levels,
2319 options.reversible,
2320 guard_bits,
2321 options.irreversible_quantization_scale,
2322 options.irreversible_quantization_subband_scales,
2323 );
2324 let quant_params: Vec<(u16, u16)> = step_sizes
2325 .iter()
2326 .map(|s| (s.exponent, s.mantissa))
2327 .collect();
2328 let cb_width = 1u32 << (options.code_block_width_exp + 2);
2329 let cb_height = 1u32 << (options.code_block_height_exp + 2);
2330 let precinct_exponents = precinct_exponents_for_options(options, num_levels)?;
2331 let params = EncodeParams {
2332 width,
2333 height,
2334 tile_width: options
2335 .tile_size
2336 .map_or(width, |(tile_width, _)| tile_width),
2337 tile_height: options
2338 .tile_size
2339 .map_or(height, |(_, tile_height)| tile_height),
2340 num_components,
2341 bit_depth,
2342 signed,
2343 num_decomposition_levels: num_levels,
2344 reversible: options.reversible,
2345 code_block_width_exp: options.code_block_width_exp,
2346 code_block_height_exp: options.code_block_height_exp,
2347 num_layers: options.num_layers,
2348 use_mct,
2349 guard_bits,
2350 block_coding_mode,
2351 progression_order: options.progression_order,
2352 write_tlm: options.write_tlm,
2353 write_plt: options.write_plt,
2354 write_plm: options.write_plm,
2355 write_sop: options.write_sop,
2356 write_eph: options.write_eph,
2357 terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
2358 && options.num_layers > 1,
2359 component_sampling,
2360 precinct_exponents,
2361 };
2362
2363 let stage_start = profile::profile_now(profile_enabled);
2364 if block_coding_mode == BlockCodingMode::HighThroughput
2365 && !(params.write_plt || params.write_plm || params.write_sop || params.write_eph)
2366 {
2367 if let Some(tile_data) = accelerator.encode_htj2k_tile(J2kHtj2kTileEncodeJob {
2368 pixels,
2369 width,
2370 height,
2371 num_components,
2372 bit_depth,
2373 signed,
2374 num_decomposition_levels: num_levels,
2375 reversible: options.reversible,
2376 use_mct,
2377 guard_bits,
2378 code_block_width: cb_width,
2379 code_block_height: cb_height,
2380 progression_order: public_packetization_progression_order(options.progression_order),
2381 component_sampling: ¶ms.component_sampling,
2382 quantization_steps: &quant_params,
2383 })? {
2384 let tile_body_us = profile::elapsed_us(stage_start);
2385 let stage_start = profile::profile_now(profile_enabled);
2386 let codestream = codestream_write::write_codestream(¶ms, &tile_data, &quant_params);
2387 let codestream_us = profile::elapsed_us(stage_start);
2388 if profile_enabled {
2389 profile::emit_profile_row(
2390 "encode",
2391 "accelerated",
2392 &[
2393 ("tile_body_us", tile_body_us),
2394 ("codestream_us", codestream_us),
2395 ("total_us", profile::elapsed_us(total_start)),
2396 ],
2397 );
2398 }
2399 return Ok(codestream);
2400 }
2401 }
2402
2403 let stage_start = profile::profile_now(profile_enabled);
2405 let mut components = match accelerator.encode_deinterleave(J2kDeinterleaveToF32Job {
2406 pixels,
2407 num_pixels,
2408 num_components,
2409 bit_depth,
2410 signed,
2411 })? {
2412 Some(components) => {
2413 validate_deinterleaved_components(components, num_components, num_pixels)?
2414 }
2415 None => deinterleave_to_f32(pixels, num_pixels, num_components, bit_depth, signed),
2416 };
2417 let deinterleave_us = profile::elapsed_us(stage_start);
2418
2419 let stage_start = profile::profile_now(profile_enabled);
2421 if use_mct {
2422 if options.reversible {
2423 if !try_encode_forward_rct(&mut components, accelerator)? {
2424 forward_mct::forward_rct(&mut components);
2425 }
2426 } else if !try_encode_forward_ict(&mut components, accelerator)? {
2427 forward_mct::forward_ict(&mut components);
2428 }
2429 }
2430 let mct_us = profile::elapsed_us(stage_start);
2431
2432 let stage_start = profile::profile_now(profile_enabled);
2434 let decompositions: Vec<DwtDecomposition> = components
2435 .iter()
2436 .map(|comp| {
2437 encode_forward_dwt(
2438 comp,
2439 width,
2440 height,
2441 num_levels,
2442 options.reversible,
2443 accelerator,
2444 )
2445 })
2446 .collect::<Result<Vec<_>, &'static str>>()?;
2447 let dwt_us = profile::elapsed_us(stage_start);
2448
2449 let mut component_resolution_packets: Vec<Vec<PreparedResolutionPacket>> =
2451 Vec::with_capacity(num_components as usize);
2452
2453 let stage_start = profile::profile_now(profile_enabled);
2454 for (component_idx, decomp) in decompositions
2455 .iter()
2456 .take(num_components as usize)
2457 .enumerate()
2458 {
2459 let component = u8::try_from(component_idx).map_err(|_| "component index exceeds u8")?;
2460 let mut packets = Vec::with_capacity(num_levels as usize + 1);
2461
2462 let ll_subband = prepare_subband(
2464 &decomp.ll,
2465 decomp.ll_width,
2466 decomp.ll_height,
2467 &step_sizes[0],
2468 bit_depth,
2469 guard_bits,
2470 options.reversible,
2471 block_coding_mode,
2472 cb_width,
2473 cb_height,
2474 SubBandType::LowLow,
2475 accelerator,
2476 )?;
2477 packets.push(PreparedResolutionPacket {
2478 component,
2479 resolution: 0,
2480 precinct: 0,
2481 subbands: vec![ll_subband],
2482 });
2483
2484 for (level_idx, level) in decomp.levels.iter().enumerate() {
2486 let step_base = 1 + level_idx * 3;
2487
2488 let hl_subband = prepare_subband(
2490 &level.hl,
2491 level.high_width,
2492 level.low_height,
2493 &step_sizes[step_base],
2494 bit_depth,
2495 guard_bits,
2496 options.reversible,
2497 block_coding_mode,
2498 cb_width,
2499 cb_height,
2500 SubBandType::HighLow,
2501 accelerator,
2502 )?;
2503
2504 let lh_subband = prepare_subband(
2506 &level.lh,
2507 level.low_width,
2508 level.high_height,
2509 &step_sizes[step_base + 1],
2510 bit_depth,
2511 guard_bits,
2512 options.reversible,
2513 block_coding_mode,
2514 cb_width,
2515 cb_height,
2516 SubBandType::LowHigh,
2517 accelerator,
2518 )?;
2519
2520 let hh_subband = prepare_subband(
2522 &level.hh,
2523 level.high_width,
2524 level.high_height,
2525 &step_sizes[step_base + 2],
2526 bit_depth,
2527 guard_bits,
2528 options.reversible,
2529 block_coding_mode,
2530 cb_width,
2531 cb_height,
2532 SubBandType::HighHigh,
2533 accelerator,
2534 )?;
2535
2536 packets.push(PreparedResolutionPacket {
2537 component,
2538 resolution: u32::try_from(level_idx + 1)
2539 .map_err(|_| "resolution index exceeds u32")?,
2540 precinct: 0,
2541 subbands: vec![hl_subband, lh_subband, hh_subband],
2542 });
2543 }
2544
2545 component_resolution_packets.push(packets);
2546 }
2547 let subband_prepare_us = profile::elapsed_us(stage_start);
2548
2549 let component_resolution_packets = split_component_resolution_packets_by_precinct(
2550 component_resolution_packets,
2551 width,
2552 height,
2553 num_levels,
2554 ¶ms.precinct_exponents,
2555 )?;
2556 let prepared_resolution_packets =
2557 ordered_prepared_resolution_packets(component_resolution_packets, options)?;
2558 let stage_start = profile::profile_now(profile_enabled);
2559 let (resolution_packets, packet_descriptors, allow_packetization_accelerator) =
2560 if options.num_layers > 1 {
2561 let (resolution_packets, packet_descriptors) =
2562 encode_prepared_resolution_packets_layered(
2563 prepared_resolution_packets,
2564 options.num_layers,
2565 options.progression_order,
2566 &options.quality_layer_byte_targets,
2567 accelerator,
2568 )?;
2569 (resolution_packets, packet_descriptors, false)
2570 } else {
2571 let packet_descriptors = packet_descriptors_for_order(
2572 &prepared_resolution_packets,
2573 1,
2574 options.progression_order,
2575 )?;
2576 let resolution_packets =
2577 encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
2578 (resolution_packets, packet_descriptors, true)
2579 };
2580 let block_encode_us = profile::elapsed_us(stage_start);
2581
2582 let stage_start = profile::profile_now(profile_enabled);
2584 let mut resolution_packets = resolution_packets;
2585 let packetization_resolutions = public_packetization_resolutions(&resolution_packets);
2586 let scalar_packet_descriptors = scalar_packet_descriptors(&packet_descriptors);
2587 let packetization_job = J2kPacketizationEncodeJob {
2588 resolution_count: resolution_packets.len() as u32,
2589 num_layers: options.num_layers,
2590 num_components,
2591 code_block_count: count_code_blocks(&resolution_packets)?,
2592 progression_order: public_packetization_progression_order(options.progression_order),
2593 packet_descriptors: &packet_descriptors,
2594 resolutions: &packetization_resolutions,
2595 };
2596 let needs_scalar_packetization =
2597 params.write_plt || params.write_plm || params.write_sop || params.write_eph;
2598 let accelerated_tile_data = if allow_packetization_accelerator && !needs_scalar_packetization {
2599 accelerator.encode_packetization(packetization_job)?
2600 } else {
2601 None
2602 };
2603 let packetized_tile = if let Some(data) = accelerated_tile_data {
2604 packet_encode::PacketizedTileData {
2605 data,
2606 packet_lengths: Vec::new(),
2607 }
2608 } else {
2609 packet_encode::form_tile_bitstream_with_descriptors_lengths_and_markers(
2610 &mut resolution_packets,
2611 &scalar_packet_descriptors,
2612 packet_encode::PacketMarkerOptions {
2613 write_sop: params.write_sop,
2614 write_eph: params.write_eph,
2615 },
2616 )?
2617 };
2618 let packetize_us = profile::elapsed_us(stage_start);
2619
2620 let stage_start = profile::profile_now(profile_enabled);
2622 let codestream = codestream_write::write_codestream_with_packet_lengths(
2623 ¶ms,
2624 &packetized_tile.data,
2625 &quant_params,
2626 &packetized_tile.packet_lengths,
2627 );
2628 let codestream_us = profile::elapsed_us(stage_start);
2629
2630 if profile_enabled {
2631 profile::emit_profile_row(
2632 "encode",
2633 "cpu",
2634 &[
2635 ("deinterleave_us", deinterleave_us),
2636 ("mct_us", mct_us),
2637 ("dwt_us", dwt_us),
2638 ("subband_prepare_us", subband_prepare_us),
2639 ("block_encode_us", block_encode_us),
2640 ("packetize_us", packetize_us),
2641 ("codestream_us", codestream_us),
2642 ("total_us", profile::elapsed_us(total_start)),
2643 ],
2644 );
2645 }
2646
2647 Ok(codestream)
2648}
2649
2650fn encode_multitile_impl(
2651 pixels: &[u8],
2652 width: u32,
2653 height: u32,
2654 num_components: u8,
2655 bit_depth: u8,
2656 signed: bool,
2657 options: &EncodeOptions,
2658 block_coding_mode: BlockCodingMode,
2659 accelerator: &mut impl J2kEncodeStageAccelerator,
2660 tile_width: u32,
2661 tile_height: u32,
2662) -> Result<Vec<u8>, &'static str> {
2663 let num_x_tiles = width.div_ceil(tile_width);
2664 let num_y_tiles = height.div_ceil(tile_height);
2665 let num_tiles = num_x_tiles
2666 .checked_mul(num_y_tiles)
2667 .ok_or("tile count overflow")?;
2668 if num_tiles > u32::from(u16::MAX) + 1 {
2669 return Err("multi-tile encode supports at most 65536 tiles");
2670 }
2671
2672 let min_tile_width = if width.is_multiple_of(tile_width) {
2673 tile_width
2674 } else {
2675 width % tile_width
2676 };
2677 let min_tile_height = if height.is_multiple_of(tile_height) {
2678 tile_height
2679 } else {
2680 height % tile_height
2681 };
2682 let num_levels = options
2683 .num_decomposition_levels
2684 .min(max_decomposition_levels(min_tile_width, min_tile_height));
2685 let use_mct = options.use_mct && num_components >= 3;
2686 let guard_bits = if options.reversible {
2687 if use_mct {
2688 options.guard_bits.max(2)
2689 } else {
2690 options.guard_bits
2691 }
2692 } else {
2693 options.guard_bits.max(2)
2694 };
2695 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
2696 bit_depth,
2697 num_levels,
2698 options.reversible,
2699 guard_bits,
2700 options.irreversible_quantization_scale,
2701 options.irreversible_quantization_subband_scales,
2702 );
2703 let quant_params: Vec<(u16, u16)> = step_sizes
2704 .iter()
2705 .map(|s| (s.exponent, s.mantissa))
2706 .collect();
2707
2708 let mut child_options = options.clone();
2709 child_options.num_decomposition_levels = num_levels;
2710 child_options.tile_size = None;
2711 child_options.write_tlm = false;
2712 child_options.write_plt = options.write_plt || options.write_plm;
2713 child_options.write_plm = false;
2714
2715 struct EncodedTilePart {
2716 data: Vec<u8>,
2717 packet_lengths: Vec<u32>,
2718 }
2719
2720 let mut tile_bodies = Vec::with_capacity(num_tiles as usize);
2721 for tile_y in 0..num_y_tiles {
2722 for tile_x in 0..num_x_tiles {
2723 let x0 = tile_x * tile_width;
2724 let y0 = tile_y * tile_height;
2725 let actual_width = (width - x0).min(tile_width);
2726 let actual_height = (height - y0).min(tile_height);
2727 let tile_pixels = extract_interleaved_tile(
2728 pixels,
2729 width,
2730 x0,
2731 y0,
2732 actual_width,
2733 actual_height,
2734 num_components,
2735 bit_depth,
2736 )?;
2737 let tile_codestream = encode_impl(
2738 &tile_pixels,
2739 actual_width,
2740 actual_height,
2741 num_components,
2742 bit_depth,
2743 signed,
2744 &child_options,
2745 block_coding_mode,
2746 accelerator,
2747 )?;
2748 let packet_lengths = if options.write_plt || options.write_plm {
2749 extract_single_tile_plt_packet_lengths(&tile_codestream)?
2750 } else {
2751 Vec::new()
2752 };
2753 tile_bodies.push(EncodedTilePart {
2754 data: extract_single_tile_body(&tile_codestream)?.to_vec(),
2755 packet_lengths,
2756 });
2757 }
2758 }
2759
2760 let component_sampling = component_sampling_for_options(options, num_components)?;
2761 let precinct_exponents = precinct_exponents_for_options(options, num_levels)?;
2762 let params = EncodeParams {
2763 width,
2764 height,
2765 tile_width,
2766 tile_height,
2767 num_components,
2768 bit_depth,
2769 signed,
2770 num_decomposition_levels: num_levels,
2771 reversible: options.reversible,
2772 code_block_width_exp: options.code_block_width_exp,
2773 code_block_height_exp: options.code_block_height_exp,
2774 num_layers: options.num_layers,
2775 use_mct,
2776 guard_bits,
2777 block_coding_mode,
2778 progression_order: options.progression_order,
2779 write_tlm: options.write_tlm,
2780 write_plt: options.write_plt,
2781 write_plm: options.write_plm,
2782 write_sop: options.write_sop,
2783 write_eph: options.write_eph,
2784 terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
2785 && options.num_layers > 1,
2786 component_sampling,
2787 precinct_exponents,
2788 };
2789 let tile_parts = tile_bodies
2790 .iter()
2791 .enumerate()
2792 .map(|(tile_index, tile)| {
2793 Ok(codestream_write::TilePartData {
2794 tile_index: u16::try_from(tile_index).map_err(|_| "tile index exceeds u16")?,
2795 data: &tile.data,
2796 packet_lengths: &tile.packet_lengths,
2797 })
2798 })
2799 .collect::<Result<Vec<_>, &'static str>>()?;
2800
2801 Ok(codestream_write::write_codestream_tiles(
2802 ¶ms,
2803 &tile_parts,
2804 &quant_params,
2805 ))
2806}
2807
2808fn extract_interleaved_tile(
2809 pixels: &[u8],
2810 image_width: u32,
2811 x0: u32,
2812 y0: u32,
2813 tile_width: u32,
2814 tile_height: u32,
2815 num_components: u8,
2816 bit_depth: u8,
2817) -> Result<Vec<u8>, &'static str> {
2818 let bytes_per_sample = if bit_depth <= 8 { 1usize } else { 2usize };
2819 let bytes_per_pixel = usize::from(num_components)
2820 .checked_mul(bytes_per_sample)
2821 .ok_or("pixel stride overflow")?;
2822 let row_bytes = usize::try_from(tile_width)
2823 .map_err(|_| "tile width exceeds usize")?
2824 .checked_mul(bytes_per_pixel)
2825 .ok_or("tile row byte count overflow")?;
2826 let out_len = row_bytes
2827 .checked_mul(usize::try_from(tile_height).map_err(|_| "tile height exceeds usize")?)
2828 .ok_or("tile byte count overflow")?;
2829 let mut tile = Vec::with_capacity(out_len);
2830 let image_row_bytes = usize::try_from(image_width)
2831 .map_err(|_| "image width exceeds usize")?
2832 .checked_mul(bytes_per_pixel)
2833 .ok_or("image row byte count overflow")?;
2834 let x_byte_offset = usize::try_from(x0)
2835 .map_err(|_| "tile x offset exceeds usize")?
2836 .checked_mul(bytes_per_pixel)
2837 .ok_or("tile x byte offset overflow")?;
2838
2839 for y in y0..y0 + tile_height {
2840 let row_start = usize::try_from(y)
2841 .map_err(|_| "tile y offset exceeds usize")?
2842 .checked_mul(image_row_bytes)
2843 .and_then(|offset| offset.checked_add(x_byte_offset))
2844 .ok_or("tile row offset overflow")?;
2845 let row_end = row_start
2846 .checked_add(row_bytes)
2847 .ok_or("tile row range overflow")?;
2848 tile.extend_from_slice(
2849 pixels
2850 .get(row_start..row_end)
2851 .ok_or("tile row range outside source pixels")?,
2852 );
2853 }
2854
2855 Ok(tile)
2856}
2857
2858fn extract_single_tile_body(codestream: &[u8]) -> Result<&[u8], &'static str> {
2859 let sod = codestream
2860 .windows(2)
2861 .position(|marker| marker == [0xFF, super::codestream::markers::SOD])
2862 .ok_or("encoded tile codestream missing SOD")?;
2863 let eoc = codestream
2864 .windows(2)
2865 .rposition(|marker| marker == [0xFF, super::codestream::markers::EOC])
2866 .ok_or("encoded tile codestream missing EOC")?;
2867 if eoc < sod + 2 {
2868 return Err("encoded tile codestream marker order invalid");
2869 }
2870 Ok(&codestream[sod + 2..eoc])
2871}
2872
2873fn extract_single_tile_plt_packet_lengths(codestream: &[u8]) -> Result<Vec<u32>, &'static str> {
2874 let sod = codestream
2875 .windows(2)
2876 .position(|marker| marker == [0xFF, super::codestream::markers::SOD])
2877 .ok_or("encoded tile codestream missing SOD")?;
2878 let mut packet_lengths = Vec::new();
2879 let mut offset = 0usize;
2880
2881 while offset + 4 <= sod {
2882 if codestream[offset] == 0xFF && codestream[offset + 1] == super::codestream::markers::PLT {
2883 let marker_len =
2884 u16::from_be_bytes([codestream[offset + 2], codestream[offset + 3]]) as usize;
2885 if marker_len < 3 {
2886 return Err("encoded tile codestream has invalid PLT length");
2887 }
2888 let marker_end = offset
2889 .checked_add(2)
2890 .and_then(|value| value.checked_add(marker_len))
2891 .ok_or("encoded tile codestream PLT length overflow")?;
2892 if marker_end > sod {
2893 return Err("encoded tile codestream PLT extends past SOD");
2894 }
2895 let length_bytes = codestream
2896 .get(offset + 5..marker_end)
2897 .ok_or("encoded tile codestream PLT payload out of range")?;
2898 packet_lengths.extend(
2899 super::codestream::decode_packet_lengths(length_bytes)
2900 .ok_or("encoded tile codestream has invalid PLT packet lengths")?,
2901 );
2902 offset = marker_end;
2903 } else {
2904 offset += 1;
2905 }
2906 }
2907
2908 if packet_lengths.is_empty() {
2909 return Err("encoded tile codestream missing PLT packet lengths");
2910 }
2911
2912 Ok(packet_lengths)
2913}
2914
2915fn try_encode_forward_rct(
2916 components: &mut [Vec<f32>],
2917 accelerator: &mut impl J2kEncodeStageAccelerator,
2918) -> Result<bool, &'static str> {
2919 debug_assert!(components.len() >= 3);
2920 let (plane0, rest) = components.split_at_mut(1);
2921 let (plane1, plane2) = rest.split_at_mut(1);
2922 accelerator.encode_forward_rct(J2kForwardRctJob {
2923 plane0: &mut plane0[0],
2924 plane1: &mut plane1[0],
2925 plane2: &mut plane2[0],
2926 })
2927}
2928
2929fn try_encode_forward_ict(
2930 components: &mut [Vec<f32>],
2931 accelerator: &mut impl J2kEncodeStageAccelerator,
2932) -> Result<bool, &'static str> {
2933 debug_assert!(components.len() >= 3);
2934 let (plane0, rest) = components.split_at_mut(1);
2935 let (plane1, plane2) = rest.split_at_mut(1);
2936 accelerator.encode_forward_ict(J2kForwardIctJob {
2937 plane0: &mut plane0[0],
2938 plane1: &mut plane1[0],
2939 plane2: &mut plane2[0],
2940 })
2941}
2942
2943fn encode_forward_dwt(
2944 component: &[f32],
2945 width: u32,
2946 height: u32,
2947 num_levels: u8,
2948 reversible: bool,
2949 accelerator: &mut impl J2kEncodeStageAccelerator,
2950) -> Result<DwtDecomposition, &'static str> {
2951 if reversible {
2952 if let Some(output) = accelerator.encode_forward_dwt53(J2kForwardDwt53Job {
2953 samples: component,
2954 width,
2955 height,
2956 num_levels,
2957 })? {
2958 return convert_forward_dwt53_output(output);
2959 }
2960 } else if let Some(output) = accelerator.encode_forward_dwt97(J2kForwardDwt97Job {
2961 samples: component,
2962 width,
2963 height,
2964 num_levels,
2965 })? {
2966 return convert_forward_dwt97_output(output);
2967 }
2968
2969 Ok(fdwt::forward_dwt(
2970 component, width, height, num_levels, reversible,
2971 ))
2972}
2973
2974fn convert_forward_dwt53_output(
2975 output: J2kForwardDwt53Output,
2976) -> Result<DwtDecomposition, &'static str> {
2977 validate_band_len(output.ll.len(), output.ll_width, output.ll_height)?;
2978 let mut levels = Vec::with_capacity(output.levels.len());
2979 for level in output.levels {
2980 validate_dwt53_level(&level)?;
2981 levels.push(fdwt::DwtLevel {
2982 hl: level.hl,
2983 lh: level.lh,
2984 hh: level.hh,
2985 low_width: level.low_width,
2986 low_height: level.low_height,
2987 high_width: level.high_width,
2988 high_height: level.high_height,
2989 });
2990 }
2991 Ok(DwtDecomposition {
2992 ll: output.ll,
2993 ll_width: output.ll_width,
2994 ll_height: output.ll_height,
2995 levels,
2996 })
2997}
2998
2999fn convert_forward_dwt97_output(
3000 output: J2kForwardDwt97Output,
3001) -> Result<DwtDecomposition, &'static str> {
3002 validate_band_len(output.ll.len(), output.ll_width, output.ll_height)?;
3003 let mut levels = Vec::with_capacity(output.levels.len());
3004 for level in output.levels {
3005 validate_dwt97_level(&level)?;
3006 levels.push(fdwt::DwtLevel {
3007 hl: level.hl,
3008 lh: level.lh,
3009 hh: level.hh,
3010 low_width: level.low_width,
3011 low_height: level.low_height,
3012 high_width: level.high_width,
3013 high_height: level.high_height,
3014 });
3015 }
3016 Ok(DwtDecomposition {
3017 ll: output.ll,
3018 ll_width: output.ll_width,
3019 ll_height: output.ll_height,
3020 levels,
3021 })
3022}
3023
3024fn validate_dwt53_level(level: &J2kForwardDwt53Level) -> Result<(), &'static str> {
3025 validate_band_len(level.hl.len(), level.high_width, level.low_height)?;
3026 validate_band_len(level.lh.len(), level.low_width, level.high_height)?;
3027 validate_band_len(level.hh.len(), level.high_width, level.high_height)?;
3028 Ok(())
3029}
3030
3031fn validate_dwt97_level(level: &J2kForwardDwt97Level) -> Result<(), &'static str> {
3032 validate_band_len(level.hl.len(), level.high_width, level.low_height)?;
3033 validate_band_len(level.lh.len(), level.low_width, level.high_height)?;
3034 validate_band_len(level.hh.len(), level.high_width, level.high_height)?;
3035 Ok(())
3036}
3037
3038fn validate_band_len(actual: usize, width: u32, height: u32) -> Result<(), &'static str> {
3039 let expected = (width as usize)
3040 .checked_mul(height as usize)
3041 .ok_or("accelerated DWT output dimensions overflow")?;
3042 if actual != expected {
3043 return Err("accelerated DWT output length mismatch");
3044 }
3045 Ok(())
3046}
3047
3048fn validate_deinterleaved_components(
3049 components: Vec<Vec<f32>>,
3050 num_components: u8,
3051 num_pixels: usize,
3052) -> Result<Vec<Vec<f32>>, &'static str> {
3053 if components.len() != usize::from(num_components) {
3054 return Err("accelerated deinterleave component count mismatch");
3055 }
3056 if components
3057 .iter()
3058 .any(|component| component.len() != num_pixels)
3059 {
3060 return Err("accelerated deinterleave component length mismatch");
3061 }
3062 Ok(components)
3063}
3064
3065fn component_sampling_for_options(
3066 options: &EncodeOptions,
3067 num_components: u8,
3068) -> Result<Vec<(u8, u8)>, &'static str> {
3069 match &options.component_sampling {
3070 Some(component_sampling) => {
3071 if component_sampling.len() != usize::from(num_components) {
3072 return Err("component sampling count does not match component count");
3073 }
3074 if component_sampling
3075 .iter()
3076 .any(|&(x_rsiz, y_rsiz)| x_rsiz == 0 || y_rsiz == 0)
3077 {
3078 return Err("component sampling factors must be non-zero");
3079 }
3080 Ok(component_sampling.clone())
3081 }
3082 None => Ok(vec![(1, 1); usize::from(num_components)]),
3083 }
3084}
3085
3086fn precinct_exponents_for_options(
3087 options: &EncodeOptions,
3088 num_decomposition_levels: u8,
3089) -> Result<Vec<(u8, u8)>, &'static str> {
3090 if options.precinct_exponents.is_empty() {
3091 return Ok(Vec::new());
3092 }
3093
3094 let expected = usize::from(num_decomposition_levels) + 1;
3095 if options.precinct_exponents.len() != expected {
3096 return Err("precinct exponent count must match resolution level count");
3097 }
3098 if options
3099 .precinct_exponents
3100 .iter()
3101 .any(|&(ppx, ppy)| ppx > 15 || ppy > 15)
3102 {
3103 return Err("precinct exponents must fit in COD marker nybbles");
3104 }
3105 let code_block_width_exp = options.code_block_width_exp + 2;
3106 let code_block_height_exp = options.code_block_height_exp + 2;
3107 for (resolution, &(ppx, ppy)) in options.precinct_exponents.iter().enumerate() {
3108 let min_ppx = if resolution == 0 {
3109 code_block_width_exp
3110 } else {
3111 code_block_width_exp + 1
3112 };
3113 let min_ppy = if resolution == 0 {
3114 code_block_height_exp
3115 } else {
3116 code_block_height_exp + 1
3117 };
3118 if ppx < min_ppx || ppy < min_ppy {
3119 return Err("precinct exponents must not reduce encoder code-block dimensions");
3120 }
3121 }
3122 Ok(options.precinct_exponents.clone())
3123}
3124
3125fn count_code_blocks(resolution_packets: &[ResolutionPacket]) -> Result<u32, &'static str> {
3126 let count = resolution_packets
3127 .iter()
3128 .flat_map(|resolution| resolution.subbands.iter())
3129 .try_fold(0usize, |acc, subband| {
3130 acc.checked_add(subband.code_blocks.len())
3131 .ok_or("packetization code-block count overflow")
3132 })?;
3133 u32::try_from(count).map_err(|_| "packetization code-block count exceeds u32")
3134}
3135
3136fn count_compact_code_blocks(
3137 resolution_packets: &[PreparedCompactResolutionPacket<'_>],
3138) -> Result<u32, &'static str> {
3139 let count = resolution_packets
3140 .iter()
3141 .flat_map(|resolution| resolution.subbands.iter())
3142 .try_fold(0usize, |acc, subband| {
3143 acc.checked_add(subband.code_blocks.len())
3144 .ok_or("packetization code-block count overflow")
3145 })?;
3146 u32::try_from(count).map_err(|_| "packetization code-block count exceeds u32")
3147}
3148
3149fn split_component_resolution_packets_by_precinct(
3150 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
3151 width: u32,
3152 height: u32,
3153 num_decomposition_levels: u8,
3154 precinct_exponents: &[(u8, u8)],
3155) -> Result<Vec<Vec<PreparedResolutionPacket>>, &'static str> {
3156 if precinct_exponents.is_empty() {
3157 return Ok(component_resolution_packets);
3158 }
3159
3160 component_resolution_packets
3161 .into_iter()
3162 .map(|component_packets| {
3163 let mut split_packets = Vec::new();
3164 for packet in component_packets {
3165 split_packets.extend(split_prepared_resolution_packet_by_precinct(
3166 packet,
3167 width,
3168 height,
3169 num_decomposition_levels,
3170 precinct_exponents,
3171 )?);
3172 }
3173 Ok(split_packets)
3174 })
3175 .collect()
3176}
3177
3178fn split_prepared_resolution_packet_by_precinct(
3179 packet: PreparedResolutionPacket,
3180 width: u32,
3181 height: u32,
3182 num_decomposition_levels: u8,
3183 precinct_exponents: &[(u8, u8)],
3184) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
3185 let resolution =
3186 usize::try_from(packet.resolution).map_err(|_| "resolution index exceeds usize")?;
3187 let &(ppx, ppy) = precinct_exponents
3188 .get(resolution)
3189 .ok_or("missing precinct exponents for resolution")?;
3190 let (precincts_x, precincts_y) = resolution_precinct_grid(
3191 width,
3192 height,
3193 num_decomposition_levels,
3194 packet.resolution,
3195 ppx,
3196 ppy,
3197 )?;
3198 let packet_count = (precincts_x as usize)
3199 .checked_mul(precincts_y as usize)
3200 .ok_or("precinct packet count overflow")?;
3201 let component = packet.component;
3202 let resolution = packet.resolution;
3203 let subbands = packet.subbands;
3204 let mut packets = Vec::with_capacity(packet_count);
3205
3206 for precinct_y in 0..precincts_y {
3207 for precinct_x in 0..precincts_x {
3208 let precinct = u64::from(precinct_y)
3209 .checked_mul(u64::from(precincts_x))
3210 .and_then(|value| value.checked_add(u64::from(precinct_x)))
3211 .ok_or("precinct index overflow")?;
3212 let split_subbands = subbands
3213 .iter()
3214 .map(|subband| {
3215 split_prepared_subband_by_precinct(
3216 subband, resolution, ppx, ppy, precinct_x, precinct_y,
3217 )
3218 })
3219 .collect::<Result<Vec<_>, &'static str>>()?;
3220 packets.push(PreparedResolutionPacket {
3221 component,
3222 resolution,
3223 precinct,
3224 subbands: split_subbands,
3225 });
3226 }
3227 }
3228
3229 Ok(packets)
3230}
3231
3232fn resolution_precinct_grid(
3233 width: u32,
3234 height: u32,
3235 num_decomposition_levels: u8,
3236 resolution: u32,
3237 ppx: u8,
3238 ppy: u8,
3239) -> Result<(u32, u32), &'static str> {
3240 let resolution_shift = u32::from(num_decomposition_levels)
3241 .checked_sub(resolution)
3242 .ok_or("resolution exceeds decomposition level count")?;
3243 let resolution_scale = pow2_u32(resolution_shift)?;
3244 let resolution_width = width.div_ceil(resolution_scale);
3245 let resolution_height = height.div_ceil(resolution_scale);
3246 let precinct_width = pow2_u32(u32::from(ppx))?;
3247 let precinct_height = pow2_u32(u32::from(ppy))?;
3248
3249 Ok((
3250 if resolution_width == 0 {
3251 0
3252 } else {
3253 resolution_width.div_ceil(precinct_width)
3254 },
3255 if resolution_height == 0 {
3256 0
3257 } else {
3258 resolution_height.div_ceil(precinct_height)
3259 },
3260 ))
3261}
3262
3263fn split_prepared_subband_by_precinct(
3264 subband: &PreparedEncodeSubband,
3265 resolution: u32,
3266 ppx: u8,
3267 ppy: u8,
3268 precinct_x: u32,
3269 precinct_y: u32,
3270) -> Result<PreparedEncodeSubband, &'static str> {
3271 if subband.code_blocks.is_empty() || subband.width == 0 || subband.height == 0 {
3272 return Ok(empty_prepared_subband_precinct(subband));
3273 }
3274
3275 let subband_ppx = if resolution > 0 {
3276 ppx.checked_sub(1)
3277 .ok_or("nonzero resolution precinct exponent underflow")?
3278 } else {
3279 ppx
3280 };
3281 let subband_ppy = if resolution > 0 {
3282 ppy.checked_sub(1)
3283 .ok_or("nonzero resolution precinct exponent underflow")?
3284 } else {
3285 ppy
3286 };
3287 let precinct_width = pow2_u32(u32::from(subband_ppx))?;
3288 let precinct_height = pow2_u32(u32::from(subband_ppy))?;
3289 let precinct_x0 = precinct_x
3290 .checked_mul(precinct_width)
3291 .ok_or("precinct x coordinate overflow")?;
3292 let precinct_y0 = precinct_y
3293 .checked_mul(precinct_height)
3294 .ok_or("precinct y coordinate overflow")?;
3295 let x0 = precinct_x0.min(subband.width);
3296 let y0 = precinct_y0.min(subband.height);
3297 let x1 = precinct_x0
3298 .checked_add(precinct_width)
3299 .ok_or("precinct x extent overflow")?
3300 .min(subband.width);
3301 let y1 = precinct_y0
3302 .checked_add(precinct_height)
3303 .ok_or("precinct y extent overflow")?
3304 .min(subband.height);
3305
3306 if x0 >= x1 || y0 >= y1 {
3307 return Ok(empty_prepared_subband_precinct(subband));
3308 }
3309
3310 let cb_width = subband.code_block_width;
3311 let cb_height = subband.code_block_height;
3312 if cb_width == 0 || cb_height == 0 {
3313 return Ok(empty_prepared_subband_precinct(subband));
3314 }
3315
3316 let cb_x0 = (x0 / cb_width) * cb_width;
3317 let cb_y0 = (y0 / cb_height) * cb_height;
3318 let cb_x1 = x1.div_ceil(cb_width) * cb_width;
3319 let cb_y1 = y1.div_ceil(cb_height) * cb_height;
3320 let cbx_start = cb_x0 / cb_width;
3321 let cby_start = cb_y0 / cb_height;
3322 let cbx_end = cb_x1 / cb_width;
3323 let cby_end = cb_y1 / cb_height;
3324 let num_cbs_x = cbx_end.saturating_sub(cbx_start);
3325 let num_cbs_y = cby_end.saturating_sub(cby_start);
3326 let mut indices = Vec::with_capacity((num_cbs_x as usize).saturating_mul(num_cbs_y as usize));
3327
3328 for cby in cby_start..cby_end {
3329 for cbx in cbx_start..cbx_end {
3330 let index = cby
3331 .checked_mul(subband.num_cbs_x)
3332 .and_then(|value| value.checked_add(cbx))
3333 .ok_or("precinct code-block index overflow")?;
3334 indices.push(usize::try_from(index).map_err(|_| "code-block index exceeds usize")?);
3335 }
3336 }
3337
3338 let code_blocks = indices
3339 .iter()
3340 .map(|&idx| {
3341 subband
3342 .code_blocks
3343 .get(idx)
3344 .cloned()
3345 .ok_or("precinct code-block index out of range")
3346 })
3347 .collect::<Result<Vec<_>, &'static str>>()?;
3348 let preencoded_ht_code_blocks = subband
3349 .preencoded_ht_code_blocks
3350 .as_ref()
3351 .map(|blocks| {
3352 indices
3353 .iter()
3354 .map(|&idx| {
3355 blocks
3356 .get(idx)
3357 .cloned()
3358 .ok_or("precinct preencoded code-block index out of range")
3359 })
3360 .collect::<Result<Vec<_>, &'static str>>()
3361 })
3362 .transpose()?;
3363
3364 Ok(PreparedEncodeSubband {
3365 code_blocks,
3366 preencoded_ht_code_blocks,
3367 num_cbs_x,
3368 num_cbs_y,
3369 code_block_width: cb_width,
3370 code_block_height: cb_height,
3371 width: x1 - x0,
3372 height: y1 - y0,
3373 sub_band_type: subband.sub_band_type,
3374 total_bitplanes: subband.total_bitplanes,
3375 block_coding_mode: subband.block_coding_mode,
3376 })
3377}
3378
3379fn empty_prepared_subband_precinct(subband: &PreparedEncodeSubband) -> PreparedEncodeSubband {
3380 PreparedEncodeSubband {
3381 code_blocks: Vec::new(),
3382 preencoded_ht_code_blocks: subband
3383 .preencoded_ht_code_blocks
3384 .as_ref()
3385 .map(|_| Vec::new()),
3386 num_cbs_x: 0,
3387 num_cbs_y: 0,
3388 code_block_width: subband.code_block_width,
3389 code_block_height: subband.code_block_height,
3390 width: 0,
3391 height: 0,
3392 sub_band_type: subband.sub_band_type,
3393 total_bitplanes: subband.total_bitplanes,
3394 block_coding_mode: subband.block_coding_mode,
3395 }
3396}
3397
3398fn pow2_u32(exponent: u32) -> Result<u32, &'static str> {
3399 1_u32
3400 .checked_shl(exponent)
3401 .ok_or("precinct exponent exceeds u32 shift width")
3402}
3403
3404fn packet_descriptors_for_order(
3405 packets: &[PreparedResolutionPacket],
3406 num_layers: u8,
3407 progression_order: EncodeProgressionOrder,
3408) -> Result<Vec<J2kPacketizationPacketDescriptor>, &'static str> {
3409 if num_layers != 1 {
3410 return Err("encode currently prepares one packet contribution layer");
3411 }
3412 let mut descriptors = packets
3413 .iter()
3414 .enumerate()
3415 .map(|(packet_index, packet)| {
3416 Ok(J2kPacketizationPacketDescriptor {
3417 packet_index: u32::try_from(packet_index)
3418 .map_err(|_| "packet descriptor index exceeds u32")?,
3419 state_index: u32::try_from(packet_index)
3420 .map_err(|_| "packet descriptor state index exceeds u32")?,
3421 layer: 0,
3422 resolution: packet.resolution,
3423 component: packet.component,
3424 precinct: packet.precinct,
3425 })
3426 })
3427 .collect::<Result<Vec<_>, &'static str>>()?;
3428 sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
3429 Ok(descriptors)
3430}
3431
3432fn packet_descriptors_for_compact_order(
3433 packets: &[PreparedCompactResolutionPacket<'_>],
3434 num_layers: u8,
3435 progression_order: EncodeProgressionOrder,
3436) -> Result<Vec<J2kPacketizationPacketDescriptor>, &'static str> {
3437 if num_layers != 1 {
3438 return Err("encode currently prepares one packet contribution layer");
3439 }
3440 let mut descriptors = packets
3441 .iter()
3442 .enumerate()
3443 .map(|(packet_index, packet)| {
3444 Ok(J2kPacketizationPacketDescriptor {
3445 packet_index: u32::try_from(packet_index)
3446 .map_err(|_| "packet descriptor index exceeds u32")?,
3447 state_index: u32::try_from(packet_index)
3448 .map_err(|_| "packet descriptor state index exceeds u32")?,
3449 layer: 0,
3450 resolution: packet.resolution,
3451 component: packet.component,
3452 precinct: packet.precinct,
3453 })
3454 })
3455 .collect::<Result<Vec<_>, &'static str>>()?;
3456 sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
3457 Ok(descriptors)
3458}
3459
3460fn ordered_prepared_resolution_packets(
3461 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
3462 options: &EncodeOptions,
3463) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
3464 match options.progression_order {
3465 EncodeProgressionOrder::Lrcp
3466 | EncodeProgressionOrder::Rlcp
3467 | EncodeProgressionOrder::Rpcl => {
3468 lrcp_ordered_prepared_resolution_packets(component_resolution_packets)
3469 }
3470 EncodeProgressionOrder::Pcrl | EncodeProgressionOrder::Cprl => {
3471 component_ordered_prepared_resolution_packets(component_resolution_packets)
3472 }
3473 }
3474}
3475
3476fn ordered_prepared_compact_resolution_packets<'a>(
3477 component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
3478 options: &EncodeOptions,
3479) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
3480 match options.progression_order {
3481 EncodeProgressionOrder::Lrcp
3482 | EncodeProgressionOrder::Rlcp
3483 | EncodeProgressionOrder::Rpcl => {
3484 lrcp_ordered_prepared_compact_resolution_packets(component_resolution_packets)
3485 }
3486 EncodeProgressionOrder::Pcrl | EncodeProgressionOrder::Cprl => {
3487 component_ordered_prepared_compact_resolution_packets(component_resolution_packets)
3488 }
3489 }
3490}
3491
3492fn lrcp_ordered_prepared_resolution_packets(
3493 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
3494) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
3495 let resolution_count = component_resolution_packets
3496 .first()
3497 .map_or(0usize, alloc::vec::Vec::len);
3498 let mut component_iters: Vec<_> = component_resolution_packets
3499 .into_iter()
3500 .map(alloc::vec::Vec::into_iter)
3501 .collect();
3502 let mut resolution_packets =
3503 Vec::with_capacity(resolution_count.saturating_mul(component_iters.len()));
3504
3505 for _resolution in 0..resolution_count {
3506 for component in &mut component_iters {
3507 resolution_packets.push(
3508 component
3509 .next()
3510 .ok_or("component packet resolution count mismatch")?,
3511 );
3512 }
3513 }
3514
3515 if component_iters
3516 .iter_mut()
3517 .any(|component| component.next().is_some())
3518 {
3519 return Err("component packet resolution count mismatch");
3520 }
3521
3522 Ok(resolution_packets)
3523}
3524
3525fn lrcp_ordered_prepared_compact_resolution_packets<'a>(
3526 component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
3527) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
3528 let resolution_count = component_resolution_packets
3529 .first()
3530 .map_or(0usize, alloc::vec::Vec::len);
3531 let mut component_iters: Vec<_> = component_resolution_packets
3532 .into_iter()
3533 .map(alloc::vec::Vec::into_iter)
3534 .collect();
3535 let mut resolution_packets =
3536 Vec::with_capacity(resolution_count.saturating_mul(component_iters.len()));
3537
3538 for _resolution in 0..resolution_count {
3539 for component in &mut component_iters {
3540 resolution_packets.push(
3541 component
3542 .next()
3543 .ok_or("component packet resolution count mismatch")?,
3544 );
3545 }
3546 }
3547
3548 if component_iters
3549 .iter_mut()
3550 .any(|component| component.next().is_some())
3551 {
3552 return Err("component packet resolution count mismatch");
3553 }
3554
3555 Ok(resolution_packets)
3556}
3557
3558fn component_ordered_prepared_resolution_packets(
3559 component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
3560) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
3561 let resolution_count = component_resolution_packets
3562 .first()
3563 .map_or(0usize, alloc::vec::Vec::len);
3564 let mut resolution_packets =
3565 Vec::with_capacity(resolution_count.saturating_mul(component_resolution_packets.len()));
3566
3567 for component in component_resolution_packets {
3568 if component.len() != resolution_count {
3569 return Err("component packet resolution count mismatch");
3570 }
3571 resolution_packets.extend(component);
3572 }
3573
3574 Ok(resolution_packets)
3575}
3576
3577fn component_ordered_prepared_compact_resolution_packets<'a>(
3578 component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
3579) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
3580 let resolution_count = component_resolution_packets
3581 .first()
3582 .map_or(0usize, alloc::vec::Vec::len);
3583 let mut resolution_packets =
3584 Vec::with_capacity(resolution_count.saturating_mul(component_resolution_packets.len()));
3585
3586 for component in component_resolution_packets {
3587 if component.len() != resolution_count {
3588 return Err("component packet resolution count mismatch");
3589 }
3590 resolution_packets.extend(component);
3591 }
3592
3593 Ok(resolution_packets)
3594}
3595
3596fn public_packetization_progression_order(
3597 progression_order: EncodeProgressionOrder,
3598) -> crate::J2kPacketizationProgressionOrder {
3599 match progression_order {
3600 EncodeProgressionOrder::Lrcp => crate::J2kPacketizationProgressionOrder::Lrcp,
3601 EncodeProgressionOrder::Rlcp => crate::J2kPacketizationProgressionOrder::Rlcp,
3602 EncodeProgressionOrder::Rpcl => crate::J2kPacketizationProgressionOrder::Rpcl,
3603 EncodeProgressionOrder::Pcrl => crate::J2kPacketizationProgressionOrder::Pcrl,
3604 EncodeProgressionOrder::Cprl => crate::J2kPacketizationProgressionOrder::Cprl,
3605 }
3606}
3607
3608fn scalar_packet_descriptors(
3609 descriptors: &[J2kPacketizationPacketDescriptor],
3610) -> Vec<packet_encode::PacketDescriptor> {
3611 descriptors
3612 .iter()
3613 .map(|descriptor| packet_encode::PacketDescriptor {
3614 packet_index: descriptor.packet_index,
3615 state_index: descriptor.state_index,
3616 layer: descriptor.layer,
3617 resolution: descriptor.resolution,
3618 component: descriptor.component,
3619 precinct: descriptor.precinct,
3620 })
3621 .collect()
3622}
3623
3624fn public_packetization_resolutions(
3625 resolution_packets: &[ResolutionPacket],
3626) -> Vec<J2kPacketizationResolution<'_>> {
3627 resolution_packets
3628 .iter()
3629 .map(|resolution| J2kPacketizationResolution {
3630 subbands: resolution
3631 .subbands
3632 .iter()
3633 .map(|subband| J2kPacketizationSubband {
3634 code_blocks: subband
3635 .code_blocks
3636 .iter()
3637 .map(|code_block| J2kPacketizationCodeBlock {
3638 data: &code_block.data,
3639 ht_cleanup_length: code_block.ht_cleanup_length,
3640 ht_refinement_length: code_block.ht_refinement_length,
3641 num_coding_passes: code_block.num_coding_passes,
3642 num_zero_bitplanes: code_block.num_zero_bitplanes,
3643 previously_included: code_block.previously_included,
3644 l_block: code_block.l_block,
3645 block_coding_mode: public_packetization_block_coding_mode(
3646 code_block.block_coding_mode,
3647 ),
3648 })
3649 .collect(),
3650 num_cbs_x: subband.num_cbs_x,
3651 num_cbs_y: subband.num_cbs_y,
3652 })
3653 .collect(),
3654 })
3655 .collect()
3656}
3657
3658fn public_packetization_resolutions_from_compact<'a>(
3659 resolution_packets: &'a [PreparedCompactResolutionPacket<'a>],
3660) -> Vec<J2kPacketizationResolution<'a>> {
3661 resolution_packets
3662 .iter()
3663 .map(|resolution| J2kPacketizationResolution {
3664 subbands: resolution
3665 .subbands
3666 .iter()
3667 .map(|subband| J2kPacketizationSubband {
3668 code_blocks: subband
3669 .code_blocks
3670 .iter()
3671 .map(|code_block| J2kPacketizationCodeBlock {
3672 data: code_block.data,
3673 ht_cleanup_length: code_block.cleanup_length,
3674 ht_refinement_length: code_block.refinement_length,
3675 num_coding_passes: code_block.num_coding_passes,
3676 num_zero_bitplanes: code_block.num_zero_bitplanes,
3677 previously_included: false,
3678 l_block: 3,
3679 block_coding_mode: J2kPacketizationBlockCodingMode::HighThroughput,
3680 })
3681 .collect(),
3682 num_cbs_x: subband.num_cbs_x,
3683 num_cbs_y: subband.num_cbs_y,
3684 })
3685 .collect(),
3686 })
3687 .collect()
3688}
3689
3690fn public_packetization_block_coding_mode(
3691 block_coding_mode: BlockCodingMode,
3692) -> J2kPacketizationBlockCodingMode {
3693 match block_coding_mode {
3694 BlockCodingMode::Classic => J2kPacketizationBlockCodingMode::Classic,
3695 BlockCodingMode::HighThroughput => J2kPacketizationBlockCodingMode::HighThroughput,
3696 }
3697}
3698
3699#[derive(Clone)]
3700struct PreparedEncodeCodeBlock {
3701 coefficients: Vec<i32>,
3702 width: u32,
3703 height: u32,
3704}
3705
3706#[derive(Clone)]
3707struct PreparedEncodeSubband {
3708 code_blocks: Vec<PreparedEncodeCodeBlock>,
3709 preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
3710 num_cbs_x: u32,
3711 num_cbs_y: u32,
3712 code_block_width: u32,
3713 code_block_height: u32,
3714 width: u32,
3715 height: u32,
3716 sub_band_type: SubBandType,
3717 total_bitplanes: u8,
3718 block_coding_mode: BlockCodingMode,
3719}
3720
3721struct PreparedResolutionPacket {
3722 component: u8,
3723 resolution: u32,
3724 precinct: u64,
3725 subbands: Vec<PreparedEncodeSubband>,
3726}
3727
3728struct PreparedCompactCodeBlock<'a> {
3729 data: &'a [u8],
3730 cleanup_length: u32,
3731 refinement_length: u32,
3732 num_coding_passes: u8,
3733 num_zero_bitplanes: u8,
3734}
3735
3736struct PreparedCompactSubband<'a> {
3737 code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
3738 num_cbs_x: u32,
3739 num_cbs_y: u32,
3740}
3741
3742struct PreparedCompactResolutionPacket<'a> {
3743 component: u8,
3744 resolution: u32,
3745 precinct: u64,
3746 subbands: Vec<PreparedCompactSubband<'a>>,
3747}
3748
3749struct PreparedPrecomputedHtj2k97Image {
3750 params: EncodeParams,
3751 quant_params: Vec<(u16, u16)>,
3752 packet_descriptors: Vec<J2kPacketizationPacketDescriptor>,
3753 packet_count: usize,
3754 prepared_packets: Vec<PreparedResolutionPacket>,
3755}
3756
3757fn prepare_precomputed_htj2k97_image_for_batch(
3758 image: &PrecomputedHtj2k97Image,
3759 options: &EncodeOptions,
3760) -> Result<PreparedPrecomputedHtj2k97Image, &'static str> {
3761 if image.width == 0 || image.height == 0 {
3762 return Err("invalid dimensions");
3763 }
3764 if image.components.is_empty() || image.components.len() > 4 {
3765 return Err("unsupported component count");
3766 }
3767 if image.bit_depth == 0 || image.bit_depth > 16 {
3768 return Err("unsupported bit depth");
3769 }
3770 validate_irreversible_quantization_profile(options)?;
3771 if image
3772 .components
3773 .iter()
3774 .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
3775 {
3776 return Err("component sampling factors must be non-zero");
3777 }
3778 validate_precomputed_dwt97_geometry(image)?;
3779
3780 let num_components =
3781 u8::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
3782 let num_levels = precomputed_97_level_count(&image.components)?;
3783 let guard_bits = options.guard_bits.max(2);
3784 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
3785 image.bit_depth,
3786 num_levels,
3787 false,
3788 guard_bits,
3789 options.irreversible_quantization_scale,
3790 options.irreversible_quantization_subband_scales,
3791 );
3792 let quant_params: Vec<(u16, u16)> = step_sizes
3793 .iter()
3794 .map(|s| (s.exponent, s.mantissa))
3795 .collect();
3796 let cb_width = 1u32 << (options.code_block_width_exp + 2);
3797 let cb_height = 1u32 << (options.code_block_height_exp + 2);
3798 let component_sampling = image
3799 .components
3800 .iter()
3801 .map(|component| (component.x_rsiz, component.y_rsiz))
3802 .collect::<Vec<_>>();
3803 let mut precomputed_options = options.clone();
3804 precomputed_options.num_decomposition_levels = num_levels;
3805 precomputed_options.reversible = false;
3806 precomputed_options.use_ht_block_coding = true;
3807 precomputed_options.use_mct = false;
3808 precomputed_options.validate_high_throughput_codestream = false;
3809 precomputed_options.component_sampling = Some(component_sampling.clone());
3810 let precinct_exponents = precinct_exponents_for_options(&precomputed_options, num_levels)?;
3811 let params = EncodeParams {
3812 width: image.width,
3813 height: image.height,
3814 tile_width: image.width,
3815 tile_height: image.height,
3816 num_components,
3817 bit_depth: image.bit_depth,
3818 signed: image.signed,
3819 num_decomposition_levels: num_levels,
3820 reversible: false,
3821 code_block_width_exp: precomputed_options.code_block_width_exp,
3822 code_block_height_exp: precomputed_options.code_block_height_exp,
3823 num_layers: 1,
3824 use_mct: false,
3825 guard_bits,
3826 block_coding_mode: BlockCodingMode::HighThroughput,
3827 progression_order: precomputed_options.progression_order,
3828 write_tlm: precomputed_options.write_tlm,
3829 write_plt: precomputed_options.write_plt,
3830 write_plm: precomputed_options.write_plm,
3831 write_sop: precomputed_options.write_sop,
3832 write_eph: precomputed_options.write_eph,
3833 terminate_coding_passes: false,
3834 component_sampling,
3835 precinct_exponents,
3836 };
3837
3838 let component_resolution_packets = image
3839 .components
3840 .iter()
3841 .enumerate()
3842 .map(|(component_idx, component)| {
3843 prepared_resolution_packets_from_precomputed_97_component(
3844 component_idx,
3845 component,
3846 &step_sizes,
3847 image.bit_depth,
3848 guard_bits,
3849 cb_width,
3850 cb_height,
3851 )
3852 })
3853 .collect::<Result<Vec<_>, _>>()?;
3854 let component_resolution_packets = split_component_resolution_packets_by_precinct(
3855 component_resolution_packets,
3856 image.width,
3857 image.height,
3858 num_levels,
3859 ¶ms.precinct_exponents,
3860 )?;
3861 let prepared_packets =
3862 ordered_prepared_resolution_packets(component_resolution_packets, &precomputed_options)?;
3863 let packet_descriptors =
3864 packet_descriptors_for_order(&prepared_packets, 1, precomputed_options.progression_order)?;
3865
3866 Ok(PreparedPrecomputedHtj2k97Image {
3867 params,
3868 quant_params,
3869 packet_descriptors,
3870 packet_count: 0,
3871 prepared_packets,
3872 })
3873}
3874
3875fn prepared_resolution_packets_from_precomputed_97_component(
3876 component_idx: usize,
3877 component: &PrecomputedHtj2k97Component,
3878 step_sizes: &[QuantStepSize],
3879 bit_depth: u8,
3880 guard_bits: u8,
3881 cb_width: u32,
3882 cb_height: u32,
3883) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
3884 let component_idx = u8::try_from(component_idx).map_err(|_| "component index exceeds u8")?;
3885 let mut packets = Vec::with_capacity(component.dwt.levels.len() + 1);
3886 packets.push(PreparedResolutionPacket {
3887 component: component_idx,
3888 resolution: 0,
3889 precinct: 0,
3890 subbands: vec![prepare_subband_cpu_quantized(
3891 &component.dwt.ll,
3892 component.dwt.ll_width,
3893 component.dwt.ll_height,
3894 step_sizes
3895 .first()
3896 .ok_or("irreversible quantization step missing")?,
3897 bit_depth,
3898 guard_bits,
3899 false,
3900 BlockCodingMode::HighThroughput,
3901 cb_width,
3902 cb_height,
3903 SubBandType::LowLow,
3904 )?],
3905 });
3906
3907 for (level_idx, level) in component.dwt.levels.iter().enumerate() {
3908 let step_base = 1 + level_idx * 3;
3909 packets.push(PreparedResolutionPacket {
3910 component: component_idx,
3911 resolution: u32::try_from(level_idx + 1).map_err(|_| "resolution index exceeds u32")?,
3912 precinct: 0,
3913 subbands: vec![
3914 prepare_subband_cpu_quantized(
3915 &level.hl,
3916 level.high_width,
3917 level.low_height,
3918 step_sizes
3919 .get(step_base)
3920 .ok_or("irreversible quantization step missing")?,
3921 bit_depth,
3922 guard_bits,
3923 false,
3924 BlockCodingMode::HighThroughput,
3925 cb_width,
3926 cb_height,
3927 SubBandType::HighLow,
3928 )?,
3929 prepare_subband_cpu_quantized(
3930 &level.lh,
3931 level.low_width,
3932 level.high_height,
3933 step_sizes
3934 .get(step_base + 1)
3935 .ok_or("irreversible quantization step missing")?,
3936 bit_depth,
3937 guard_bits,
3938 false,
3939 BlockCodingMode::HighThroughput,
3940 cb_width,
3941 cb_height,
3942 SubBandType::LowHigh,
3943 )?,
3944 prepare_subband_cpu_quantized(
3945 &level.hh,
3946 level.high_width,
3947 level.high_height,
3948 step_sizes
3949 .get(step_base + 2)
3950 .ok_or("irreversible quantization step missing")?,
3951 bit_depth,
3952 guard_bits,
3953 false,
3954 BlockCodingMode::HighThroughput,
3955 cb_width,
3956 cb_height,
3957 SubBandType::HighHigh,
3958 )?,
3959 ],
3960 });
3961 }
3962
3963 Ok(packets)
3964}
3965
3966fn copy_code_block_coefficients(
3967 quantized: &[i32],
3968 width: usize,
3969 x0: usize,
3970 y0: usize,
3971 cbw: usize,
3972 cbh: usize,
3973) -> Vec<i32> {
3974 let len = cbw * cbh;
3975 let start = y0 * width + x0;
3976 if cbw == width {
3977 return quantized[start..start + len].to_vec();
3978 }
3979
3980 let mut coefficients = Vec::with_capacity(len);
3981 for y in 0..cbh {
3982 let row_start = (y0 + y) * width + x0;
3983 coefficients.extend_from_slice(&quantized[row_start..row_start + cbw]);
3984 }
3985 coefficients
3986}
3987
3988fn prepare_subband(
3989 coefficients: &[f32],
3990 width: u32,
3991 height: u32,
3992 step_size: &QuantStepSize,
3993 bit_depth: u8,
3994 guard_bits: u8,
3995 reversible: bool,
3996 block_coding_mode: BlockCodingMode,
3997 cb_width: u32,
3998 cb_height: u32,
3999 sub_band_type: SubBandType,
4000 accelerator: &mut impl J2kEncodeStageAccelerator,
4001) -> Result<PreparedEncodeSubband, &'static str> {
4002 if width == 0 || height == 0 {
4003 return Ok(PreparedEncodeSubband {
4004 code_blocks: Vec::new(),
4005 preencoded_ht_code_blocks: None,
4006 num_cbs_x: 0,
4007 num_cbs_y: 0,
4008 code_block_width: cb_width,
4009 code_block_height: cb_height,
4010 width,
4011 height,
4012 sub_band_type,
4013 total_bitplanes: 0,
4014 block_coding_mode,
4015 });
4016 }
4017
4018 let range_bits = subband_range_bits(bit_depth, sub_band_type);
4019 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
4020 let total_bitplanes = guard_bits
4021 .saturating_add(step_size.exponent as u8)
4022 .saturating_sub(1);
4023 let num_cbs_x = width.div_ceil(cb_width);
4024 let num_cbs_y = height.div_ceil(cb_height);
4025
4026 if block_coding_mode == BlockCodingMode::HighThroughput {
4027 if let Some(encoded) = accelerator.encode_ht_subband(J2kHtSubbandEncodeJob {
4028 coefficients,
4029 width,
4030 height,
4031 step_exponent: step_size.exponent,
4032 step_mantissa: step_size.mantissa,
4033 range_bits,
4034 reversible,
4035 code_block_width: cb_width,
4036 code_block_height: cb_height,
4037 total_bitplanes,
4038 })? {
4039 let expected_code_blocks = (num_cbs_x as usize)
4040 .checked_mul(num_cbs_y as usize)
4041 .ok_or("code-block count overflow")?;
4042 if encoded.len() != expected_code_blocks {
4043 return Err("accelerated HT subband code-block count mismatch");
4044 }
4045 return Ok(PreparedEncodeSubband {
4046 code_blocks: code_block_shapes(width, height, cb_width, cb_height)?,
4047 preencoded_ht_code_blocks: Some(encoded),
4048 num_cbs_x,
4049 num_cbs_y,
4050 code_block_width: cb_width,
4051 code_block_height: cb_height,
4052 width,
4053 height,
4054 sub_band_type,
4055 total_bitplanes,
4056 block_coding_mode,
4057 });
4058 }
4059 }
4060
4061 let quantized = match accelerator.encode_quantize_subband(J2kQuantizeSubbandJob {
4062 coefficients,
4063 step_exponent: step_size.exponent,
4064 step_mantissa: step_size.mantissa,
4065 range_bits,
4066 reversible,
4067 })? {
4068 Some(quantized) => {
4069 if quantized.len() != coefficients.len() {
4070 return Err("accelerated quantized subband length mismatch");
4071 }
4072 quantized
4073 }
4074 None => quantize::quantize_subband(coefficients, step_size, range_bits, reversible),
4075 };
4076
4077 let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
4079
4080 for cby in 0..num_cbs_y {
4081 for cbx in 0..num_cbs_x {
4082 let x0 = cbx * cb_width;
4083 let y0 = cby * cb_height;
4084 let x1 = (x0 + cb_width).min(width);
4085 let y1 = (y0 + cb_height).min(height);
4086 let cbw = x1 - x0;
4087 let cbh = y1 - y0;
4088
4089 let cb_coeffs = copy_code_block_coefficients(
4090 &quantized,
4091 width as usize,
4092 x0 as usize,
4093 y0 as usize,
4094 cbw as usize,
4095 cbh as usize,
4096 );
4097
4098 code_blocks.push(PreparedEncodeCodeBlock {
4099 coefficients: cb_coeffs,
4100 width: cbw,
4101 height: cbh,
4102 });
4103 }
4104 }
4105
4106 Ok(PreparedEncodeSubband {
4107 code_blocks,
4108 preencoded_ht_code_blocks: None,
4109 num_cbs_x,
4110 num_cbs_y,
4111 code_block_width: cb_width,
4112 code_block_height: cb_height,
4113 width,
4114 height,
4115 sub_band_type,
4116 total_bitplanes,
4117 block_coding_mode,
4118 })
4119}
4120
4121fn prepare_subband_cpu_quantized(
4122 coefficients: &[f32],
4123 width: u32,
4124 height: u32,
4125 step_size: &QuantStepSize,
4126 bit_depth: u8,
4127 guard_bits: u8,
4128 reversible: bool,
4129 block_coding_mode: BlockCodingMode,
4130 cb_width: u32,
4131 cb_height: u32,
4132 sub_band_type: SubBandType,
4133) -> Result<PreparedEncodeSubband, &'static str> {
4134 if width == 0 || height == 0 {
4135 return Ok(PreparedEncodeSubband {
4136 code_blocks: Vec::new(),
4137 preencoded_ht_code_blocks: None,
4138 num_cbs_x: 0,
4139 num_cbs_y: 0,
4140 code_block_width: cb_width,
4141 code_block_height: cb_height,
4142 width,
4143 height,
4144 sub_band_type,
4145 total_bitplanes: 0,
4146 block_coding_mode,
4147 });
4148 }
4149
4150 let range_bits = subband_range_bits(bit_depth, sub_band_type);
4151 debug_assert!(step_size.exponent <= u16::from(u8::MAX));
4152 let total_bitplanes = guard_bits
4153 .saturating_add(step_size.exponent as u8)
4154 .saturating_sub(1);
4155 let num_cbs_x = width.div_ceil(cb_width);
4156 let num_cbs_y = height.div_ceil(cb_height);
4157 let quantized = quantize::quantize_subband(coefficients, step_size, range_bits, reversible);
4158 let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
4159
4160 for cby in 0..num_cbs_y {
4161 for cbx in 0..num_cbs_x {
4162 let x0 = cbx * cb_width;
4163 let y0 = cby * cb_height;
4164 let x1 = (x0 + cb_width).min(width);
4165 let y1 = (y0 + cb_height).min(height);
4166 let cbw = x1 - x0;
4167 let cbh = y1 - y0;
4168 let cb_coeffs = copy_code_block_coefficients(
4169 &quantized,
4170 width as usize,
4171 x0 as usize,
4172 y0 as usize,
4173 cbw as usize,
4174 cbh as usize,
4175 );
4176
4177 code_blocks.push(PreparedEncodeCodeBlock {
4178 coefficients: cb_coeffs,
4179 width: cbw,
4180 height: cbh,
4181 });
4182 }
4183 }
4184
4185 Ok(PreparedEncodeSubband {
4186 code_blocks,
4187 preencoded_ht_code_blocks: None,
4188 num_cbs_x,
4189 num_cbs_y,
4190 code_block_width: cb_width,
4191 code_block_height: cb_height,
4192 width,
4193 height,
4194 sub_band_type,
4195 total_bitplanes,
4196 block_coding_mode,
4197 })
4198}
4199
4200fn code_block_shapes(
4201 width: u32,
4202 height: u32,
4203 cb_width: u32,
4204 cb_height: u32,
4205) -> Result<Vec<PreparedEncodeCodeBlock>, &'static str> {
4206 let num_cbs_x = width.div_ceil(cb_width);
4207 let num_cbs_y = height.div_ceil(cb_height);
4208 let count = (num_cbs_x as usize)
4209 .checked_mul(num_cbs_y as usize)
4210 .ok_or("code-block count overflow")?;
4211 let mut code_blocks = Vec::with_capacity(count);
4212 for cby in 0..num_cbs_y {
4213 for cbx in 0..num_cbs_x {
4214 let x0 = cbx * cb_width;
4215 let y0 = cby * cb_height;
4216 let x1 = (x0 + cb_width).min(width);
4217 let y1 = (y0 + cb_height).min(height);
4218 code_blocks.push(PreparedEncodeCodeBlock {
4219 coefficients: Vec::new(),
4220 width: x1 - x0,
4221 height: y1 - y0,
4222 });
4223 }
4224 }
4225 Ok(code_blocks)
4226}
4227
4228fn subband_range_bits(bit_depth: u8, sub_band_type: SubBandType) -> u8 {
4229 let log_gain = match sub_band_type {
4230 SubBandType::LowLow => 0,
4231 SubBandType::LowHigh | SubBandType::HighLow => 1,
4232 SubBandType::HighHigh => 2,
4233 };
4234
4235 bit_depth.saturating_add(log_gain)
4236}
4237
4238fn encode_prepared_resolution_packets(
4239 prepared_packets: Vec<PreparedResolutionPacket>,
4240 accelerator: &mut impl J2kEncodeStageAccelerator,
4241) -> Result<Vec<ResolutionPacket>, &'static str> {
4242 let subband_counts: Vec<_> = prepared_packets
4243 .iter()
4244 .map(|packet| packet.subbands.len())
4245 .collect();
4246 let prepared_subbands: Vec<_> = prepared_packets
4247 .into_iter()
4248 .flat_map(|packet| packet.subbands)
4249 .collect();
4250 let mut encoded_subbands =
4251 encode_prepared_subbands(prepared_subbands, accelerator)?.into_iter();
4252
4253 subband_counts
4254 .into_iter()
4255 .map(|subband_count| {
4256 let mut subbands = Vec::with_capacity(subband_count);
4257 for _ in 0..subband_count {
4258 subbands.push(
4259 encoded_subbands
4260 .next()
4261 .ok_or("encoded subband count mismatch")?,
4262 );
4263 }
4264 Ok(ResolutionPacket { subbands })
4265 })
4266 .collect()
4267}
4268
4269fn encode_prepared_resolution_packets_layered(
4270 prepared_packets: Vec<PreparedResolutionPacket>,
4271 num_layers: u8,
4272 progression_order: EncodeProgressionOrder,
4273 quality_layer_byte_targets: &[u64],
4274 accelerator: &mut impl J2kEncodeStageAccelerator,
4275) -> Result<(Vec<ResolutionPacket>, Vec<J2kPacketizationPacketDescriptor>), &'static str> {
4276 let layer_count = usize::from(num_layers);
4277 let mut layered_packets = Vec::with_capacity(prepared_packets.len());
4278 let mut classic_candidates = Vec::new();
4279 let mut classic_locations = Vec::new();
4280 let mut classic_block_index = 0usize;
4281 let mut ht_candidates = Vec::new();
4282 let mut ht_locations = Vec::new();
4283 let mut ht_block_index = 0usize;
4284
4285 for prepared_packet in prepared_packets {
4286 let packet_idx = layered_packets.len();
4287 let mut layered_packet = LayeredPreparedPacket {
4288 component: prepared_packet.component,
4289 resolution: prepared_packet.resolution,
4290 precinct: prepared_packet.precinct,
4291 subbands: Vec::with_capacity(prepared_packet.subbands.len()),
4292 };
4293
4294 for subband in prepared_packet.subbands {
4295 let subband_idx = layered_packet.subbands.len();
4296 let mut layered_subband = LayeredPreparedSubband {
4297 num_cbs_x: subband.num_cbs_x,
4298 num_cbs_y: subband.num_cbs_y,
4299 blocks: Vec::with_capacity(subband.code_blocks.len()),
4300 };
4301
4302 match subband.block_coding_mode {
4303 BlockCodingMode::Classic => {
4304 for block in subband.code_blocks {
4305 let block_idx = layered_subband.blocks.len();
4306 let encoded = bitplane_encode::encode_code_block_segments_with_style(
4307 &block.coefficients,
4308 block.width,
4309 block.height,
4310 subband.sub_band_type,
4311 subband.total_bitplanes,
4312 &classic_multilayer_code_block_style(),
4313 );
4314 let segment_layers = if quality_layer_byte_targets.is_empty() {
4315 classic_unbudgeted_segment_layers(&encoded, num_layers)?
4316 } else {
4317 for (segment_idx, segment) in encoded.segments.iter().enumerate() {
4318 classic_candidates.push(ClassicSegmentAssignmentCandidate {
4319 block_index: classic_block_index,
4320 segment_index: segment_idx,
4321 rate: u64::from(segment.data_length),
4322 distortion_delta: segment.distortion_delta,
4323 });
4324 classic_locations.push(ClassicSegmentLocation {
4325 packet_idx,
4326 subband_idx,
4327 block_idx,
4328 segment_idx,
4329 });
4330 }
4331 vec![layer_count.saturating_sub(1); encoded.segments.len()]
4332 };
4333 layered_subband.blocks.push(LayeredPreparedBlock::Classic {
4334 encoded,
4335 segment_layers,
4336 });
4337 classic_block_index = classic_block_index
4338 .checked_add(1)
4339 .ok_or("classic PCRD block index overflow")?;
4340 }
4341 }
4342 BlockCodingMode::HighThroughput => {
4343 let encoded_blocks =
4344 encode_all_ht_code_blocks(core::slice::from_ref(&subband), accelerator)?;
4345 let block_count = encoded_blocks.len();
4346 for (block_idx, encoded) in encoded_blocks.into_iter().enumerate() {
4347 let target_layer = if quality_layer_byte_targets.is_empty() {
4348 ht_target_layer(block_idx, block_count, layer_count)?
4349 } else {
4350 ht_candidates.push(HtSegmentAssignmentCandidate {
4351 block_index: ht_block_index,
4352 rate: u64::try_from(encoded.data.len())
4353 .map_err(|_| "HTJ2K packet contribution length exceeds u64")?,
4354 });
4355 ht_locations.push(HtSegmentLocation {
4356 packet_idx,
4357 subband_idx,
4358 block_idx: layered_subband.blocks.len(),
4359 });
4360 layer_count.saturating_sub(1)
4361 };
4362 layered_subband
4363 .blocks
4364 .push(LayeredPreparedBlock::HighThroughput {
4365 encoded,
4366 target_layer,
4367 });
4368 ht_block_index = ht_block_index
4369 .checked_add(1)
4370 .ok_or("HTJ2K segment block index overflow")?;
4371 }
4372 }
4373 }
4374
4375 layered_packet.subbands.push(layered_subband);
4376 }
4377
4378 layered_packets.push(layered_packet);
4379 }
4380
4381 if !quality_layer_byte_targets.is_empty() {
4382 let assignments = assign_classic_segment_layers_by_slope(
4383 &classic_candidates,
4384 layer_count,
4385 quality_layer_byte_targets,
4386 )?;
4387 for (assignment_idx, layer) in assignments.into_iter().enumerate() {
4388 let location = classic_locations
4389 .get(assignment_idx)
4390 .ok_or("classic PCRD assignment location mismatch")?;
4391 let block = layered_packets
4392 .get_mut(location.packet_idx)
4393 .ok_or("classic PCRD packet index mismatch")?
4394 .subbands
4395 .get_mut(location.subband_idx)
4396 .ok_or("classic PCRD subband index mismatch")?
4397 .blocks
4398 .get_mut(location.block_idx)
4399 .ok_or("classic PCRD block index mismatch")?;
4400 let LayeredPreparedBlock::Classic { segment_layers, .. } = block else {
4401 return Err("classic PCRD assignment referenced HT block");
4402 };
4403 let segment_layer = segment_layers
4404 .get_mut(location.segment_idx)
4405 .ok_or("classic PCRD segment index mismatch")?;
4406 *segment_layer = layer;
4407 }
4408 enforce_classic_segment_layer_monotonicity(&mut layered_packets);
4409 }
4410 if !quality_layer_byte_targets.is_empty() {
4411 let assignments = assign_ht_segment_layers_by_budget(
4412 &ht_candidates,
4413 layer_count,
4414 quality_layer_byte_targets,
4415 )?;
4416 for (assignment_idx, layer) in assignments.into_iter().enumerate() {
4417 let location = ht_locations
4418 .get(assignment_idx)
4419 .ok_or("HTJ2K segment assignment location mismatch")?;
4420 let block = layered_packets
4421 .get_mut(location.packet_idx)
4422 .ok_or("HTJ2K packet index mismatch")?
4423 .subbands
4424 .get_mut(location.subband_idx)
4425 .ok_or("HTJ2K subband index mismatch")?
4426 .blocks
4427 .get_mut(location.block_idx)
4428 .ok_or("HTJ2K block index mismatch")?;
4429 let LayeredPreparedBlock::HighThroughput { target_layer, .. } = block else {
4430 return Err("HTJ2K segment assignment referenced classic block");
4431 };
4432 *target_layer = layer;
4433 }
4434 }
4435
4436 let mut resolution_packets = Vec::with_capacity(layered_packets.len() * layer_count);
4437 let mut descriptors = Vec::with_capacity(layered_packets.len() * layer_count);
4438 for (state_index, layered_packet) in layered_packets.into_iter().enumerate() {
4439 let mut layer_packets: Vec<_> = (0..layer_count)
4440 .map(|_| ResolutionPacket {
4441 subbands: Vec::with_capacity(layered_packet.subbands.len()),
4442 })
4443 .collect();
4444
4445 for subband in layered_packet.subbands {
4446 let mut layer_subbands: Vec<_> = (0..layer_count)
4447 .map(|_| SubbandPrecinct {
4448 code_blocks: Vec::with_capacity(subband.blocks.len()),
4449 num_cbs_x: subband.num_cbs_x,
4450 num_cbs_y: subband.num_cbs_y,
4451 })
4452 .collect();
4453
4454 for block in subband.blocks {
4455 let contributions = match block {
4456 LayeredPreparedBlock::Classic {
4457 encoded,
4458 segment_layers,
4459 } => classic_layer_contributions(encoded, num_layers, &segment_layers)?,
4460 LayeredPreparedBlock::HighThroughput {
4461 encoded,
4462 target_layer,
4463 } => ht_layer_contributions(encoded, num_layers, target_layer)?,
4464 };
4465 for (layer_idx, contribution) in contributions.into_iter().enumerate() {
4466 layer_subbands[layer_idx].code_blocks.push(contribution);
4467 }
4468 }
4469
4470 for (layer_packet, layer_subband) in layer_packets.iter_mut().zip(layer_subbands) {
4471 layer_packet.subbands.push(layer_subband);
4472 }
4473 }
4474
4475 let state_index =
4476 u32::try_from(state_index).map_err(|_| "packet descriptor state index exceeds u32")?;
4477 for (layer_idx, layer_packet) in layer_packets.into_iter().enumerate() {
4478 let packet_index = u32::try_from(resolution_packets.len())
4479 .map_err(|_| "packet descriptor index exceeds u32")?;
4480 resolution_packets.push(layer_packet);
4481 descriptors.push(J2kPacketizationPacketDescriptor {
4482 packet_index,
4483 state_index,
4484 layer: u8::try_from(layer_idx).map_err(|_| "quality layer index exceeds u8")?,
4485 resolution: layered_packet.resolution,
4486 component: layered_packet.component,
4487 precinct: layered_packet.precinct,
4488 });
4489 }
4490 }
4491
4492 sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
4493
4494 Ok((resolution_packets, descriptors))
4495}
4496
4497fn sort_packet_descriptors_for_progression(
4498 descriptors: &mut [J2kPacketizationPacketDescriptor],
4499 progression_order: EncodeProgressionOrder,
4500) {
4501 match progression_order {
4502 EncodeProgressionOrder::Lrcp => descriptors.sort_by_key(|descriptor| {
4503 (
4504 descriptor.layer,
4505 descriptor.resolution,
4506 descriptor.component,
4507 descriptor.precinct,
4508 )
4509 }),
4510 EncodeProgressionOrder::Rlcp => descriptors.sort_by_key(|descriptor| {
4511 (
4512 descriptor.resolution,
4513 descriptor.layer,
4514 descriptor.component,
4515 descriptor.precinct,
4516 )
4517 }),
4518 EncodeProgressionOrder::Rpcl => descriptors.sort_by_key(|descriptor| {
4519 (
4520 descriptor.resolution,
4521 descriptor.precinct,
4522 descriptor.component,
4523 descriptor.layer,
4524 )
4525 }),
4526 EncodeProgressionOrder::Pcrl => descriptors.sort_by_key(|descriptor| {
4527 (
4528 descriptor.precinct,
4529 descriptor.component,
4530 descriptor.resolution,
4531 descriptor.layer,
4532 )
4533 }),
4534 EncodeProgressionOrder::Cprl => descriptors.sort_by_key(|descriptor| {
4535 (
4536 descriptor.component,
4537 descriptor.precinct,
4538 descriptor.resolution,
4539 descriptor.layer,
4540 )
4541 }),
4542 }
4543}
4544
4545fn classic_multilayer_code_block_style() -> CodeBlockStyle {
4546 CodeBlockStyle {
4547 termination_on_each_pass: true,
4548 ..CodeBlockStyle::default()
4549 }
4550}
4551
4552struct LayeredPreparedPacket {
4553 component: u8,
4554 resolution: u32,
4555 precinct: u64,
4556 subbands: Vec<LayeredPreparedSubband>,
4557}
4558
4559struct LayeredPreparedSubband {
4560 num_cbs_x: u32,
4561 num_cbs_y: u32,
4562 blocks: Vec<LayeredPreparedBlock>,
4563}
4564
4565enum LayeredPreparedBlock {
4566 Classic {
4567 encoded: bitplane_encode::EncodedCodeBlockWithSegments,
4568 segment_layers: Vec<usize>,
4569 },
4570 HighThroughput {
4571 encoded: bitplane_encode::EncodedCodeBlock,
4572 target_layer: usize,
4573 },
4574}
4575
4576#[derive(Debug, Clone, Copy, PartialEq)]
4577struct ClassicSegmentAssignmentCandidate {
4578 block_index: usize,
4579 segment_index: usize,
4580 rate: u64,
4581 distortion_delta: f64,
4582}
4583
4584#[derive(Debug, Clone, Copy)]
4585struct ClassicSegmentLocation {
4586 packet_idx: usize,
4587 subband_idx: usize,
4588 block_idx: usize,
4589 segment_idx: usize,
4590}
4591
4592#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4593struct HtSegmentAssignmentCandidate {
4594 block_index: usize,
4595 rate: u64,
4596}
4597
4598#[derive(Debug, Clone, Copy)]
4599struct HtSegmentLocation {
4600 packet_idx: usize,
4601 subband_idx: usize,
4602 block_idx: usize,
4603}
4604
4605struct ClassicLayerBudgetAllocator {
4606 cumulative_targets: Vec<u64>,
4607 cumulative_used: Vec<u64>,
4608}
4609
4610impl ClassicLayerBudgetAllocator {
4611 fn new(cumulative_targets: &[u64], layer_count: usize) -> Result<Self, &'static str> {
4612 if cumulative_targets.is_empty() {
4613 return Ok(Self {
4614 cumulative_targets: Vec::new(),
4615 cumulative_used: Vec::new(),
4616 });
4617 }
4618 if cumulative_targets.len() != layer_count {
4619 return Err("quality layer byte target count must match quality layer count");
4620 }
4621 if cumulative_targets.windows(2).any(|pair| pair[0] > pair[1]) {
4622 return Err("quality layer byte targets must be cumulative and monotonic");
4623 }
4624 Ok(Self {
4625 cumulative_targets: cumulative_targets
4626 .iter()
4627 .map(|&target| target.saturating_add(classic_rate_target_tolerance(target)))
4628 .collect(),
4629 cumulative_used: vec![0; layer_count],
4630 })
4631 }
4632
4633 fn is_budgeted(&self) -> bool {
4634 !self.cumulative_targets.is_empty()
4635 }
4636
4637 fn assign_segment(
4638 &mut self,
4639 min_layer: usize,
4640 data_length: u64,
4641 ) -> Result<usize, &'static str> {
4642 if !self.is_budgeted() {
4643 return Ok(min_layer);
4644 }
4645
4646 let rate = data_length;
4647 let last_layer = self
4648 .cumulative_targets
4649 .len()
4650 .checked_sub(1)
4651 .ok_or("quality layer target count underflow")?;
4652 for layer_idx in min_layer..last_layer {
4653 if self.layer_can_accept(layer_idx, rate)? {
4654 self.record_segment(layer_idx, rate)?;
4655 return Ok(layer_idx);
4656 }
4657 }
4658 self.record_segment(last_layer, rate)?;
4659 Ok(last_layer)
4660 }
4661
4662 fn layer_can_accept(&self, layer_idx: usize, rate: u64) -> Result<bool, &'static str> {
4663 for cumulative_idx in layer_idx..self.cumulative_targets.len() {
4664 let used = self.cumulative_used[cumulative_idx]
4665 .checked_add(rate)
4666 .ok_or("quality layer byte budget overflow")?;
4667 if used > self.cumulative_targets[cumulative_idx] {
4668 return Ok(false);
4669 }
4670 }
4671 Ok(true)
4672 }
4673
4674 fn record_segment(&mut self, layer_idx: usize, rate: u64) -> Result<(), &'static str> {
4675 for used in &mut self.cumulative_used[layer_idx..] {
4676 *used = used
4677 .checked_add(rate)
4678 .ok_or("quality layer byte budget overflow")?;
4679 }
4680 Ok(())
4681 }
4682}
4683
4684fn classic_rate_target_tolerance(target: u64) -> u64 {
4685 (target / 100).max(512)
4686}
4687
4688fn assign_classic_segment_layers_by_slope(
4689 candidates: &[ClassicSegmentAssignmentCandidate],
4690 layer_count: usize,
4691 cumulative_targets: &[u64],
4692) -> Result<Vec<usize>, &'static str> {
4693 let mut allocator = ClassicLayerBudgetAllocator::new(cumulative_targets, layer_count)?;
4694 if candidates.is_empty() {
4695 return Ok(Vec::new());
4696 }
4697
4698 let block_count = candidates
4699 .iter()
4700 .map(|candidate| candidate.block_index)
4701 .max()
4702 .and_then(|max| max.checked_add(1))
4703 .ok_or("classic PCRD block count overflow")?;
4704 let mut block_candidates = vec![Vec::new(); block_count];
4705 for (candidate_idx, candidate) in candidates.iter().enumerate() {
4706 block_candidates
4707 .get_mut(candidate.block_index)
4708 .ok_or("classic PCRD block index mismatch")?
4709 .push(candidate_idx);
4710 }
4711 for block in &mut block_candidates {
4712 block.sort_by_key(|&idx| candidates[idx].segment_index);
4713 }
4714
4715 let mut block_min_layers = vec![0usize; block_count];
4716 let mut assignments = vec![layer_count.saturating_sub(1); candidates.len()];
4717 let mut next_block_segment = vec![0usize; block_count];
4718 let mut remaining = candidates.len();
4719 while remaining > 0 {
4720 let candidate_idx = block_candidates
4721 .iter()
4722 .enumerate()
4723 .filter_map(|(block_idx, block)| block.get(next_block_segment[block_idx]).copied())
4724 .min_by(|&left, &right| compare_classic_segment_candidates(candidates, left, right))
4725 .ok_or("classic PCRD candidate queue underflow")?;
4726 let candidate = candidates[candidate_idx];
4727 let min_layer = *block_min_layers
4728 .get(candidate.block_index)
4729 .ok_or("classic PCRD block index mismatch")?;
4730 let layer = allocator.assign_segment(min_layer, candidate.rate)?;
4731 assignments[candidate_idx] = layer;
4732 if let Some(block_layer) = block_min_layers.get_mut(candidate.block_index) {
4733 *block_layer = layer;
4734 }
4735 if let Some(next) = next_block_segment.get_mut(candidate.block_index) {
4736 *next = next
4737 .checked_add(1)
4738 .ok_or("classic PCRD segment index overflow")?;
4739 }
4740 remaining -= 1;
4741 }
4742
4743 enforce_classic_assignment_monotonicity(candidates, &mut assignments);
4744 Ok(assignments)
4745}
4746
4747fn compare_classic_segment_candidates(
4748 candidates: &[ClassicSegmentAssignmentCandidate],
4749 left: usize,
4750 right: usize,
4751) -> Ordering {
4752 let left_candidate = candidates[left];
4753 let right_candidate = candidates[right];
4754 pcrd_slope(right_candidate)
4755 .partial_cmp(&pcrd_slope(left_candidate))
4756 .unwrap_or(Ordering::Equal)
4757 .then_with(|| left_candidate.block_index.cmp(&right_candidate.block_index))
4758 .then_with(|| {
4759 left_candidate
4760 .segment_index
4761 .cmp(&right_candidate.segment_index)
4762 })
4763}
4764
4765fn pcrd_slope(candidate: ClassicSegmentAssignmentCandidate) -> f64 {
4766 if candidate.rate == 0 {
4767 return f64::INFINITY;
4768 }
4769 candidate.distortion_delta / candidate.rate as f64
4770}
4771
4772fn enforce_classic_assignment_monotonicity(
4773 candidates: &[ClassicSegmentAssignmentCandidate],
4774 assignments: &mut [usize],
4775) {
4776 let mut order: Vec<_> = (0..candidates.len()).collect();
4777 order.sort_by_key(|&idx| (candidates[idx].block_index, candidates[idx].segment_index));
4778 let mut current_block = None;
4779 let mut min_layer = 0usize;
4780 for idx in order {
4781 if current_block != Some(candidates[idx].block_index) {
4782 current_block = Some(candidates[idx].block_index);
4783 min_layer = 0;
4784 }
4785 if assignments[idx] < min_layer {
4786 assignments[idx] = min_layer;
4787 }
4788 min_layer = assignments[idx];
4789 }
4790}
4791
4792fn enforce_classic_segment_layer_monotonicity(layered_packets: &mut [LayeredPreparedPacket]) {
4793 for packet in layered_packets {
4794 for subband in &mut packet.subbands {
4795 for block in &mut subband.blocks {
4796 if let LayeredPreparedBlock::Classic { segment_layers, .. } = block {
4797 let mut min_layer = 0usize;
4798 for layer in segment_layers {
4799 if *layer < min_layer {
4800 *layer = min_layer;
4801 }
4802 min_layer = *layer;
4803 }
4804 }
4805 }
4806 }
4807 }
4808}
4809
4810fn assign_ht_segment_layers_by_budget(
4811 candidates: &[HtSegmentAssignmentCandidate],
4812 layer_count: usize,
4813 cumulative_targets: &[u64],
4814) -> Result<Vec<usize>, &'static str> {
4815 let mut allocator = ClassicLayerBudgetAllocator::new(cumulative_targets, layer_count)?;
4816 let mut assignments = vec![layer_count.saturating_sub(1); candidates.len()];
4817 let mut candidate_order: Vec<_> = (0..candidates.len()).collect();
4818 candidate_order.sort_by_key(|&idx| candidates[idx].block_index);
4819
4820 for candidate_idx in candidate_order {
4821 let candidate = candidates
4822 .get(candidate_idx)
4823 .ok_or("HTJ2K segment candidate index mismatch")?;
4824 assignments[candidate_idx] = allocator.assign_segment(0, candidate.rate)?;
4825 }
4826
4827 Ok(assignments)
4828}
4829
4830fn classic_unbudgeted_segment_layers(
4831 encoded: &bitplane_encode::EncodedCodeBlockWithSegments,
4832 num_layers: u8,
4833) -> Result<Vec<usize>, &'static str> {
4834 let mut segment_layers = Vec::with_capacity(encoded.segments.len());
4835 for segment in &encoded.segments {
4836 let mut assigned = None;
4837 for layer_idx in 0..usize::from(num_layers) {
4838 let previous_pass =
4839 previous_layer_pass_count(encoded.num_coding_passes, layer_idx, num_layers)?;
4840 let cumulative_passes = if layer_idx + 1 == usize::from(num_layers) {
4841 encoded.num_coding_passes
4842 } else {
4843 layer_pass_count(encoded.num_coding_passes, layer_idx + 1, num_layers)?
4844 };
4845 if segment.start_coding_pass >= previous_pass
4846 && segment.end_coding_pass <= cumulative_passes
4847 {
4848 assigned = Some(layer_idx);
4849 break;
4850 }
4851 }
4852 segment_layers.push(
4853 assigned.ok_or("classic quality layer split must align to terminated coding passes")?,
4854 );
4855 }
4856 Ok(segment_layers)
4857}
4858
4859fn classic_layer_contributions(
4860 encoded: bitplane_encode::EncodedCodeBlockWithSegments,
4861 num_layers: u8,
4862 segment_layers: &[usize],
4863) -> Result<Vec<CodeBlockPacketData>, &'static str> {
4864 let layer_count = usize::from(num_layers);
4865 if segment_layers.len() != encoded.segments.len() {
4866 return Err("classic PCRD segment assignment count mismatch");
4867 }
4868 if segment_layers.iter().any(|&layer| layer >= layer_count) {
4869 return Err("classic PCRD segment layer exceeds layer count");
4870 }
4871 let mut contributions = Vec::with_capacity(layer_count);
4872
4873 for layer_idx in 0..layer_count {
4874 let mut data = Vec::new();
4875 let mut classic_segment_lengths = Vec::new();
4876 let mut contribution_passes = 0u8;
4877
4878 for (segment_idx, segment) in encoded.segments.iter().enumerate() {
4879 if segment_layers[segment_idx] != layer_idx {
4880 continue;
4881 }
4882 let start = usize::try_from(segment.data_offset)
4883 .map_err(|_| "classic code-block segment offset overflow")?;
4884 let len = usize::try_from(segment.data_length)
4885 .map_err(|_| "classic code-block segment length overflow")?;
4886 let end = start
4887 .checked_add(len)
4888 .ok_or("classic code-block segment range overflow")?;
4889 data.extend_from_slice(
4890 encoded
4891 .data
4892 .get(start..end)
4893 .ok_or("classic code-block segment range invalid")?,
4894 );
4895 classic_segment_lengths.push(segment.data_length);
4896 contribution_passes = contribution_passes
4897 .checked_add(segment.end_coding_pass - segment.start_coding_pass)
4898 .ok_or("classic code-block contribution pass count overflow")?;
4899 }
4900
4901 contributions.push(CodeBlockPacketData {
4902 data,
4903 ht_cleanup_length: 0,
4904 ht_refinement_length: 0,
4905 num_coding_passes: contribution_passes,
4906 classic_segment_lengths,
4907 num_zero_bitplanes: encoded.num_zero_bitplanes,
4908 previously_included: false,
4909 l_block: 3,
4910 block_coding_mode: BlockCodingMode::Classic,
4911 });
4912 }
4913
4914 Ok(contributions)
4915}
4916
4917fn layer_pass_count(
4918 num_coding_passes: u8,
4919 layer_count: usize,
4920 num_layers: u8,
4921) -> Result<u8, &'static str> {
4922 let numerator = u32::from(num_coding_passes)
4923 .checked_mul(u32::try_from(layer_count).map_err(|_| "layer index overflow")?)
4924 .ok_or("quality layer pass allocation overflow")?;
4925 numerator
4926 .div_ceil(u32::from(num_layers))
4927 .try_into()
4928 .map_err(|_| "quality layer pass allocation overflow")
4929}
4930
4931fn previous_layer_pass_count(
4932 num_coding_passes: u8,
4933 layer_idx: usize,
4934 num_layers: u8,
4935) -> Result<u8, &'static str> {
4936 if layer_idx == 0 {
4937 Ok(0)
4938 } else {
4939 layer_pass_count(num_coding_passes, layer_idx, num_layers)
4940 }
4941}
4942
4943fn ht_target_layer(
4944 block_idx: usize,
4945 block_count: usize,
4946 layer_count: usize,
4947) -> Result<usize, &'static str> {
4948 if block_count == 0 || layer_count == 0 {
4949 return Err("HTJ2K layer allocation requires non-empty inputs");
4950 }
4951 Ok(block_idx
4952 .checked_mul(layer_count)
4953 .ok_or("HTJ2K layer allocation overflow")?
4954 / block_count)
4955}
4956
4957fn ht_layer_contributions(
4958 encoded: bitplane_encode::EncodedCodeBlock,
4959 num_layers: u8,
4960 target_layer: usize,
4961) -> Result<Vec<CodeBlockPacketData>, &'static str> {
4962 let layer_count = usize::from(num_layers);
4963 if target_layer >= layer_count {
4964 return Err("HTJ2K target layer out of range");
4965 }
4966
4967 let mut data = Some(encoded.data);
4968 let mut contributions = Vec::with_capacity(layer_count);
4969 for layer_idx in 0..layer_count {
4970 let include = layer_idx == target_layer && encoded.num_coding_passes > 0;
4971 let layer_data = if include {
4972 data.take()
4973 .ok_or("HTJ2K layer contribution data was already consumed")?
4974 } else {
4975 Vec::new()
4976 };
4977 contributions.push(CodeBlockPacketData {
4978 data: layer_data,
4979 ht_cleanup_length: if include {
4980 encoded.ht_cleanup_length
4981 } else {
4982 0
4983 },
4984 ht_refinement_length: if include {
4985 encoded.ht_refinement_length
4986 } else {
4987 0
4988 },
4989 num_coding_passes: if include {
4990 encoded.num_coding_passes
4991 } else {
4992 0
4993 },
4994 classic_segment_lengths: Vec::new(),
4995 num_zero_bitplanes: encoded.num_zero_bitplanes,
4996 previously_included: false,
4997 l_block: 3,
4998 block_coding_mode: BlockCodingMode::HighThroughput,
4999 });
5000 }
5001
5002 Ok(contributions)
5003}
5004
5005fn encode_prepared_subbands(
5006 prepared_subbands: Vec<PreparedEncodeSubband>,
5007 accelerator: &mut impl J2kEncodeStageAccelerator,
5008) -> Result<Vec<SubbandPrecinct>, &'static str> {
5009 let block_coding_mode = prepared_subbands
5010 .iter()
5011 .find(|subband| !subband.code_blocks.is_empty())
5012 .map(|subband| subband.block_coding_mode);
5013 let encoded_blocks = match block_coding_mode {
5014 Some(BlockCodingMode::HighThroughput) => {
5015 encode_all_ht_code_blocks(&prepared_subbands, accelerator)?
5016 }
5017 Some(BlockCodingMode::Classic) => {
5018 encode_all_tier1_code_blocks(&prepared_subbands, accelerator)?
5019 }
5020 None => Vec::new(),
5021 };
5022
5023 let mut encoded_iter = encoded_blocks.into_iter();
5024 let mut precincts = Vec::with_capacity(prepared_subbands.len());
5025 for subband in prepared_subbands {
5026 let mut code_blocks = Vec::with_capacity(subband.code_blocks.len());
5027 for _ in 0..subband.code_blocks.len() {
5028 let encoded = encoded_iter
5029 .next()
5030 .ok_or("encoded code-block count mismatch")?;
5031 code_blocks.push(CodeBlockPacketData {
5032 data: encoded.data,
5033 ht_cleanup_length: if subband.block_coding_mode == BlockCodingMode::HighThroughput {
5034 encoded.ht_cleanup_length
5035 } else {
5036 0
5037 },
5038 ht_refinement_length: if subband.block_coding_mode
5039 == BlockCodingMode::HighThroughput
5040 {
5041 encoded.ht_refinement_length
5042 } else {
5043 0
5044 },
5045 num_coding_passes: encoded.num_coding_passes,
5046 classic_segment_lengths: Vec::new(),
5047 num_zero_bitplanes: encoded.num_zero_bitplanes,
5048 previously_included: false,
5049 l_block: 3,
5050 block_coding_mode: subband.block_coding_mode,
5051 });
5052 }
5053 precincts.push(SubbandPrecinct {
5054 code_blocks,
5055 num_cbs_x: subband.num_cbs_x,
5056 num_cbs_y: subband.num_cbs_y,
5057 });
5058 }
5059 if encoded_iter.next().is_some() {
5060 return Err("encoded code-block count mismatch");
5061 }
5062
5063 Ok(precincts)
5064}
5065
5066fn encode_all_ht_code_blocks(
5067 prepared_subbands: &[PreparedEncodeSubband],
5068 accelerator: &mut impl J2kEncodeStageAccelerator,
5069) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5070 if prepared_subbands.iter().all(|subband| {
5071 subband.code_blocks.is_empty() || subband.preencoded_ht_code_blocks.is_some()
5072 }) {
5073 let total_blocks = prepared_subbands
5074 .iter()
5075 .map(|subband| subband.code_blocks.len())
5076 .sum();
5077 let mut encoded = Vec::with_capacity(total_blocks);
5078 for subband in prepared_subbands {
5079 if let Some(blocks) = &subband.preencoded_ht_code_blocks {
5080 if blocks.len() != subband.code_blocks.len() {
5081 return Err("preencoded HT subband code-block count mismatch");
5082 }
5083 encoded.extend(
5084 blocks
5085 .iter()
5086 .cloned()
5087 .map(ht_encoded_code_block_from_accelerator),
5088 );
5089 }
5090 }
5091 return Ok(encoded);
5092 }
5093 if prepared_subbands
5094 .iter()
5095 .any(|subband| subband.preencoded_ht_code_blocks.is_some())
5096 {
5097 return Err("mixed preencoded and quantized HT subbands are unsupported");
5098 }
5099
5100 let jobs: Vec<_> = prepared_subbands
5101 .iter()
5102 .flat_map(|subband| {
5103 subband
5104 .code_blocks
5105 .iter()
5106 .map(move |block| crate::J2kHtCodeBlockEncodeJob {
5107 coefficients: &block.coefficients,
5108 width: block.width,
5109 height: block.height,
5110 total_bitplanes: subband.total_bitplanes,
5111 target_coding_passes: 1,
5112 })
5113 })
5114 .collect();
5115
5116 if let Some(encoded) = accelerator.encode_ht_code_blocks(&jobs)? {
5117 if encoded.len() != jobs.len() {
5118 return Err("accelerated HT code-block batch length mismatch");
5119 }
5120 return Ok(encoded
5121 .into_iter()
5122 .map(ht_encoded_code_block_from_accelerator)
5123 .collect());
5124 }
5125
5126 if accelerator.prefer_parallel_cpu_code_block_fallback() {
5127 if jobs.len() < HT_CPU_PARALLEL_FALLBACK_MIN_JOBS {
5128 return encode_all_ht_code_blocks_serial_cpu(&jobs);
5129 }
5130 return encode_all_ht_code_blocks_parallel(&jobs);
5131 }
5132
5133 jobs.iter()
5134 .map(|job| {
5135 encode_ht_code_block(
5136 job.coefficients,
5137 job.width,
5138 job.height,
5139 job.total_bitplanes,
5140 accelerator,
5141 )
5142 })
5143 .collect()
5144}
5145
5146fn encode_all_tier1_code_blocks(
5147 prepared_subbands: &[PreparedEncodeSubband],
5148 accelerator: &mut impl J2kEncodeStageAccelerator,
5149) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5150 let style = default_public_code_block_style();
5151 let jobs: Vec<_> = prepared_subbands
5152 .iter()
5153 .flat_map(|subband| {
5154 let public_sub_band_type = public_sub_band_type(subband.sub_band_type);
5155 subband
5156 .code_blocks
5157 .iter()
5158 .map(move |block| J2kTier1CodeBlockEncodeJob {
5159 coefficients: &block.coefficients,
5160 width: block.width,
5161 height: block.height,
5162 sub_band_type: public_sub_band_type,
5163 total_bitplanes: subband.total_bitplanes,
5164 style,
5165 })
5166 })
5167 .collect();
5168
5169 if let Some(encoded) = accelerator.encode_tier1_code_blocks(&jobs)? {
5170 if encoded.len() != jobs.len() {
5171 return Err("accelerated classic code-block batch length mismatch");
5172 }
5173 return Ok(encoded
5174 .into_iter()
5175 .map(encoded_code_block_from_accelerator)
5176 .collect());
5177 }
5178
5179 if accelerator.prefer_parallel_cpu_code_block_fallback() {
5180 return encode_all_tier1_code_blocks_parallel(&jobs);
5181 }
5182
5183 let mut encoded = Vec::with_capacity(jobs.len());
5184 for subband in prepared_subbands {
5185 for block in &subband.code_blocks {
5186 encoded.push(encode_tier1_code_block(
5187 &block.coefficients,
5188 block.width,
5189 block.height,
5190 subband.sub_band_type,
5191 subband.total_bitplanes,
5192 accelerator,
5193 )?);
5194 }
5195 }
5196 Ok(encoded)
5197}
5198
5199fn encode_all_ht_code_blocks_serial_cpu(
5200 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
5201) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5202 if jobs.iter().any(|job| job.target_coding_passes != 1) {
5203 return Err("CPU HTJ2K code-block fallback supports cleanup-only encode");
5204 }
5205 jobs.iter()
5206 .map(|job| {
5207 ht_block_encode::encode_code_block(
5208 job.coefficients,
5209 job.width,
5210 job.height,
5211 job.total_bitplanes,
5212 )
5213 })
5214 .collect()
5215}
5216
5217#[cfg(feature = "parallel")]
5218fn encode_all_ht_code_blocks_parallel(
5219 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
5220) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5221 if jobs.iter().any(|job| job.target_coding_passes != 1) {
5222 return Err("CPU HTJ2K code-block fallback supports cleanup-only encode");
5223 }
5224 jobs.par_iter()
5225 .map(|job| {
5226 ht_block_encode::encode_code_block(
5227 job.coefficients,
5228 job.width,
5229 job.height,
5230 job.total_bitplanes,
5231 )
5232 })
5233 .collect()
5234}
5235
5236#[cfg(not(feature = "parallel"))]
5237fn encode_all_ht_code_blocks_parallel(
5238 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
5239) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5240 if jobs.iter().any(|job| job.target_coding_passes != 1) {
5241 return Err("CPU HTJ2K code-block fallback supports cleanup-only encode");
5242 }
5243 jobs.iter()
5244 .map(|job| {
5245 ht_block_encode::encode_code_block(
5246 job.coefficients,
5247 job.width,
5248 job.height,
5249 job.total_bitplanes,
5250 )
5251 })
5252 .collect()
5253}
5254
5255#[cfg(feature = "parallel")]
5256fn encode_all_tier1_code_blocks_parallel(
5257 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
5258) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5259 jobs.par_iter()
5260 .map(|job| {
5261 Ok(bitplane_encode::encode_code_block(
5262 job.coefficients,
5263 job.width,
5264 job.height,
5265 internal_sub_band_type(job.sub_band_type),
5266 job.total_bitplanes,
5267 ))
5268 })
5269 .collect()
5270}
5271
5272#[cfg(not(feature = "parallel"))]
5273fn encode_all_tier1_code_blocks_parallel(
5274 jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
5275) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
5276 jobs.iter()
5277 .map(|job| {
5278 Ok(bitplane_encode::encode_code_block(
5279 job.coefficients,
5280 job.width,
5281 job.height,
5282 internal_sub_band_type(job.sub_band_type),
5283 job.total_bitplanes,
5284 ))
5285 })
5286 .collect()
5287}
5288
5289fn encode_ht_code_block(
5290 coefficients: &[i32],
5291 width: u32,
5292 height: u32,
5293 total_bitplanes: u8,
5294 accelerator: &mut impl J2kEncodeStageAccelerator,
5295) -> Result<bitplane_encode::EncodedCodeBlock, &'static str> {
5296 if let Some(encoded) = accelerator.encode_ht_code_block(crate::J2kHtCodeBlockEncodeJob {
5297 coefficients,
5298 width,
5299 height,
5300 total_bitplanes,
5301 target_coding_passes: 1,
5302 })? {
5303 return Ok(ht_encoded_code_block_from_accelerator(encoded));
5304 }
5305
5306 ht_block_encode::encode_code_block(coefficients, width, height, total_bitplanes)
5307}
5308
5309fn ht_encoded_code_block_from_accelerator(
5310 encoded: crate::EncodedHtJ2kCodeBlock,
5311) -> bitplane_encode::EncodedCodeBlock {
5312 bitplane_encode::EncodedCodeBlock {
5313 data: encoded.data,
5314 num_coding_passes: encoded.num_coding_passes,
5315 num_zero_bitplanes: encoded.num_zero_bitplanes,
5316 ht_cleanup_length: encoded.cleanup_length,
5317 ht_refinement_length: encoded.refinement_length,
5318 }
5319}
5320
5321fn encode_tier1_code_block(
5322 coefficients: &[i32],
5323 width: u32,
5324 height: u32,
5325 sub_band_type: SubBandType,
5326 total_bitplanes: u8,
5327 accelerator: &mut impl J2kEncodeStageAccelerator,
5328) -> Result<bitplane_encode::EncodedCodeBlock, &'static str> {
5329 if let Some(encoded) = accelerator.encode_tier1_code_block(J2kTier1CodeBlockEncodeJob {
5330 coefficients,
5331 width,
5332 height,
5333 sub_band_type: public_sub_band_type(sub_band_type),
5334 total_bitplanes,
5335 style: default_public_code_block_style(),
5336 })? {
5337 return Ok(encoded_code_block_from_accelerator(encoded));
5338 }
5339
5340 Ok(bitplane_encode::encode_code_block(
5341 coefficients,
5342 width,
5343 height,
5344 sub_band_type,
5345 total_bitplanes,
5346 ))
5347}
5348
5349fn encoded_code_block_from_accelerator(
5350 encoded: EncodedJ2kCodeBlock,
5351) -> bitplane_encode::EncodedCodeBlock {
5352 bitplane_encode::EncodedCodeBlock {
5353 data: encoded.data,
5354 num_coding_passes: encoded.number_of_coding_passes,
5355 num_zero_bitplanes: encoded.missing_bit_planes,
5356 ht_cleanup_length: 0,
5357 ht_refinement_length: 0,
5358 }
5359}
5360
5361fn public_sub_band_type(sub_band_type: SubBandType) -> J2kSubBandType {
5362 match sub_band_type {
5363 SubBandType::LowLow => J2kSubBandType::LowLow,
5364 SubBandType::HighLow => J2kSubBandType::HighLow,
5365 SubBandType::LowHigh => J2kSubBandType::LowHigh,
5366 SubBandType::HighHigh => J2kSubBandType::HighHigh,
5367 }
5368}
5369
5370fn internal_sub_band_type(sub_band_type: J2kSubBandType) -> SubBandType {
5371 match sub_band_type {
5372 J2kSubBandType::LowLow => SubBandType::LowLow,
5373 J2kSubBandType::HighLow => SubBandType::HighLow,
5374 J2kSubBandType::LowHigh => SubBandType::LowHigh,
5375 J2kSubBandType::HighHigh => SubBandType::HighHigh,
5376 }
5377}
5378
5379fn default_public_code_block_style() -> crate::J2kCodeBlockStyle {
5380 crate::J2kCodeBlockStyle {
5381 selective_arithmetic_coding_bypass: false,
5382 reset_context_probabilities: false,
5383 termination_on_each_pass: false,
5384 vertically_causal_context: false,
5385 segmentation_symbols: false,
5386 }
5387}
5388
5389pub(crate) fn deinterleave_to_f32(
5391 pixels: &[u8],
5392 num_pixels: usize,
5393 num_components: u8,
5394 bit_depth: u8,
5395 signed: bool,
5396) -> Vec<Vec<f32>> {
5397 if num_components == 3 && bit_depth == 8 && !signed {
5398 return deinterleave_rgb8_unsigned_to_f32(pixels, num_pixels);
5399 }
5400
5401 let nc = num_components as usize;
5402 let mut components = vec![vec![0.0f32; num_pixels]; nc];
5403 let unsigned_offset = if signed {
5404 0.0
5405 } else {
5406 (1u32 << (bit_depth as u32 - 1)) as f32
5407 };
5408
5409 if bit_depth <= 8 {
5410 for (i, pixel) in pixels.chunks_exact(nc).take(num_pixels).enumerate() {
5411 for (c, component) in components.iter_mut().enumerate().take(nc) {
5412 let val = pixel[c];
5413 component[i] = if signed {
5414 (val as i8) as f32
5415 } else {
5416 val as f32 - unsigned_offset
5417 };
5418 }
5419 }
5420 } else {
5421 for (i, pixel) in pixels.chunks_exact(nc * 2).take(num_pixels).enumerate() {
5423 for (c, component) in components.iter_mut().enumerate().take(nc) {
5424 let offset = c * 2;
5425 let val = u16::from_le_bytes([pixel[offset], pixel[offset + 1]]);
5426 component[i] = if signed {
5427 (val as i16) as f32
5428 } else {
5429 val as f32 - unsigned_offset
5430 };
5431 }
5432 }
5433 }
5434
5435 components
5436}
5437
5438fn deinterleave_rgb8_unsigned_to_f32(pixels: &[u8], num_pixels: usize) -> Vec<Vec<f32>> {
5439 let mut r = Vec::with_capacity(num_pixels);
5440 let mut g = Vec::with_capacity(num_pixels);
5441 let mut b = Vec::with_capacity(num_pixels);
5442
5443 for pixel in pixels.chunks_exact(3).take(num_pixels) {
5444 r.push(f32::from(pixel[0]) - 128.0);
5445 g.push(f32::from(pixel[1]) - 128.0);
5446 b.push(f32::from(pixel[2]) - 128.0);
5447 }
5448
5449 vec![r, g, b]
5450}
5451
5452fn max_decomposition_levels(width: u32, height: u32) -> u8 {
5454 let min_dim = width.min(height);
5455 if min_dim <= 1 {
5456 return 0;
5457 }
5458 floor_f32(log2_f32(min_dim as f32)) as u8
5459}
5460
5461#[cfg(test)]
5462mod tests {
5463 use super::*;
5464 use crate::PrequantizedHtj2k97CodeBlock;
5465
5466 fn test_preencoded_subband_payload(marker: u8) -> PreencodedHtj2k97Subband {
5467 PreencodedHtj2k97Subband {
5468 sub_band_type: J2kSubBandType::LowLow,
5469 num_cbs_x: 1,
5470 num_cbs_y: 1,
5471 total_bitplanes: 8,
5472 code_blocks: vec![PreencodedHtj2k97CodeBlock {
5473 width: 1,
5474 height: 1,
5475 encoded: crate::EncodedHtJ2kCodeBlock {
5476 data: vec![marker; 8],
5477 cleanup_length: 8,
5478 refinement_length: 0,
5479 num_coding_passes: 1,
5480 num_zero_bitplanes: 0,
5481 },
5482 }],
5483 }
5484 }
5485
5486 #[test]
5487 fn prepared_subband_from_owned_preencoded_moves_payload_without_clone() {
5488 let subband = test_preencoded_subband_payload(7);
5489 let original_ptr = subband.code_blocks[0].encoded.data.as_ptr() as usize;
5490
5491 let prepared =
5492 prepared_subband_from_preencoded_owned(subband).expect("owned preencoded subband");
5493 let prepared_blocks = prepared
5494 .preencoded_ht_code_blocks
5495 .expect("preencoded payloads");
5496
5497 assert_eq!(prepared_blocks[0].data.as_ptr() as usize, original_ptr);
5498 assert!(prepared.code_blocks[0].coefficients.is_empty());
5499 }
5500
5501 #[test]
5502 fn compact_preencoded_packetization_borrows_payload_ranges() {
5503 #[derive(Default)]
5504 struct RecordingPacketizationAccelerator {
5505 payload_base: usize,
5506 observed_offsets: Vec<usize>,
5507 observed_lengths: Vec<usize>,
5508 }
5509
5510 impl crate::J2kEncodeStageAccelerator for RecordingPacketizationAccelerator {
5511 fn encode_packetization(
5512 &mut self,
5513 job: crate::J2kPacketizationEncodeJob<'_>,
5514 ) -> core::result::Result<Option<Vec<u8>>, &'static str> {
5515 for code_block in job
5516 .resolutions
5517 .iter()
5518 .flat_map(|resolution| resolution.subbands.iter())
5519 .flat_map(|subband| subband.code_blocks.iter())
5520 .filter(|code_block| !code_block.data.is_empty())
5521 {
5522 self.observed_offsets
5523 .push((code_block.data.as_ptr() as usize) - self.payload_base);
5524 self.observed_lengths.push(code_block.data.len());
5525 }
5526 Ok(Some(crate::encode_j2k_packetization_scalar(job)?))
5527 }
5528 }
5529
5530 let (preencoded, options) = sample_preencoded_htj2k97_for_test();
5531 let expected = encode_preencoded_htj2k_97(&preencoded, &options).expect("owned preencoded");
5532 let mut payload = Vec::new();
5533 let mut expected_offsets = Vec::new();
5534 let mut expected_lengths = Vec::new();
5535 let components = preencoded
5536 .components
5537 .iter()
5538 .map(|component| PreencodedHtj2k97CompactComponent {
5539 x_rsiz: component.x_rsiz,
5540 y_rsiz: component.y_rsiz,
5541 resolutions: component
5542 .resolutions
5543 .iter()
5544 .map(|resolution| PreencodedHtj2k97CompactResolution {
5545 subbands: resolution
5546 .subbands
5547 .iter()
5548 .map(|subband| PreencodedHtj2k97CompactSubband {
5549 sub_band_type: subband.sub_band_type,
5550 num_cbs_x: subband.num_cbs_x,
5551 num_cbs_y: subband.num_cbs_y,
5552 total_bitplanes: subband.total_bitplanes,
5553 code_blocks: subband
5554 .code_blocks
5555 .iter()
5556 .map(|block| {
5557 let start = payload.len();
5558 payload.extend_from_slice(&block.encoded.data);
5559 let end = payload.len();
5560 if start != end {
5561 expected_offsets.push(start);
5562 expected_lengths.push(end - start);
5563 }
5564 PreencodedHtj2k97CompactCodeBlock {
5565 width: block.width,
5566 height: block.height,
5567 payload_range: start..end,
5568 cleanup_length: block.encoded.cleanup_length,
5569 refinement_length: block.encoded.refinement_length,
5570 num_coding_passes: block.encoded.num_coding_passes,
5571 num_zero_bitplanes: block.encoded.num_zero_bitplanes,
5572 }
5573 })
5574 .collect(),
5575 })
5576 .collect(),
5577 })
5578 .collect(),
5579 })
5580 .collect();
5581 let compact = PreencodedHtj2k97CompactImage {
5582 width: preencoded.width,
5583 height: preencoded.height,
5584 bit_depth: preencoded.bit_depth,
5585 signed: preencoded.signed,
5586 payload,
5587 components,
5588 };
5589 let mut accelerator = RecordingPacketizationAccelerator {
5590 payload_base: compact.payload.as_ptr() as usize,
5591 ..Default::default()
5592 };
5593
5594 let actual = encode_preencoded_htj2k_97_compact_owned_with_accelerator(
5595 compact,
5596 &options,
5597 &mut accelerator,
5598 )
5599 .expect("compact preencoded");
5600
5601 assert_eq!(actual, expected);
5602 assert_eq!(accelerator.observed_offsets, expected_offsets);
5603 assert_eq!(accelerator.observed_lengths, expected_lengths);
5604 }
5605
5606 #[test]
5607 fn test_encode_8bit_gray() {
5608 let width = 8u32;
5609 let height = 8u32;
5610 let pixels: Vec<u8> = (0..64).collect();
5611
5612 let result = encode(
5613 &pixels,
5614 width,
5615 height,
5616 1,
5617 8,
5618 false,
5619 &EncodeOptions {
5620 num_decomposition_levels: 2,
5621 ..Default::default()
5622 },
5623 );
5624
5625 assert!(result.is_ok());
5626 let codestream = result.unwrap();
5627 assert_eq!(codestream[0], 0xFF);
5629 assert_eq!(codestream[1], 0x4F);
5630 let len = codestream.len();
5632 assert_eq!(codestream[len - 2], 0xFF);
5633 assert_eq!(codestream[len - 1], 0xD9);
5634 }
5635
5636 #[test]
5637 fn test_encode_16bit_gray() {
5638 let width = 8u32;
5639 let height = 8u32;
5640 let mut pixels = Vec::with_capacity(128);
5641 for i in 0..64u16 {
5642 let val = i * 100;
5643 pixels.extend_from_slice(&val.to_le_bytes());
5644 }
5645
5646 let result = encode(
5647 &pixels,
5648 width,
5649 height,
5650 1,
5651 16,
5652 false,
5653 &EncodeOptions {
5654 num_decomposition_levels: 2,
5655 ..Default::default()
5656 },
5657 );
5658
5659 assert!(result.is_ok());
5660 }
5661
5662 #[test]
5663 fn test_encode_rgb() {
5664 let width = 16u32;
5665 let height = 16u32;
5666 let pixels: Vec<u8> = (0..width * height * 3).map(|i| (i & 0xFF) as u8).collect();
5667
5668 let result = encode(
5669 &pixels,
5670 width,
5671 height,
5672 3,
5673 8,
5674 false,
5675 &EncodeOptions {
5676 num_decomposition_levels: 3,
5677 ..Default::default()
5678 },
5679 );
5680
5681 assert!(result.is_ok(), "RGB encode failed: {:?}", result.err());
5682 }
5683
5684 #[test]
5685 fn encode_with_accelerator_calls_lossless_stage_hooks() {
5686 #[derive(Default)]
5687 struct CountingAccelerator {
5688 forward_rct: usize,
5689 forward_dwt53: usize,
5690 tier1_code_blocks: usize,
5691 tier1_code_block_batches: usize,
5692 tier1_batched_jobs: usize,
5693 packetization: usize,
5694 packetization_resolution_count: u32,
5695 packetization_code_block_count: u32,
5696 packetization_saw_payload: bool,
5697 }
5698
5699 impl crate::J2kEncodeStageAccelerator for CountingAccelerator {
5700 fn encode_forward_rct(
5701 &mut self,
5702 _job: crate::J2kForwardRctJob<'_>,
5703 ) -> core::result::Result<bool, &'static str> {
5704 self.forward_rct += 1;
5705 Ok(false)
5706 }
5707
5708 fn encode_forward_dwt53(
5709 &mut self,
5710 _job: crate::J2kForwardDwt53Job<'_>,
5711 ) -> core::result::Result<Option<crate::J2kForwardDwt53Output>, &'static str>
5712 {
5713 self.forward_dwt53 += 1;
5714 Ok(None)
5715 }
5716
5717 fn encode_tier1_code_block(
5718 &mut self,
5719 _job: crate::J2kTier1CodeBlockEncodeJob<'_>,
5720 ) -> core::result::Result<Option<crate::EncodedJ2kCodeBlock>, &'static str>
5721 {
5722 self.tier1_code_blocks += 1;
5723 Ok(None)
5724 }
5725
5726 fn encode_tier1_code_blocks(
5727 &mut self,
5728 jobs: &[crate::J2kTier1CodeBlockEncodeJob<'_>],
5729 ) -> core::result::Result<Option<Vec<crate::EncodedJ2kCodeBlock>>, &'static str>
5730 {
5731 self.tier1_code_block_batches += 1;
5732 self.tier1_batched_jobs += jobs.len();
5733 Ok(None)
5734 }
5735
5736 fn encode_packetization(
5737 &mut self,
5738 job: crate::J2kPacketizationEncodeJob<'_>,
5739 ) -> core::result::Result<Option<Vec<u8>>, &'static str> {
5740 self.packetization += 1;
5741 self.packetization_resolution_count = job.resolution_count;
5742 self.packetization_code_block_count = job.code_block_count;
5743 self.packetization_saw_payload = job
5744 .resolutions
5745 .iter()
5746 .flat_map(|resolution| resolution.subbands.iter())
5747 .flat_map(|subband| subband.code_blocks.iter())
5748 .any(|code_block| !code_block.data.is_empty());
5749 Ok(None)
5750 }
5751 }
5752
5753 let pixels: Vec<u8> = (0..8 * 8 * 3).map(|i| (i & 0xFF) as u8).collect();
5754 let options = EncodeOptions {
5755 num_decomposition_levels: 1,
5756 reversible: true,
5757 ..EncodeOptions::default()
5758 };
5759 let mut accelerator = CountingAccelerator::default();
5760
5761 let codestream =
5762 encode_with_accelerator(&pixels, 8, 8, 3, 8, false, &options, &mut accelerator)
5763 .expect("encode with accelerator hooks");
5764
5765 assert!(codestream.starts_with(&[0xFF, 0x4F]));
5766 assert_eq!(accelerator.forward_rct, 1);
5767 assert_eq!(accelerator.forward_dwt53, 3);
5768 assert!(accelerator.tier1_code_block_batches > 0);
5769 assert_eq!(
5770 accelerator.tier1_code_blocks,
5771 accelerator.tier1_batched_jobs
5772 );
5773 assert_eq!(accelerator.packetization, 1);
5774 assert_eq!(accelerator.packetization_resolution_count, 6);
5775 assert_eq!(
5776 accelerator.packetization_code_block_count,
5777 u32::try_from(accelerator.tier1_code_blocks).expect("test code-block count fits u32")
5778 );
5779 assert!(accelerator.packetization_saw_payload);
5780 }
5781
5782 #[test]
5783 fn cpu_only_accelerator_opts_into_parallel_block_fallback_only_for_native_cpu() {
5784 #[derive(Default)]
5785 struct ExternalAccelerator;
5786
5787 impl crate::J2kEncodeStageAccelerator for ExternalAccelerator {}
5788
5789 let cpu = crate::CpuOnlyJ2kEncodeStageAccelerator;
5790 let external = ExternalAccelerator;
5791
5792 assert!(cpu.prefer_parallel_cpu_code_block_fallback());
5793 assert!(!external.prefer_parallel_cpu_code_block_fallback());
5794 }
5795
5796 #[test]
5797 fn cpu_parallel_block_fallback_matches_serial_classic_and_htj2k_output() {
5798 #[derive(Default)]
5799 struct SerialCpuFallbackAccelerator;
5800
5801 impl crate::J2kEncodeStageAccelerator for SerialCpuFallbackAccelerator {}
5802
5803 let pixels = gradient_u8(96, 80);
5804 for use_ht_block_coding in [false, true] {
5805 let options = EncodeOptions {
5806 num_decomposition_levels: 1,
5807 code_block_width_exp: 2,
5808 code_block_height_exp: 2,
5809 use_ht_block_coding,
5810 ..EncodeOptions::default()
5811 };
5812 let parallel = encode(&pixels, 96, 80, 1, 8, false, &options)
5813 .expect("parallel CPU fallback encode");
5814 let mut serial_accelerator = SerialCpuFallbackAccelerator;
5815 let serial = encode_with_accelerator(
5816 &pixels,
5817 96,
5818 80,
5819 1,
5820 8,
5821 false,
5822 &options,
5823 &mut serial_accelerator,
5824 )
5825 .expect("serial CPU fallback encode");
5826
5827 assert_eq!(parallel, serial);
5828 }
5829 }
5830
5831 #[test]
5832 fn precomputed_htj2k53_offers_ht_code_blocks_to_encode_accelerator() {
5833 let image = sample_precomputed_htj2k53_image();
5834 let options = EncodeOptions {
5835 num_decomposition_levels: 1,
5836 reversible: true,
5837 guard_bits: 2,
5838 code_block_width_exp: 2,
5839 code_block_height_exp: 2,
5840 ..EncodeOptions::default()
5841 };
5842 let mut accelerator = CountingHtEncodeAccelerator::default();
5843
5844 let encoded =
5845 encode_precomputed_htj2k_53_with_accelerator(&image, &options, &mut accelerator)
5846 .expect("precomputed 5/3 encode accepts encode accelerator");
5847
5848 assert!(encoded.starts_with(&[0xff, 0x4f]));
5849 assert_eq!(accelerator.forward_dwt53, 0);
5850 assert_eq!(accelerator.forward_dwt97, 0);
5851 assert_eq!(accelerator.ht_batches, 1);
5852 assert!(accelerator.ht_jobs > 0);
5853 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
5854 }
5855
5856 #[test]
5857 fn precomputed_htj2k97_offers_ht_code_blocks_to_encode_accelerator() {
5858 let image = sample_precomputed_htj2k97_image();
5859 let options = EncodeOptions {
5860 num_decomposition_levels: 1,
5861 reversible: false,
5862 guard_bits: 2,
5863 code_block_width_exp: 2,
5864 code_block_height_exp: 2,
5865 ..EncodeOptions::default()
5866 };
5867 let mut accelerator = CountingHtEncodeAccelerator::default();
5868
5869 let encoded =
5870 encode_precomputed_htj2k_97_with_accelerator(&image, &options, &mut accelerator)
5871 .expect("precomputed 9/7 encode accepts encode accelerator");
5872
5873 assert!(encoded.starts_with(&[0xff, 0x4f]));
5874 assert_eq!(accelerator.forward_dwt53, 0);
5875 assert_eq!(accelerator.forward_dwt97, 0);
5876 assert_eq!(accelerator.ht_batches, 1);
5877 assert!(accelerator.ht_jobs > 0);
5878 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
5879 }
5880
5881 #[test]
5882 fn precomputed_dwt_geometry_validation_rejects_recursive_mismatch_for_both_filters() {
5883 let mut dwt53 = sample_precomputed_htj2k53_image();
5884 dwt53.components[0].dwt.levels[0].low_width += 1;
5885 assert_eq!(
5886 validate_precomputed_dwt_geometry(&dwt53),
5887 Err("precomputed DWT recursive geometry mismatch")
5888 );
5889
5890 let mut dwt97 = sample_precomputed_htj2k97_image();
5891 dwt97.components[0].dwt.levels[0].low_width += 1;
5892 assert_eq!(
5893 validate_precomputed_dwt97_geometry(&dwt97),
5894 Err("precomputed DWT recursive geometry mismatch")
5895 );
5896 }
5897
5898 #[test]
5899 fn prequantized_htj2k97_offers_ht_code_blocks_to_encode_accelerator() {
5900 let image = sample_precomputed_htj2k97_image();
5901 let options = EncodeOptions {
5902 num_decomposition_levels: 1,
5903 reversible: false,
5904 guard_bits: 2,
5905 code_block_width_exp: 2,
5906 code_block_height_exp: 2,
5907 ..EncodeOptions::default()
5908 };
5909 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
5910 .expect("test prequantized image");
5911 let mut accelerator = CountingHtEncodeAccelerator::default();
5912
5913 let encoded = encode_prequantized_htj2k_97_with_accelerator(
5914 &prequantized,
5915 &options,
5916 &mut accelerator,
5917 )
5918 .expect("prequantized 9/7 encode accepts encode accelerator");
5919
5920 assert!(encoded.starts_with(&[0xff, 0x4f]));
5921 assert_eq!(accelerator.forward_dwt53, 0);
5922 assert_eq!(accelerator.forward_dwt97, 0);
5923 assert_eq!(accelerator.ht_batches, 1);
5924 assert!(accelerator.ht_jobs > 0);
5925 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
5926 }
5927
5928 #[test]
5929 fn precomputed_htj2k97_batch_offers_all_ht_code_blocks_in_one_accelerator_call() {
5930 let image = sample_precomputed_htj2k97_image();
5931 let options = EncodeOptions {
5932 num_decomposition_levels: 1,
5933 reversible: false,
5934 guard_bits: 2,
5935 code_block_width_exp: 2,
5936 code_block_height_exp: 2,
5937 ..EncodeOptions::default()
5938 };
5939 let mut accelerator = CountingHtEncodeAccelerator::default();
5940
5941 let encoded = encode_precomputed_htj2k_97_batch_with_accelerator(
5942 &[image.clone(), image],
5943 &options,
5944 &mut accelerator,
5945 )
5946 .expect("batch precomputed 9/7 encode accepts encode accelerator");
5947
5948 assert_eq!(encoded.len(), 2);
5949 assert!(encoded
5950 .iter()
5951 .all(|codestream| codestream.starts_with(&[0xff, 0x4f])));
5952 assert_eq!(accelerator.forward_dwt53, 0);
5953 assert_eq!(accelerator.forward_dwt97, 0);
5954 assert_eq!(accelerator.ht_batches, 1);
5955 assert!(accelerator.ht_jobs > 0);
5956 assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
5957 }
5958
5959 #[derive(Default)]
5960 struct CountingHtEncodeAccelerator {
5961 forward_dwt53: usize,
5962 forward_dwt97: usize,
5963 ht_batches: usize,
5964 ht_jobs: usize,
5965 ht_single_blocks: usize,
5966 }
5967
5968 impl crate::J2kEncodeStageAccelerator for CountingHtEncodeAccelerator {
5969 fn encode_forward_dwt53(
5970 &mut self,
5971 _job: crate::J2kForwardDwt53Job<'_>,
5972 ) -> core::result::Result<Option<crate::J2kForwardDwt53Output>, &'static str> {
5973 self.forward_dwt53 += 1;
5974 Ok(None)
5975 }
5976
5977 fn encode_forward_dwt97(
5978 &mut self,
5979 _job: crate::J2kForwardDwt97Job<'_>,
5980 ) -> core::result::Result<Option<crate::J2kForwardDwt97Output>, &'static str> {
5981 self.forward_dwt97 += 1;
5982 Ok(None)
5983 }
5984
5985 fn encode_ht_code_blocks(
5986 &mut self,
5987 jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
5988 ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str> {
5989 self.ht_batches += 1;
5990 self.ht_jobs += jobs.len();
5991 Ok(None)
5992 }
5993
5994 fn encode_ht_code_block(
5995 &mut self,
5996 _job: crate::J2kHtCodeBlockEncodeJob<'_>,
5997 ) -> core::result::Result<Option<crate::EncodedHtJ2kCodeBlock>, &'static str> {
5998 self.ht_single_blocks += 1;
5999 Ok(None)
6000 }
6001 }
6002
6003 #[test]
6004 fn prepare_subband_uses_fused_ht_subband_without_host_quantized_codeblocks() {
6005 #[derive(Default)]
6006 struct FusedHtSubbandAccelerator {
6007 subband_calls: usize,
6008 quantize_calls: usize,
6009 ht_batch_calls: usize,
6010 }
6011
6012 impl crate::J2kEncodeStageAccelerator for FusedHtSubbandAccelerator {
6013 fn encode_ht_subband(
6014 &mut self,
6015 job: crate::J2kHtSubbandEncodeJob<'_>,
6016 ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str>
6017 {
6018 self.subband_calls += 1;
6019 let count = (job.width.div_ceil(job.code_block_width) as usize)
6020 .checked_mul(job.height.div_ceil(job.code_block_height) as usize)
6021 .ok_or("test code-block count overflow")?;
6022 Ok(Some(
6023 (0..count)
6024 .map(|idx| crate::EncodedHtJ2kCodeBlock {
6025 data: vec![u8::try_from(idx).expect("test block index fits"), 0],
6026 cleanup_length: 2,
6027 refinement_length: 0,
6028 num_coding_passes: 1,
6029 num_zero_bitplanes: 0,
6030 })
6031 .collect(),
6032 ))
6033 }
6034
6035 fn encode_quantize_subband(
6036 &mut self,
6037 _job: crate::J2kQuantizeSubbandJob<'_>,
6038 ) -> core::result::Result<Option<Vec<i32>>, &'static str> {
6039 self.quantize_calls += 1;
6040 Ok(None)
6041 }
6042
6043 fn encode_ht_code_blocks(
6044 &mut self,
6045 _jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
6046 ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str>
6047 {
6048 self.ht_batch_calls += 1;
6049 Ok(None)
6050 }
6051 }
6052
6053 let coefficients = vec![0.0; 16];
6054 let mut accelerator = FusedHtSubbandAccelerator::default();
6055 let prepared = prepare_subband(
6056 &coefficients,
6057 4,
6058 4,
6059 &QuantStepSize {
6060 exponent: 8,
6061 mantissa: 0,
6062 },
6063 8,
6064 2,
6065 true,
6066 BlockCodingMode::HighThroughput,
6067 2,
6068 2,
6069 SubBandType::LowLow,
6070 &mut accelerator,
6071 )
6072 .expect("fused HT subband prepare");
6073
6074 assert_eq!(accelerator.subband_calls, 1);
6075 assert_eq!(accelerator.quantize_calls, 0);
6076 assert!(prepared.preencoded_ht_code_blocks.is_some());
6077 assert!(prepared
6078 .code_blocks
6079 .iter()
6080 .all(|block| block.coefficients.is_empty()));
6081
6082 let precincts = encode_prepared_subbands(vec![prepared], &mut accelerator)
6083 .expect("preencoded HT subband packet data");
6084
6085 assert_eq!(accelerator.ht_batch_calls, 0);
6086 assert_eq!(precincts[0].code_blocks.len(), 4);
6087 assert_eq!(precincts[0].code_blocks[2].data, vec![2, 0]);
6088 }
6089
6090 #[test]
6091 fn ht_cpu_parallel_fallback_threshold_matches_parallel_output() {
6092 assert_eq!(HT_CPU_PARALLEL_FALLBACK_MIN_JOBS, 4);
6093
6094 let blocks: Vec<Vec<i32>> = (0..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS)
6095 .map(|seed| {
6096 (0usize..64 * 64)
6097 .map(|index| {
6098 let value = (((index * 31) ^ (seed * 17)) & 0x01ff) as i32 - 255;
6099 if (index + seed).is_multiple_of(11) {
6100 0
6101 } else {
6102 value
6103 }
6104 })
6105 .collect()
6106 })
6107 .collect();
6108 let jobs: Vec<_> = blocks
6109 .iter()
6110 .map(|coefficients| crate::J2kHtCodeBlockEncodeJob {
6111 coefficients,
6112 width: 64,
6113 height: 64,
6114 total_bitplanes: 10,
6115 target_coding_passes: 1,
6116 })
6117 .collect();
6118
6119 let serial =
6120 encode_all_ht_code_blocks_serial_cpu(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS - 1])
6121 .expect("serial tiny HT encode");
6122 let parallel =
6123 encode_all_ht_code_blocks_parallel(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS])
6124 .expect("parallel HT encode");
6125 let serial_threshold =
6126 encode_all_ht_code_blocks_serial_cpu(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS])
6127 .expect("serial threshold HT encode");
6128
6129 assert_eq!(serial.len(), HT_CPU_PARALLEL_FALLBACK_MIN_JOBS - 1);
6130 assert_eq!(parallel.len(), HT_CPU_PARALLEL_FALLBACK_MIN_JOBS);
6131 assert_eq!(serial_threshold.len(), parallel.len());
6132 for (serial, parallel) in serial_threshold.iter().zip(¶llel) {
6133 assert_eq!(serial.data, parallel.data);
6134 assert_eq!(serial.num_coding_passes, parallel.num_coding_passes);
6135 assert_eq!(serial.num_zero_bitplanes, parallel.num_zero_bitplanes);
6136 }
6137 }
6138
6139 #[test]
6140 fn code_block_extraction_copies_partial_edge_blocks_rowwise() {
6141 let quantized: Vec<i32> = (0..20).collect();
6142
6143 let block = copy_code_block_coefficients(&quantized, 5, 3, 1, 2, 3);
6144
6145 assert_eq!(block, vec![8, 9, 13, 14, 18, 19]);
6146 }
6147
6148 #[test]
6149 fn test_encode_lossy() {
6150 let pixels: Vec<u8> = (0..64).collect();
6151
6152 let result = encode(
6153 &pixels,
6154 8,
6155 8,
6156 1,
6157 8,
6158 false,
6159 &EncodeOptions {
6160 num_decomposition_levels: 2,
6161 reversible: false,
6162 guard_bits: 2,
6163 ..Default::default()
6164 },
6165 );
6166
6167 assert!(result.is_ok());
6168 }
6169
6170 #[test]
6171 fn prequantized_htj2k97_matches_precomputed_dwt97_codestream() {
6172 let image = sample_precomputed_htj2k97_image();
6173 let options = EncodeOptions {
6174 num_decomposition_levels: 1,
6175 reversible: false,
6176 guard_bits: 2,
6177 code_block_width_exp: 2,
6178 code_block_height_exp: 2,
6179 ..EncodeOptions::default()
6180 };
6181
6182 let precomputed =
6183 encode_precomputed_htj2k_97(&image, &options).expect("precomputed DWT encode");
6184 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
6185 .expect("test prequantized image");
6186 let direct =
6187 encode_prequantized_htj2k_97(&prequantized, &options).expect("prequantized encode");
6188
6189 assert_eq!(direct, precomputed);
6190 }
6191
6192 #[test]
6193 fn preencoded_htj2k97_matches_prequantized_codestream() {
6194 let image = sample_precomputed_htj2k97_image();
6195 let options = EncodeOptions {
6196 num_decomposition_levels: 1,
6197 reversible: false,
6198 guard_bits: 2,
6199 code_block_width_exp: 2,
6200 code_block_height_exp: 2,
6201 ..EncodeOptions::default()
6202 };
6203 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
6204 .expect("test prequantized image");
6205 let expected =
6206 encode_prequantized_htj2k_97(&prequantized, &options).expect("prequantized encode");
6207 let preencoded = preencoded_htj2k97_image_from_prequantized_for_test(&prequantized)
6208 .expect("test preencoded image");
6209 let actual = encode_preencoded_htj2k_97(&preencoded, &options).expect("preencoded encode");
6210
6211 assert_eq!(actual, expected);
6212 }
6213
6214 #[test]
6215 fn preencoded_htj2k97_rejects_empty_block_with_wrong_zero_bitplanes() {
6216 let (mut image, options) = sample_preencoded_htj2k97_for_test();
6217 let block = &mut image.components[0].resolutions[0].subbands[0].code_blocks[0];
6218 block.encoded = EncodedHtJ2kCodeBlock {
6219 data: Vec::new(),
6220 cleanup_length: 0,
6221 refinement_length: 0,
6222 num_coding_passes: 0,
6223 num_zero_bitplanes: 0,
6224 };
6225
6226 let error = encode_preencoded_htj2k_97(&image, &options)
6227 .expect_err("invalid all-zero block metadata must be rejected");
6228
6229 assert_eq!(error, "empty HTJ2K code-block zero-bitplane count mismatch");
6230 }
6231
6232 #[test]
6233 fn preencoded_htj2k97_rejects_coded_block_with_too_many_zero_bitplanes() {
6234 let (mut image, options) = sample_preencoded_htj2k97_for_test();
6235 let subband = &mut image.components[0].resolutions[0].subbands[0];
6236 subband.code_blocks[0].encoded.num_zero_bitplanes = subband.total_bitplanes;
6237
6238 let error = encode_preencoded_htj2k_97(&image, &options)
6239 .expect_err("coded block with no coded bitplanes must be rejected");
6240
6241 assert_eq!(error, "HTJ2K code-block zero-bitplane count out of range");
6242 }
6243
6244 #[cfg(feature = "std")]
6245 #[test]
6246 fn preencoded_htj2k97_rejects_too_many_coding_passes_without_panic() {
6247 let (mut image, options) = sample_preencoded_htj2k97_for_test();
6248 image.components[0].resolutions[0].subbands[0].code_blocks[0]
6249 .encoded
6250 .num_coding_passes = 165;
6251
6252 let result = std::panic::catch_unwind(|| encode_preencoded_htj2k_97(&image, &options));
6253
6254 assert!(result.is_ok(), "invalid coding pass count must not panic");
6255 assert_eq!(
6256 result.expect("catch_unwind returned checked result"),
6257 Err("HTJ2K code-block coding pass count out of range")
6258 );
6259 }
6260
6261 #[test]
6262 fn prequantized_htj2k97_accepts_empty_high_subbands() {
6263 let options = EncodeOptions {
6264 num_decomposition_levels: 1,
6265 reversible: false,
6266 guard_bits: 2,
6267 code_block_width_exp: 2,
6268 code_block_height_exp: 2,
6269 ..EncodeOptions::default()
6270 };
6271 let image = PrequantizedHtj2k97Image {
6272 width: 1,
6273 height: 1,
6274 bit_depth: 8,
6275 signed: false,
6276 components: vec![PrequantizedHtj2k97Component {
6277 x_rsiz: 1,
6278 y_rsiz: 1,
6279 resolutions: vec![
6280 PrequantizedHtj2k97Resolution {
6281 subbands: vec![PrequantizedHtj2k97Subband {
6282 sub_band_type: J2kSubBandType::LowLow,
6283 num_cbs_x: 1,
6284 num_cbs_y: 1,
6285 total_bitplanes: 11,
6286 code_blocks: vec![PrequantizedHtj2k97CodeBlock {
6287 coefficients: vec![0],
6288 width: 1,
6289 height: 1,
6290 }],
6291 }],
6292 },
6293 PrequantizedHtj2k97Resolution {
6294 subbands: vec![
6295 empty_prequantized_subband(J2kSubBandType::HighLow),
6296 empty_prequantized_subband(J2kSubBandType::LowHigh),
6297 empty_prequantized_subband(J2kSubBandType::HighHigh),
6298 ],
6299 },
6300 ],
6301 }],
6302 };
6303
6304 let encoded =
6305 encode_prequantized_htj2k_97(&image, &options).expect("empty high subbands encode");
6306
6307 assert!(encoded.starts_with(&[0xff, 0x4f]));
6308 }
6309
6310 fn empty_prequantized_subband(sub_band_type: J2kSubBandType) -> PrequantizedHtj2k97Subband {
6311 PrequantizedHtj2k97Subband {
6312 sub_band_type,
6313 num_cbs_x: 0,
6314 num_cbs_y: 0,
6315 total_bitplanes: 0,
6316 code_blocks: Vec::new(),
6317 }
6318 }
6319
6320 fn sample_precomputed_htj2k97_image() -> PrecomputedHtj2k97Image {
6321 let width = 17u32;
6322 let height = 13u32;
6323 let low_width = width.div_ceil(2);
6324 let low_height = height.div_ceil(2);
6325 let high_width = width / 2;
6326 let high_height = height / 2;
6327
6328 PrecomputedHtj2k97Image {
6329 width,
6330 height,
6331 bit_depth: 8,
6332 signed: false,
6333 components: vec![PrecomputedHtj2k97Component {
6334 x_rsiz: 1,
6335 y_rsiz: 1,
6336 dwt: J2kForwardDwt97Output {
6337 ll: sample_f32_coefficients(low_width * low_height, 0.25),
6338 ll_width: low_width,
6339 ll_height: low_height,
6340 levels: vec![J2kForwardDwt97Level {
6341 hl: sample_f32_coefficients(high_width * low_height, -0.75),
6342 lh: sample_f32_coefficients(low_width * high_height, 1.25),
6343 hh: sample_f32_coefficients(high_width * high_height, -1.5),
6344 width,
6345 height,
6346 low_width,
6347 low_height,
6348 high_width,
6349 high_height,
6350 }],
6351 },
6352 }],
6353 }
6354 }
6355
6356 fn sample_precomputed_htj2k53_image() -> PrecomputedHtj2k53Image {
6357 let width = 17u32;
6358 let height = 13u32;
6359 let low_width = width.div_ceil(2);
6360 let low_height = height.div_ceil(2);
6361 let high_width = width / 2;
6362 let high_height = height / 2;
6363
6364 PrecomputedHtj2k53Image {
6365 width,
6366 height,
6367 bit_depth: 8,
6368 signed: false,
6369 components: vec![PrecomputedHtj2k53Component {
6370 x_rsiz: 1,
6371 y_rsiz: 1,
6372 dwt: J2kForwardDwt53Output {
6373 ll: sample_f32_coefficients(low_width * low_height, 0.0),
6374 ll_width: low_width,
6375 ll_height: low_height,
6376 levels: vec![J2kForwardDwt53Level {
6377 hl: sample_f32_coefficients(high_width * low_height, -2.0),
6378 lh: sample_f32_coefficients(low_width * high_height, 2.0),
6379 hh: sample_f32_coefficients(high_width * high_height, -4.0),
6380 width,
6381 height,
6382 low_width,
6383 low_height,
6384 high_width,
6385 high_height,
6386 }],
6387 },
6388 }],
6389 }
6390 }
6391
6392 fn sample_f32_coefficients(len: u32, offset: f32) -> Vec<f32> {
6393 (0..len)
6394 .map(|idx| ((idx % 17) as f32 - 8.0) * 0.5 + offset)
6395 .collect()
6396 }
6397
6398 fn prequantized_htj2k97_image_from_precomputed_for_test(
6399 image: &PrecomputedHtj2k97Image,
6400 options: &EncodeOptions,
6401 ) -> Result<PrequantizedHtj2k97Image, &'static str> {
6402 let guard_bits = options.guard_bits.max(2);
6403 let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
6404 image.bit_depth,
6405 1,
6406 false,
6407 guard_bits,
6408 options.irreversible_quantization_scale,
6409 options.irreversible_quantization_subband_scales,
6410 );
6411 let cb_width = 1u32 << (options.code_block_width_exp + 2);
6412 let cb_height = 1u32 << (options.code_block_height_exp + 2);
6413
6414 let components = image
6415 .components
6416 .iter()
6417 .map(|component| {
6418 let mut resolutions = Vec::with_capacity(component.dwt.levels.len() + 1);
6419 resolutions.push(PrequantizedHtj2k97Resolution {
6420 subbands: vec![prequantized_subband_for_test(
6421 &component.dwt.ll,
6422 component.dwt.ll_width,
6423 component.dwt.ll_height,
6424 SubBandType::LowLow,
6425 &step_sizes[0],
6426 image.bit_depth,
6427 guard_bits,
6428 cb_width,
6429 cb_height,
6430 )?],
6431 });
6432
6433 for (level_index, level) in component.dwt.levels.iter().enumerate() {
6434 let step_base = 1 + level_index * 3;
6435 resolutions.push(PrequantizedHtj2k97Resolution {
6436 subbands: vec![
6437 prequantized_subband_for_test(
6438 &level.hl,
6439 level.high_width,
6440 level.low_height,
6441 SubBandType::HighLow,
6442 &step_sizes[step_base],
6443 image.bit_depth,
6444 guard_bits,
6445 cb_width,
6446 cb_height,
6447 )?,
6448 prequantized_subband_for_test(
6449 &level.lh,
6450 level.low_width,
6451 level.high_height,
6452 SubBandType::LowHigh,
6453 &step_sizes[step_base + 1],
6454 image.bit_depth,
6455 guard_bits,
6456 cb_width,
6457 cb_height,
6458 )?,
6459 prequantized_subband_for_test(
6460 &level.hh,
6461 level.high_width,
6462 level.high_height,
6463 SubBandType::HighHigh,
6464 &step_sizes[step_base + 2],
6465 image.bit_depth,
6466 guard_bits,
6467 cb_width,
6468 cb_height,
6469 )?,
6470 ],
6471 });
6472 }
6473
6474 Ok(PrequantizedHtj2k97Component {
6475 x_rsiz: component.x_rsiz,
6476 y_rsiz: component.y_rsiz,
6477 resolutions,
6478 })
6479 })
6480 .collect::<Result<Vec<_>, &'static str>>()?;
6481
6482 Ok(PrequantizedHtj2k97Image {
6483 width: image.width,
6484 height: image.height,
6485 bit_depth: image.bit_depth,
6486 signed: image.signed,
6487 components,
6488 })
6489 }
6490
6491 #[allow(clippy::too_many_arguments)]
6492 fn prequantized_subband_for_test(
6493 coefficients: &[f32],
6494 width: u32,
6495 height: u32,
6496 sub_band_type: SubBandType,
6497 step_size: &QuantStepSize,
6498 bit_depth: u8,
6499 guard_bits: u8,
6500 cb_width: u32,
6501 cb_height: u32,
6502 ) -> Result<PrequantizedHtj2k97Subband, &'static str> {
6503 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
6504 let prepared = prepare_subband(
6505 coefficients,
6506 width,
6507 height,
6508 step_size,
6509 bit_depth,
6510 guard_bits,
6511 false,
6512 BlockCodingMode::HighThroughput,
6513 cb_width,
6514 cb_height,
6515 sub_band_type,
6516 &mut accelerator,
6517 )?;
6518
6519 Ok(PrequantizedHtj2k97Subband {
6520 sub_band_type: public_sub_band_type(sub_band_type),
6521 num_cbs_x: prepared.num_cbs_x,
6522 num_cbs_y: prepared.num_cbs_y,
6523 total_bitplanes: prepared.total_bitplanes,
6524 code_blocks: prepared
6525 .code_blocks
6526 .into_iter()
6527 .map(|block| PrequantizedHtj2k97CodeBlock {
6528 coefficients: block.coefficients,
6529 width: block.width,
6530 height: block.height,
6531 })
6532 .collect(),
6533 })
6534 }
6535
6536 fn preencoded_htj2k97_image_from_prequantized_for_test(
6537 image: &PrequantizedHtj2k97Image,
6538 ) -> Result<PreencodedHtj2k97Image, &'static str> {
6539 let components = image
6540 .components
6541 .iter()
6542 .map(|component| {
6543 Ok(PreencodedHtj2k97Component {
6544 x_rsiz: component.x_rsiz,
6545 y_rsiz: component.y_rsiz,
6546 resolutions: component
6547 .resolutions
6548 .iter()
6549 .map(|resolution| {
6550 Ok(PreencodedHtj2k97Resolution {
6551 subbands: resolution
6552 .subbands
6553 .iter()
6554 .map(preencoded_subband_from_prequantized_for_test)
6555 .collect::<Result<Vec<_>, &'static str>>()?,
6556 })
6557 })
6558 .collect::<Result<Vec<_>, &'static str>>()?,
6559 })
6560 })
6561 .collect::<Result<Vec<_>, &'static str>>()?;
6562
6563 Ok(PreencodedHtj2k97Image {
6564 width: image.width,
6565 height: image.height,
6566 bit_depth: image.bit_depth,
6567 signed: image.signed,
6568 components,
6569 })
6570 }
6571
6572 fn sample_preencoded_htj2k97_for_test() -> (PreencodedHtj2k97Image, EncodeOptions) {
6573 let image = sample_precomputed_htj2k97_image();
6574 let options = EncodeOptions {
6575 num_decomposition_levels: 1,
6576 reversible: false,
6577 guard_bits: 2,
6578 code_block_width_exp: 2,
6579 code_block_height_exp: 2,
6580 ..EncodeOptions::default()
6581 };
6582 let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
6583 .expect("test prequantized image");
6584 let preencoded = preencoded_htj2k97_image_from_prequantized_for_test(&prequantized)
6585 .expect("test preencoded image");
6586 (preencoded, options)
6587 }
6588
6589 fn preencoded_subband_from_prequantized_for_test(
6590 subband: &PrequantizedHtj2k97Subband,
6591 ) -> Result<PreencodedHtj2k97Subband, &'static str> {
6592 let code_blocks = subband
6593 .code_blocks
6594 .iter()
6595 .map(|block| {
6596 let encoded = ht_block_encode::encode_code_block(
6597 &block.coefficients,
6598 block.width,
6599 block.height,
6600 subband.total_bitplanes,
6601 )?;
6602 Ok(PreencodedHtj2k97CodeBlock {
6603 width: block.width,
6604 height: block.height,
6605 encoded: EncodedHtJ2kCodeBlock {
6606 data: encoded.data,
6607 cleanup_length: encoded.ht_cleanup_length,
6608 refinement_length: encoded.ht_refinement_length,
6609 num_coding_passes: encoded.num_coding_passes,
6610 num_zero_bitplanes: encoded.num_zero_bitplanes,
6611 },
6612 })
6613 })
6614 .collect::<Result<Vec<_>, &'static str>>()?;
6615
6616 Ok(PreencodedHtj2k97Subband {
6617 sub_band_type: subband.sub_band_type,
6618 num_cbs_x: subband.num_cbs_x,
6619 num_cbs_y: subband.num_cbs_y,
6620 total_bitplanes: subband.total_bitplanes,
6621 code_blocks,
6622 })
6623 }
6624
6625 fn assert_htj2k_lossless_roundtrip(
6626 pixels: &[u8],
6627 width: u32,
6628 height: u32,
6629 bit_depth: u8,
6630 num_decomposition_levels: u8,
6631 ) {
6632 let codestream = encode_htj2k(
6633 pixels,
6634 width,
6635 height,
6636 1,
6637 bit_depth,
6638 false,
6639 &EncodeOptions {
6640 num_decomposition_levels,
6641 ..Default::default()
6642 },
6643 )
6644 .expect("HTJ2K encode");
6645
6646 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
6647 let cod_offset = codestream
6648 .windows(2)
6649 .position(|window| window == [0xFF, 0x52])
6650 .expect("COD marker");
6651 assert_eq!(codestream[cod_offset + 12], 0x40);
6652
6653 let image = Image::new(
6654 &codestream,
6655 &DecodeSettings {
6656 resolve_palette_indices: true,
6657 strict: true,
6658 target_resolution: None,
6659 },
6660 )
6661 .expect("parse HT codestream");
6662 let decoded = image.decode_native().expect("decode HT codestream");
6663
6664 assert_eq!(decoded.width, width);
6665 assert_eq!(decoded.height, height);
6666 assert_eq!(decoded.bit_depth, bit_depth);
6667 assert_eq!(decoded.data, pixels);
6668 }
6669
6670 fn gradient_u8(width: u32, height: u32) -> Vec<u8> {
6671 let mut pixels = Vec::with_capacity((width * height) as usize);
6672 for y in 0..height {
6673 for x in 0..width {
6674 pixels.push(((x * 17 + y * 31) % 256) as u8);
6675 }
6676 }
6677 pixels
6678 }
6679
6680 fn lossy_htj2k_roundtrip_u8(
6681 pixels: &[u8],
6682 width: u32,
6683 height: u32,
6684 num_decomposition_levels: u8,
6685 ) -> (Vec<u8>, usize) {
6686 let codestream = encode_htj2k(
6687 pixels,
6688 width,
6689 height,
6690 1,
6691 8,
6692 false,
6693 &EncodeOptions {
6694 num_decomposition_levels,
6695 reversible: false,
6696 guard_bits: 2,
6697 ..Default::default()
6698 },
6699 )
6700 .expect("lossy HT encode");
6701
6702 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
6703
6704 let image = Image::new(
6705 &codestream,
6706 &DecodeSettings {
6707 resolve_palette_indices: true,
6708 strict: true,
6709 target_resolution: None,
6710 },
6711 )
6712 .expect("parse lossy HT codestream");
6713 let decoded = image.decode_native().expect("decode lossy HT codestream");
6714
6715 assert_eq!(decoded.width, width);
6716 assert_eq!(decoded.height, height);
6717 assert_eq!(decoded.bit_depth, 8);
6718
6719 (decoded.data, codestream.len())
6720 }
6721
6722 fn max_abs_error(expected: &[u8], actual: &[u8]) -> u8 {
6723 expected
6724 .iter()
6725 .zip(actual)
6726 .map(|(&expected, &actual)| expected.abs_diff(actual))
6727 .max()
6728 .unwrap_or(0)
6729 }
6730
6731 fn psnr_db(expected: &[u8], actual: &[u8]) -> f64 {
6732 let mse = expected
6733 .iter()
6734 .zip(actual)
6735 .map(|(&expected, &actual)| {
6736 let diff = f64::from(expected) - f64::from(actual);
6737 diff * diff
6738 })
6739 .sum::<f64>()
6740 / expected.len() as f64;
6741
6742 if mse == 0.0 {
6743 f64::INFINITY
6744 } else {
6745 20.0 * 255.0f64.log10() - 10.0 * mse.log10()
6746 }
6747 }
6748
6749 fn assert_not_flat_128(decoded: &[u8]) {
6750 assert!(
6751 decoded.iter().any(|&sample| sample != 128),
6752 "lossy decode collapsed to flat 128"
6753 );
6754 }
6755
6756 #[test]
6757 fn test_encode_high_throughput_zero_image_roundtrip() {
6758 let width = 4u32;
6759 let height = 4u32;
6760 let sample = 2048u16.to_le_bytes();
6761 let mut pixels = Vec::with_capacity((width * height * 2) as usize);
6762 for _ in 0..(width * height) {
6763 pixels.extend_from_slice(&sample);
6764 }
6765
6766 let codestream = encode(
6767 &pixels,
6768 width,
6769 height,
6770 1,
6771 12,
6772 false,
6773 &EncodeOptions {
6774 num_decomposition_levels: 2,
6775 use_ht_block_coding: true,
6776 ..Default::default()
6777 },
6778 )
6779 .expect("HT all-zero encode");
6780
6781 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
6782 let cod_offset = codestream
6783 .windows(2)
6784 .position(|window| window == [0xFF, 0x52])
6785 .expect("COD marker");
6786 assert_eq!(codestream[cod_offset + 12], 0x40);
6787
6788 let image =
6789 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
6790 let decoded = image.decode_native().expect("decode HT codestream");
6791
6792 assert_eq!(decoded.width, width);
6793 assert_eq!(decoded.height, height);
6794 assert_eq!(decoded.bit_depth, 12);
6795 assert_eq!(decoded.data, pixels);
6796 }
6797
6798 #[test]
6799 fn test_encode_high_throughput_nonzero_roundtrip() {
6800 let width = 1u32;
6801 let height = 1u32;
6802 let pixels = 2049u16.to_le_bytes().to_vec();
6803
6804 let codestream = encode_htj2k(
6805 &pixels,
6806 width,
6807 height,
6808 1,
6809 12,
6810 false,
6811 &EncodeOptions {
6812 num_decomposition_levels: 0,
6813 ..Default::default()
6814 },
6815 )
6816 .expect("HT non-zero encode");
6817
6818 assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
6819 let image =
6820 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
6821 let decoded = image.decode_native().expect("decode HT codestream");
6822
6823 assert_eq!(decoded.width, width);
6824 assert_eq!(decoded.height, height);
6825 assert_eq!(decoded.bit_depth, 12);
6826 assert_eq!(decoded.data, pixels);
6827 }
6828
6829 #[test]
6830 fn test_encode_high_throughput_varied_12bit_roundtrip() {
6831 let mut pixels = Vec::with_capacity(32);
6832 for i in 0u16..16 {
6833 pixels.extend_from_slice(&((i * 257) & 0x0FFF).to_le_bytes());
6834 }
6835
6836 let codestream = encode_htj2k(
6837 &pixels,
6838 4,
6839 4,
6840 1,
6841 12,
6842 false,
6843 &EncodeOptions {
6844 num_decomposition_levels: 1,
6845 ..Default::default()
6846 },
6847 )
6848 .expect("HT varied encode");
6849
6850 let image =
6851 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
6852 let decoded = image.decode_native().expect("decode HT codestream");
6853
6854 assert_eq!(decoded.width, 4);
6855 assert_eq!(decoded.height, 4);
6856 assert_eq!(decoded.bit_depth, 12);
6857 assert_eq!(decoded.data, pixels);
6858 }
6859
6860 #[test]
6861 fn test_encode_high_throughput_gradient_8bit_roundtrip() {
6862 let pixels: Vec<u8> = (0..64).collect();
6863
6864 let codestream = encode_htj2k(
6865 &pixels,
6866 8,
6867 8,
6868 1,
6869 8,
6870 false,
6871 &EncodeOptions {
6872 num_decomposition_levels: 3,
6873 ..Default::default()
6874 },
6875 )
6876 .expect("HT gradient encode");
6877
6878 let image =
6879 Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
6880 let decoded = image.decode_native().expect("decode HT codestream");
6881
6882 assert_eq!(decoded.width, 8);
6883 assert_eq!(decoded.height, 8);
6884 assert_eq!(decoded.bit_depth, 8);
6885 assert_eq!(decoded.data, pixels);
6886 }
6887
6888 #[test]
6889 fn test_encode_high_throughput_varied_12bit_large_roundtrip() {
6890 let width = 16u32;
6891 let height = 8u32;
6892 let mut pixels = Vec::with_capacity((width * height * 2) as usize);
6893 for y in 0u16..height as u16 {
6894 for x in 0u16..width as u16 {
6895 let value = (x * 257 + y * 17) & 0x0FFF;
6896 pixels.extend_from_slice(&value.to_le_bytes());
6897 }
6898 }
6899
6900 assert_htj2k_lossless_roundtrip(&pixels, width, height, 12, 4);
6901 }
6902
6903 #[test]
6904 fn test_encode_high_throughput_ramp_16bit_roundtrip() {
6905 let width = 48u32;
6906 let height = 24u32;
6907 let mut pixels = Vec::with_capacity((width * height * 2) as usize);
6908 for y in 0u16..height as u16 {
6909 for x in 0u16..width as u16 {
6910 let value = x * 521 + y * 997;
6911 pixels.extend_from_slice(&value.to_le_bytes());
6912 }
6913 }
6914
6915 assert_htj2k_lossless_roundtrip(&pixels, width, height, 16, 4);
6916 }
6917
6918 #[test]
6919 fn test_encode_high_throughput_lossy_large_gradient_is_parseable() {
6920 let pixels = gradient_u8(128, 128);
6921
6922 let (decoded, codestream_len) = lossy_htj2k_roundtrip_u8(&pixels, 128, 128, 5);
6923
6924 assert!(codestream_len > 110);
6925 assert_not_flat_128(&decoded);
6926 assert!(
6927 psnr_db(&pixels, &decoded) >= 30.0,
6928 "psnr={} max_abs={}",
6929 psnr_db(&pixels, &decoded),
6930 max_abs_error(&pixels, &decoded)
6931 );
6932 }
6933
6934 #[test]
6935 fn test_encode_high_throughput_lossy_constant_extremes_are_not_midgray() {
6936 for sample in [0u8, 255] {
6937 let pixels = vec![sample; 64 * 64];
6938 let (decoded, codestream_len) = lossy_htj2k_roundtrip_u8(&pixels, 64, 64, 4);
6939
6940 assert!(codestream_len > 110);
6941 assert_not_flat_128(&decoded);
6942 assert!(
6943 max_abs_error(&pixels, &decoded) <= 2,
6944 "sample={sample} max_abs={} decoded_min={} decoded_max={}",
6945 max_abs_error(&pixels, &decoded),
6946 decoded.iter().min().unwrap(),
6947 decoded.iter().max().unwrap()
6948 );
6949 }
6950 }
6951
6952 #[test]
6953 fn test_encode_invalid_dimensions() {
6954 let result = encode(&[], 0, 0, 1, 8, false, &EncodeOptions::default());
6955 assert!(result.is_err());
6956 }
6957
6958 #[test]
6959 fn test_encode_too_short() {
6960 let pixels = vec![0u8; 10]; let result = encode(&pixels, 8, 8, 1, 8, false, &EncodeOptions::default());
6962 assert!(result.is_err());
6963 }
6964
6965 #[test]
6966 fn test_deinterleave_rgb() {
6967 let pixels = vec![
6968 10u8, 20, 30, 40, 50, 60, ];
6971 let comps = deinterleave_to_f32(&pixels, 2, 3, 8, false);
6972 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]); }
6976
6977 #[test]
6978 fn deinterleave_rgb8_unsigned_fast_path_matches_generic_output() {
6979 let pixels = (0..96)
6980 .map(|value| ((value * 19 + value / 3) & 0xff) as u8)
6981 .collect::<Vec<_>>();
6982
6983 let expected = deinterleave_to_f32(&pixels, 32, 3, 8, false);
6984 let actual = deinterleave_rgb8_unsigned_to_f32(&pixels, 32);
6985
6986 assert_eq!(actual, expected);
6987 }
6988
6989 #[test]
6990 fn test_encode_decode_roundtrip_gray_8bit() {
6991 use crate::{DecodeSettings, Image};
6992
6993 let original: Vec<u8> = vec![42u8; 64]; let encoded = encode(
6996 &original,
6997 8,
6998 8,
6999 1,
7000 8,
7001 false,
7002 &EncodeOptions {
7003 num_decomposition_levels: 0,
7004 reversible: true,
7005 ..Default::default()
7006 },
7007 )
7008 .expect("encode failed");
7009
7010 let settings = DecodeSettings {
7011 resolve_palette_indices: false,
7012 strict: false,
7013 target_resolution: None,
7014 };
7015 let image = Image::new(&encoded, &settings).expect("parse failed");
7016 let decoded = image.decode_native().expect("decode failed");
7017
7018 assert_eq!(decoded.width, 8);
7019 assert_eq!(decoded.height, 8);
7020 assert_eq!(decoded.data, original, "round-trip mismatch");
7021 }
7022
7023 #[test]
7024 fn test_encode_decode_roundtrip_gray_8bit_single_dwt_level() {
7025 use crate::{DecodeSettings, Image};
7026
7027 let original: Vec<u8> = (0..64 * 64)
7028 .map(|value| ((value * 37 + value / 7) & 0xFF) as u8)
7029 .collect();
7030 let encoded = encode(
7031 &original,
7032 64,
7033 64,
7034 1,
7035 8,
7036 false,
7037 &EncodeOptions {
7038 num_decomposition_levels: 1,
7039 reversible: true,
7040 ..Default::default()
7041 },
7042 )
7043 .expect("encode failed");
7044
7045 let image = Image::new(&encoded, &DecodeSettings::default()).expect("parse failed");
7046 let decoded = image.decode_native().expect("decode failed");
7047
7048 assert_eq!(decoded.width, 64);
7049 assert_eq!(decoded.height, 64);
7050 assert_eq!(decoded.data, original, "round-trip mismatch");
7051 }
7052
7053 #[cfg(feature = "std")]
7058 #[test]
7059 fn encode_htj2k_is_byte_deterministic() {
7060 const WIDTH: u32 = 96;
7061 const HEIGHT: u32 = 80;
7062 const NUM_COMPONENTS: u8 = 3;
7063 const BIT_DEPTH: u8 = 8;
7064 const REPETITIONS: usize = 8;
7065
7066 let pixel_count = (WIDTH * HEIGHT) as usize * usize::from(NUM_COMPONENTS);
7068 let pixels: Vec<u8> = (0..pixel_count)
7069 .map(|i| {
7070 let v = i
7071 .wrapping_mul(6364136223846793005)
7072 .wrapping_add(1442695040888963407);
7073 (v >> 56) as u8
7074 })
7075 .collect();
7076
7077 let options = EncodeOptions {
7078 use_ht_block_coding: true,
7079 reversible: true,
7080 num_decomposition_levels: 5,
7081 validate_high_throughput_codestream: true,
7082 ..EncodeOptions::default()
7083 };
7084
7085 let baseline = encode_htj2k(
7086 &pixels,
7087 WIDTH,
7088 HEIGHT,
7089 NUM_COMPONENTS,
7090 BIT_DEPTH,
7091 false,
7092 &options,
7093 )
7094 .expect("encode_htj2k baseline failed");
7095
7096 assert!(
7097 !baseline.is_empty(),
7098 "baseline codestream must not be empty"
7099 );
7100
7101 for i in 0..REPETITIONS {
7102 let result = encode_htj2k(
7103 &pixels,
7104 WIDTH,
7105 HEIGHT,
7106 NUM_COMPONENTS,
7107 BIT_DEPTH,
7108 false,
7109 &options,
7110 )
7111 .unwrap_or_else(|e| panic!("encode_htj2k repetition {i} failed: {e}"));
7112 assert_eq!(
7113 result,
7114 baseline,
7115 "encode_htj2k repetition {i} produced different bytes \
7116 (len baseline={}, len result={})",
7117 baseline.len(),
7118 result.len()
7119 );
7120 }
7121
7122 println!(
7123 "encode_htj2k_is_byte_deterministic: {} bytes, {} repetitions all identical",
7124 baseline.len(),
7125 REPETITIONS
7126 );
7127 }
7128
7129 #[cfg(feature = "std")]
7132 #[test]
7133 fn native_htj2k_roundtrips_two_component_lossless() {
7134 const WIDTH: u32 = 32;
7135 const HEIGHT: u32 = 24;
7136 const NUM_COMPONENTS: u8 = 2;
7137 const BIT_DEPTH: u8 = 8;
7138
7139 let pixel_count = WIDTH as usize * HEIGHT as usize * usize::from(NUM_COMPONENTS);
7142 let pixels: Vec<u8> = (0..pixel_count)
7143 .map(|i| ((i.wrapping_mul(251).wrapping_add(i / 7)) & 0xFF) as u8)
7144 .collect();
7145
7146 let codestream = encode_htj2k(
7147 &pixels,
7148 WIDTH,
7149 HEIGHT,
7150 NUM_COMPONENTS,
7151 BIT_DEPTH,
7152 false,
7153 &EncodeOptions::default(),
7154 )
7155 .expect("native 2-component HTJ2K encode failed");
7156
7157 let image = Image::new(
7158 &codestream,
7159 &DecodeSettings {
7160 resolve_palette_indices: true,
7161 strict: true,
7162 target_resolution: None,
7163 },
7164 )
7165 .expect("native 2-component HTJ2K parse failed");
7166 let decoded = image
7167 .decode_native()
7168 .expect("native 2-component HTJ2K decode failed");
7169
7170 assert_eq!(decoded.width, WIDTH, "width mismatch");
7171 assert_eq!(decoded.height, HEIGHT, "height mismatch");
7172 assert_eq!(decoded.bit_depth, BIT_DEPTH, "bit_depth mismatch");
7173 assert_eq!(
7174 decoded.num_components, NUM_COMPONENTS,
7175 "component count mismatch"
7176 );
7177 assert_eq!(
7178 decoded.data, pixels,
7179 "2-component HTJ2K lossless round-trip mismatch"
7180 );
7181
7182 println!(
7183 "native_htj2k_roundtrips_two_component_lossless: {} bytes codestream, {} pixel bytes",
7184 codestream.len(),
7185 pixels.len()
7186 );
7187 }
7188
7189 #[cfg(feature = "std")]
7193 #[test]
7194 fn native_htj2k_roundtrips_four_component_lossless() {
7195 const WIDTH: u32 = 32;
7196 const HEIGHT: u32 = 24;
7197 const NUM_COMPONENTS: u8 = 4;
7198 const BIT_DEPTH: u8 = 8;
7199
7200 let pixel_count = WIDTH as usize * HEIGHT as usize * usize::from(NUM_COMPONENTS);
7202 let pixels: Vec<u8> = (0..pixel_count)
7203 .map(|i| ((i.wrapping_mul(197).wrapping_add(i / 13)) & 0xFF) as u8)
7204 .collect();
7205
7206 let codestream = encode_htj2k(
7207 &pixels,
7208 WIDTH,
7209 HEIGHT,
7210 NUM_COMPONENTS,
7211 BIT_DEPTH,
7212 false,
7213 &EncodeOptions::default(),
7214 )
7215 .expect("native 4-component HTJ2K encode failed");
7216
7217 let image = Image::new(
7218 &codestream,
7219 &DecodeSettings {
7220 resolve_palette_indices: true,
7221 strict: true,
7222 target_resolution: None,
7223 },
7224 )
7225 .expect("native 4-component HTJ2K parse failed");
7226 let decoded = image
7227 .decode_native()
7228 .expect("native 4-component HTJ2K decode failed");
7229
7230 assert_eq!(decoded.width, WIDTH, "width mismatch");
7231 assert_eq!(decoded.height, HEIGHT, "height mismatch");
7232 assert_eq!(decoded.bit_depth, BIT_DEPTH, "bit_depth mismatch");
7233 assert_eq!(
7234 decoded.num_components, NUM_COMPONENTS,
7235 "component count mismatch"
7236 );
7237 assert_eq!(
7238 decoded.data, pixels,
7239 "4-component HTJ2K lossless round-trip mismatch"
7240 );
7241
7242 println!(
7243 "native_htj2k_roundtrips_four_component_lossless: {} bytes codestream, {} pixel bytes",
7244 codestream.len(),
7245 pixels.len()
7246 );
7247 }
7248
7249 #[test]
7250 fn classic_pcrd_assigns_limited_budget_by_distortion_slope() {
7251 let candidates = vec![
7252 ClassicSegmentAssignmentCandidate {
7253 block_index: 0,
7254 segment_index: 0,
7255 rate: 500,
7256 distortion_delta: 500.0,
7257 },
7258 ClassicSegmentAssignmentCandidate {
7259 block_index: 1,
7260 segment_index: 0,
7261 rate: 700,
7262 distortion_delta: 7_000.0,
7263 },
7264 ClassicSegmentAssignmentCandidate {
7265 block_index: 2,
7266 segment_index: 0,
7267 rate: 600,
7268 distortion_delta: 3_000.0,
7269 },
7270 ];
7271
7272 let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 3_000])
7273 .expect("PCRD assignment");
7274
7275 assert_eq!(
7276 assignments,
7277 vec![1, 0, 1],
7278 "the highest slope contribution should consume the constrained first-layer budget"
7279 );
7280 }
7281
7282 #[test]
7283 fn classic_pcrd_allows_byte_target_tolerance_for_first_legal_truncation() {
7284 let candidates = vec![ClassicSegmentAssignmentCandidate {
7285 block_index: 0,
7286 segment_index: 0,
7287 rate: 300,
7288 distortion_delta: 1_000.0,
7289 }];
7290
7291 let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 1_000])
7292 .expect("PCRD assignment");
7293
7294 assert_eq!(assignments, vec![0]);
7295 }
7296
7297 #[test]
7298 fn classic_pcrd_does_not_spend_budget_on_non_prefix_segments() {
7299 let candidates = vec![
7300 ClassicSegmentAssignmentCandidate {
7301 block_index: 0,
7302 segment_index: 0,
7303 rate: 1_000,
7304 distortion_delta: 1_000.0,
7305 },
7306 ClassicSegmentAssignmentCandidate {
7307 block_index: 0,
7308 segment_index: 1,
7309 rate: 500,
7310 distortion_delta: 10_000.0,
7311 },
7312 ClassicSegmentAssignmentCandidate {
7313 block_index: 1,
7314 segment_index: 0,
7315 rate: 300,
7316 distortion_delta: 600.0,
7317 },
7318 ];
7319
7320 let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 2_000])
7321 .expect("PCRD assignment");
7322
7323 assert_eq!(
7324 assignments,
7325 vec![1, 1, 0],
7326 "first-layer budget must go to the best legal prefix contribution"
7327 );
7328 }
7329
7330 #[test]
7331 fn ht_layer_assignment_uses_segment_budget_before_block_index() {
7332 let candidates = vec![
7333 HtSegmentAssignmentCandidate {
7334 block_index: 0,
7335 rate: 900,
7336 },
7337 HtSegmentAssignmentCandidate {
7338 block_index: 1,
7339 rate: 200,
7340 },
7341 HtSegmentAssignmentCandidate {
7342 block_index: 2,
7343 rate: 200,
7344 },
7345 ];
7346
7347 let assignments = assign_ht_segment_layers_by_budget(&candidates, 2, &[256, 2_000])
7348 .expect("HTJ2K segment assignment");
7349
7350 assert_eq!(
7351 assignments,
7352 vec![1, 0, 0],
7353 "HTJ2K early layers should be filled by segment byte budget, not block index"
7354 );
7355 }
7356}