1use alloc::vec::Vec;
9use core::cmp::Ordering;
10use core::ops::Range;
11use j2k_types::encode_geometry::maximum_decomposition_levels;
12
13use super::bitplane_encode;
14use super::build::SubBandType;
15use super::codestream::CodeBlockStyle;
16use super::codestream_write::{self, BlockCodingMode, EncodeComponentSampleInfo, EncodeParams};
17use super::fdwt::{self, DwtDecomposition};
18use super::forward_mct;
19use super::ht_block_encode;
20use super::packet_encode::{self, CodeBlockPacketData, ResolutionPacket, SubbandPrecinct};
21#[doc(hidden)]
22pub use super::quantize::irreversible_quantization_step_for_subband;
23use super::quantize::{self, QuantStepSize};
24use crate::profile;
25pub(crate) use crate::J2kSubBandType;
26use crate::{
27 CpuOnlyJ2kEncodeStageAccelerator, EncodedHtJ2kCodeBlock, EncodedJ2kCodeBlock,
28 J2kDeinterleaveMctToF32Job, J2kDeinterleaveToF32Job, J2kEncodeContext,
29 J2kEncodeStageAccelerator, J2kForwardDwt53Job, J2kForwardDwt53Level, J2kForwardDwt53Output,
30 J2kForwardDwt97Job, J2kForwardDwt97Level, J2kForwardDwt97Output, J2kForwardIctJob,
31 J2kForwardRctJob, J2kHtSubbandEncodeJob, J2kHtj2kTileEncodeJob,
32 J2kPacketizationBlockCodingMode, J2kPacketizationCodeBlock, J2kPacketizationEncodeJob,
33 J2kPacketizationPacketDescriptor, J2kPacketizationResolution, J2kPacketizationSubband,
34 J2kQuantizeSubbandJob, J2kResidentEncodeInput, J2kResidentHtj2kTileEncodeJob,
35 J2kTier1CodeBlockEncodeJob, PrecomputedHtj2k53Component, PrecomputedHtj2k53Image,
36 PrecomputedHtj2k97Component, PrecomputedHtj2k97Image, PreencodedHtj2k97CodeBlock,
37 PreencodedHtj2k97CompactCodeBlock, PreencodedHtj2k97CompactComponent,
38 PreencodedHtj2k97CompactImage, PreencodedHtj2k97CompactResolution,
39 PreencodedHtj2k97CompactSubband, PreencodedHtj2k97Component, PreencodedHtj2k97Image,
40 PreencodedHtj2k97Resolution, PreencodedHtj2k97Subband, PrequantizedHtj2k97Component,
41 PrequantizedHtj2k97Image, PrequantizedHtj2k97Resolution, PrequantizedHtj2k97Subband,
42 MAX_J2K_SPEC_COMPONENTS,
43};
44
45const HT_CPU_PARALLEL_FALLBACK_MIN_JOBS: usize = 4;
46const MAX_RAW_PIXEL_ENCODE_BIT_DEPTH: u8 = 24;
47const MAX_PART1_SAMPLE_BIT_DEPTH: u8 = j2k_types::MAX_JPEG2000_PART1_SAMPLE_BIT_DEPTH;
48const MAX_REVERSIBLE_NO_QUANT_EXPONENT: u16 = 31;
49const MAX_REVERSIBLE_NO_QUANT_GUARD_BITS: u8 = 7;
50const MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES: u16 =
51 MAX_REVERSIBLE_NO_QUANT_GUARD_BITS as u16 + MAX_REVERSIBLE_NO_QUANT_EXPONENT - 1;
52const MAX_CLASSIC_ROI_CODED_BITPLANES: u8 = 55;
56const MAX_HT_ROI_CODED_BITPLANES: u8 = 31;
57
58mod api_helpers;
59pub(crate) use self::api_helpers::try_deinterleave_to_f32;
60use self::api_helpers::{
61 default_public_code_block_style, internal_sub_band_type, public_sub_band_type,
62};
63#[cfg(test)]
64pub(crate) use self::api_helpers::{deinterleave_rgb8_unsigned_to_f32, deinterleave_to_f32};
65pub(crate) mod allocation;
66mod code_block_metadata;
67use self::allocation::{checked_add_bytes, checked_element_bytes, host_allocation_failed};
68mod retained_api;
69pub(crate) use self::retained_api::encode_with_accelerator_and_retained_input;
70mod retained_input;
71pub(crate) use self::retained_input::NativeEncodeRetainedInput;
72use self::retained_input::{
73 NativeEncodePhase, NativeEncodePipelineError, NativeEncodePipelineResult, NativeEncodeSession,
74};
75mod options;
76use self::options::{
77 validate_code_block_geometry, validate_irreversible_quantization_profile,
78 validate_precinct_exponents_for_options, CodeBlockGeometry,
79};
80pub use self::options::{
81 EncodeComponentPlane, EncodeOptions, EncodeProgressionOrder, EncodeRoiRegion,
82 EncodeTypedComponentPlane,
83};
84mod resident_contract;
85#[doc(hidden)]
86pub use self::resident_contract::ResidentHtj2kEncodeError;
87mod exact;
88use self::exact::{
89 forward_rct_i64, validate_htj2k_codestream, validate_reversible_i64_encode_options,
90};
91mod i64_packetize;
92mod magnitude;
93use self::i64_packetize::{packetize_i64_component_resolution_packets, I64PacketizeRequest};
94mod multitile;
95mod tile_grid;
96mod tile_parts;
97use self::tile_parts::{
98 validate_packet_header_marker_payloads, write_single_tile_packetized_codestream_for_session,
99};
100mod precomputed;
101use self::precomputed::encode_precomputed_53_with_component_sample_info_for_session;
102pub(in crate::j2c) use self::precomputed::encode_precomputed_htj2k_53_with_mct_and_retained_owner;
103#[cfg(test)]
104use self::precomputed::prepared_subband_from_preencoded_owned_for_tests as prepared_subband_from_preencoded_owned;
105pub use self::precomputed::{
106 encode_precomputed_htj2k_53, encode_precomputed_htj2k_53_with_accelerator,
107 encode_precomputed_htj2k_53_with_accelerator_and_max_host_bytes,
108 encode_precomputed_htj2k_53_with_mct, encode_precomputed_htj2k_53_with_mct_and_accelerator,
109 encode_precomputed_htj2k_97, encode_precomputed_htj2k_97_batch_owned_with_accelerator,
110 encode_precomputed_htj2k_97_batch_owned_with_accelerator_and_max_host_bytes,
111 encode_precomputed_htj2k_97_batch_with_accelerator,
112 encode_precomputed_htj2k_97_with_accelerator,
113 encode_precomputed_htj2k_97_with_accelerator_and_max_host_bytes, encode_precomputed_j2k_53,
114 encode_precomputed_j2k_53_with_accelerator, encode_precomputed_j2k_53_with_mct,
115 encode_precomputed_j2k_53_with_mct_and_accelerator, encode_preencoded_htj2k_97,
116 encode_preencoded_htj2k_97_compact_owned_with_accelerator,
117 encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes,
118 encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes_and_required_magnitude_bound,
119 encode_preencoded_htj2k_97_owned_with_accelerator,
120 encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes,
121 encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes_and_required_magnitude_bound,
122 encode_preencoded_htj2k_97_with_accelerator, encode_prequantized_htj2k_97,
123 encode_prequantized_htj2k_97_with_accelerator,
124 encode_prequantized_htj2k_97_with_accelerator_and_max_host_bytes,
125};
126#[cfg(test)]
127use self::precomputed::{validate_precomputed_dwt97_geometry, validate_precomputed_dwt_geometry};
128mod precomputed_batch;
129use self::precomputed_batch::prepare_precomputed_htj2k97_image_for_batch;
130#[cfg(test)]
131use self::precomputed_batch::{copy_code_block_coefficients, downcast_i64_coefficients_to_i32};
132mod prepared_packets;
133use self::prepared_packets::{
134 encode_prepared_resolution_packets_for_session,
135 encode_prepared_resolution_packets_layered_for_session,
136};
137mod packet_plan;
138use self::packet_plan::{
139 count_compact_code_blocks, ordered_prepared_resolution_packets_for_session,
140 packet_descriptors_for_session, packetization_requires_scalar,
141 packetize_resolution_packets_with_options_for_session,
142 split_component_resolution_packets_by_precinct_for_session,
143};
144mod rate_control;
145#[cfg(test)]
146use self::rate_control::{
147 assign_classic_segment_layers_by_slope, assign_ht_segment_layers_by_budget,
148 ht_layer_contributions,
149};
150use self::rate_control::{
151 assign_classic_segment_layers_by_slope_accounted, assign_ht_segment_layers_by_budget_accounted,
152 classic_layer_contributions_accounted, classic_multilayer_code_block_style,
153 classic_unbudgeted_segment_layers_accounted, enforce_classic_segment_layer_monotonicity,
154 enforce_ht_segment_layer_monotonicity, ht_layer_contributions_accounted, ht_segment_count,
155 ht_segment_rate, ht_unbudgeted_segment_layers_accounted, ClassicSegmentAssignmentCandidate,
156 ClassicSegmentLocation, HtSegmentAssignmentCandidate, HtSegmentLocation, LayeredPreparedBlock,
157 LayeredPreparedPacket, LayeredPreparedSubband,
158};
159mod roi_plan;
160use self::roi_plan::{
161 max_total_bitplanes_for_components, roi_subband_scale,
162 validate_roi_encode_options_nonallocating, ComponentRoiEncodePlan, ComponentRoiEncodeRegion,
163};
164mod samples;
165use self::samples::{
166 native_samples_equal, raw_pixel_bytes_per_sample, read_le_sample_value, sign_extend_sample,
167};
168mod single_tile;
169use self::single_tile::ownership::{cpu_dwt_transient_bytes, dwt_decompositions_retained_bytes};
170#[doc(hidden)]
171pub use self::single_tile::IrreversibleScaleEncoder;
172use self::single_tile::{
173 encode_impl, encode_precomputed_53_single_tile, encode_precomputed_97_single_tile,
174 encode_resident_impl,
175};
176mod subband;
177#[cfg(test)]
178use self::subband::prepare_subband;
179use self::subband::{
180 prepare_subband_for_session, F32SubbandEncodeRequest, I64SubbandEncodeSettings, SubbandSamples,
181};
182mod tier1_allocation;
183mod tier1_driver;
184use self::tier1_driver::encode_prepared_subbands_for_session;
185#[cfg(test)]
186use self::tier1_driver::{
187 encode_all_ht_code_blocks_parallel, encode_all_ht_code_blocks_serial_cpu,
188 encode_prepared_subbands,
189};
190mod transform;
191#[cfg(test)]
192use self::transform::forward_dwt53_output_from_decomposition;
193use self::transform::{
194 adjust_component_step_sizes_for_guard_delta, adjust_reversible_step_sizes_for_guard_delta,
195 encode_forward_dwt, forward_dwt53_output_retained_bytes,
196 reversible_guard_bits_for_marker_limit, try_component_plane_to_f32_for_session,
197 try_encode_forward_ict, try_encode_forward_rct, try_forward_dwt53_output_from_decomposition,
198 validate_band_len, validate_component_sample_info, validate_deinterleaved_components,
199 ForwardDwtRequest,
200};
201mod typed_i64;
202use self::typed_i64::encode_typed_component_planes_53_i64;
203mod typed_components;
204use self::typed_components::encode_typed_component_planes_53_for_session;
205
206pub fn encode(
225 pixels: &[u8],
226 width: u32,
227 height: u32,
228 num_components: u16,
229 bit_depth: u8,
230 signed: bool,
231 options: &EncodeOptions,
232) -> crate::EncodeResult<Vec<u8>> {
233 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
234 encode_with_accelerator(
235 pixels,
236 width,
237 height,
238 num_components,
239 bit_depth,
240 signed,
241 options,
242 &mut accelerator,
243 )
244}
245
246#[expect(
260 clippy::too_many_arguments,
261 reason = "this codec boundary keeps geometry, sample representation, and quantization policy explicit"
262)]
263pub fn encode_htj2k_with_qfactor(
264 pixels: &[u8],
265 width: u32,
266 height: u32,
267 num_components: u16,
268 bit_depth: u8,
269 signed: bool,
270 qfactor: u8,
271 options: &EncodeOptions,
272) -> crate::EncodeResult<Vec<u8>> {
273 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
274 encode_htj2k_with_qfactor_and_accelerator(
275 pixels,
276 width,
277 height,
278 num_components,
279 bit_depth,
280 signed,
281 qfactor,
282 options,
283 &mut accelerator,
284 )
285}
286
287#[doc(hidden)]
289#[expect(
290 clippy::too_many_arguments,
291 reason = "this codec boundary keeps geometry, sample representation, quantization policy, and accelerator explicit"
292)]
293pub fn encode_htj2k_with_qfactor_and_accelerator(
294 pixels: &[u8],
295 width: u32,
296 height: u32,
297 num_components: u16,
298 bit_depth: u8,
299 signed: bool,
300 qfactor: u8,
301 options: &EncodeOptions,
302 accelerator: &mut impl J2kEncodeStageAccelerator,
303) -> crate::EncodeResult<Vec<u8>> {
304 validate_openhtj2k_qfactor_request(qfactor, num_components, options)?;
305 let session = NativeEncodeSession::try_new_with_openhtj2k_qfactor(
306 NativeEncodeRetainedInput::none(),
307 qfactor,
308 )?;
309 let component_sample_info = [EncodeComponentSampleInfo { bit_depth, signed }; 3];
310 encode_with_accelerator_and_component_sample_info_for_session(
311 pixels,
312 width,
313 height,
314 num_components,
315 bit_depth,
316 signed,
317 options,
318 &component_sample_info[..usize::from(num_components)],
319 &session,
320 accelerator,
321 )
322}
323
324fn validate_openhtj2k_qfactor_request(
325 qfactor: u8,
326 num_components: u16,
327 options: &EncodeOptions,
328) -> crate::EncodeResult<()> {
329 if !(1..=100).contains(&qfactor) {
330 return Err(crate::EncodeError::InvalidInput {
331 what: "OpenHTJ2K Qfactor must be in 1..=100",
332 });
333 }
334 if options.reversible || !options.use_ht_block_coding {
335 return Err(crate::EncodeError::InvalidInput {
336 what: "OpenHTJ2K Qfactor requires irreversible HT block coding",
337 });
338 }
339 if options.guard_bits != 1 {
340 return Err(crate::EncodeError::InvalidInput {
341 what: "OpenHTJ2K Qfactor requires one quantization guard bit",
342 });
343 }
344 if !matches!(num_components, 1 | 3) || (num_components == 3 && !options.use_mct) {
345 return Err(crate::EncodeError::InvalidInput {
346 what: "OpenHTJ2K Qfactor requires grayscale or three-component RGB with MCT",
347 });
348 }
349 Ok(())
350}
351
352#[doc(hidden)]
358#[expect(
359 clippy::too_many_arguments,
360 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
361)]
362pub fn encode_with_accelerator(
363 pixels: &[u8],
364 width: u32,
365 height: u32,
366 num_components: u16,
367 bit_depth: u8,
368 signed: bool,
369 options: &EncodeOptions,
370 accelerator: &mut impl J2kEncodeStageAccelerator,
371) -> crate::EncodeResult<Vec<u8>> {
372 encode_with_accelerator_and_retained_input(
373 pixels,
374 width,
375 height,
376 num_components,
377 bit_depth,
378 signed,
379 options,
380 NativeEncodeRetainedInput::none(),
381 accelerator,
382 )
383}
384
385#[doc(hidden)]
391pub fn encode_resident_htj2k_with_accelerator(
392 input: J2kResidentEncodeInput,
393 options: &EncodeOptions,
394 accelerator: &mut impl J2kEncodeStageAccelerator,
395) -> Result<Vec<u8>, ResidentHtj2kEncodeError> {
396 encode_resident_impl(input, options, block_coding_mode(options), accelerator)
397}
398
399#[expect(
400 clippy::too_many_arguments,
401 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
402)]
403fn encode_with_accelerator_and_component_sample_info_for_session(
404 pixels: &[u8],
405 width: u32,
406 height: u32,
407 num_components: u16,
408 bit_depth: u8,
409 signed: bool,
410 options: &EncodeOptions,
411 component_sample_info: &[EncodeComponentSampleInfo],
412 session: &NativeEncodeSession<'_>,
413 accelerator: &mut impl J2kEncodeStageAccelerator,
414) -> crate::EncodeResult<Vec<u8>> {
415 let block_coding_mode = block_coding_mode(options);
416 encode_with_accelerator_and_mode_for_session(
417 pixels,
418 width,
419 height,
420 num_components,
421 bit_depth,
422 signed,
423 options,
424 component_sample_info,
425 block_coding_mode,
426 session,
427 accelerator,
428 )
429}
430
431#[expect(
432 clippy::too_many_arguments,
433 reason = "this internal mode boundary keeps caller geometry and validated coding policy explicit"
434)]
435fn encode_with_accelerator_and_mode_for_session(
436 pixels: &[u8],
437 width: u32,
438 height: u32,
439 num_components: u16,
440 bit_depth: u8,
441 signed: bool,
442 options: &EncodeOptions,
443 component_sample_info: &[EncodeComponentSampleInfo],
444 block_coding_mode: BlockCodingMode,
445 session: &NativeEncodeSession<'_>,
446 accelerator: &mut impl J2kEncodeStageAccelerator,
447) -> crate::EncodeResult<Vec<u8>> {
448 let codestream = encode_impl(
449 pixels,
450 width,
451 height,
452 num_components,
453 bit_depth,
454 signed,
455 options,
456 block_coding_mode,
457 &[],
458 component_sample_info,
459 session,
460 accelerator,
461 )
462 .map_err(NativeEncodePipelineError::into_encode_error)?;
463
464 if block_coding_mode == BlockCodingMode::HighThroughput
465 && options.validate_high_throughput_codestream
466 {
467 validate_htj2k_codestream(
468 &codestream,
469 codestream.capacity(),
470 pixels,
471 width,
472 height,
473 num_components,
474 bit_depth,
475 signed,
476 options.reversible,
477 )?;
478 }
479
480 Ok(codestream)
481}
482
483#[expect(
494 clippy::too_many_arguments,
495 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
496)]
497pub fn encode_with_roi_regions(
498 pixels: &[u8],
499 width: u32,
500 height: u32,
501 num_components: u16,
502 bit_depth: u8,
503 signed: bool,
504 options: &EncodeOptions,
505 roi_regions: &[EncodeRoiRegion],
506) -> crate::EncodeResult<Vec<u8>> {
507 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
508 encode_with_accelerator_and_roi_regions(
509 pixels,
510 width,
511 height,
512 num_components,
513 bit_depth,
514 signed,
515 options,
516 roi_regions,
517 &mut accelerator,
518 )
519}
520
521#[doc(hidden)]
523#[expect(
524 clippy::too_many_arguments,
525 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
526)]
527pub fn encode_with_accelerator_and_roi_regions(
528 pixels: &[u8],
529 width: u32,
530 height: u32,
531 num_components: u16,
532 bit_depth: u8,
533 signed: bool,
534 options: &EncodeOptions,
535 roi_regions: &[EncodeRoiRegion],
536 accelerator: &mut impl J2kEncodeStageAccelerator,
537) -> crate::EncodeResult<Vec<u8>> {
538 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
539 let block_coding_mode = block_coding_mode(options);
540 let codestream = encode_impl(
541 pixels,
542 width,
543 height,
544 num_components,
545 bit_depth,
546 signed,
547 options,
548 block_coding_mode,
549 roi_regions,
550 &[],
551 &session,
552 accelerator,
553 )
554 .map_err(NativeEncodePipelineError::into_encode_error)?;
555
556 if block_coding_mode == BlockCodingMode::HighThroughput
557 && options.validate_high_throughput_codestream
558 {
559 validate_htj2k_codestream(
560 &codestream,
561 codestream.capacity(),
562 pixels,
563 width,
564 height,
565 num_components,
566 bit_depth,
567 signed,
568 options.reversible,
569 )?;
570 }
571
572 Ok(codestream)
573}
574
575pub fn encode_htj2k(
584 pixels: &[u8],
585 width: u32,
586 height: u32,
587 num_components: u16,
588 bit_depth: u8,
589 signed: bool,
590 options: &EncodeOptions,
591) -> crate::EncodeResult<Vec<u8>> {
592 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
593 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
594 encode_with_accelerator_and_mode_for_session(
595 pixels,
596 width,
597 height,
598 num_components,
599 bit_depth,
600 signed,
601 options,
602 &[],
603 BlockCodingMode::HighThroughput,
604 &session,
605 &mut accelerator,
606 )
607}
608
609pub fn encode_component_planes_53(
621 planes: &[EncodeComponentPlane<'_>],
622 width: u32,
623 height: u32,
624 bit_depth: u8,
625 signed: bool,
626 options: &EncodeOptions,
627) -> crate::EncodeResult<Vec<u8>> {
628 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
629 let requested_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
630 planes.len(),
631 "component-plane typed descriptor owners",
632 )?;
633 session.checked_phase(requested_bytes, "component-plane typed descriptor owners")?;
634 let mut typed_planes = Vec::new();
635 typed_planes.try_reserve_exact(planes.len()).map_err(|_| {
636 host_allocation_failed("component-plane typed descriptor owners", requested_bytes)
637 })?;
638 typed_planes.extend(planes.iter().map(|plane| EncodeTypedComponentPlane {
639 data: plane.data,
640 x_rsiz: plane.x_rsiz,
641 y_rsiz: plane.y_rsiz,
642 bit_depth,
643 signed,
644 }));
645 let actual_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
646 typed_planes.capacity(),
647 "component-plane typed descriptor owners",
648 )?;
649 let typed_session = session.checked_child_session(
650 &typed_planes,
651 actual_bytes,
652 "component-plane typed descriptor owners",
653 )?;
654 encode_typed_component_planes_53_for_session(
655 &typed_planes,
656 width,
657 height,
658 options,
659 &typed_session,
660 )
661 .map_err(NativeEncodePipelineError::into_encode_error)
662}
663
664pub fn encode_typed_component_planes_53(
677 planes: &[EncodeTypedComponentPlane<'_>],
678 width: u32,
679 height: u32,
680 options: &EncodeOptions,
681) -> crate::EncodeResult<Vec<u8>> {
682 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
683 encode_typed_component_planes_53_for_session(planes, width, height, options, &session)
684 .map_err(NativeEncodePipelineError::into_encode_error)
685}
686
687fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
691 if options.use_ht_block_coding {
692 BlockCodingMode::HighThroughput
693 } else {
694 BlockCodingMode::Classic
695 }
696}
697
698fn ht_target_coding_passes_for_options(
699 options: &EncodeOptions,
700 block_coding_mode: BlockCodingMode,
701) -> u8 {
702 if block_coding_mode == BlockCodingMode::HighThroughput
703 && !options.reversible
704 && options.num_layers > 1
705 {
706 options.num_layers.min(3)
707 } else {
708 1
709 }
710}
711
712#[doc(hidden)]
721#[must_use]
722pub fn irreversible_quantization_profile_scale_ceiling(
723 bit_depth: u8,
724 options: &EncodeOptions,
725) -> Option<f32> {
726 let profile = options
727 .irreversible_quantization_profile
728 .as_deref()
729 .filter(|profile| !quantize::profile_is_unity(profile))?;
730 Some(quantize::profile_scale_ceiling(
731 bit_depth,
732 requested_guard_bits(options, options.use_mct, false),
733 options.irreversible_quantization_scale,
734 options.irreversible_quantization_subband_scales,
735 profile,
736 ))
737}
738
739#[doc(hidden)]
747#[must_use]
748pub fn irreversible_quantization_scale_bounds(
749 bit_depth: u8,
750 options: &EncodeOptions,
751) -> (f32, f32) {
752 let guard_bits = requested_guard_bits(options, options.use_mct, false);
753 let maximum_exponent = if options.use_ht_block_coding {
756 32_u16.saturating_sub(u16::from(guard_bits)).min(31)
757 } else {
758 31
759 };
760 quantize::quantization_scale_bounds(
761 bit_depth,
762 guard_bits,
763 options.irreversible_quantization_subband_scales,
764 options.irreversible_quantization_profile.as_deref(),
765 options.num_decomposition_levels,
766 maximum_exponent,
767 )
768}
769
770fn requested_guard_bits(options: &EncodeOptions, use_mct: bool, openhtj2k_qfactor: bool) -> u8 {
771 if openhtj2k_qfactor {
772 1
773 } else if options.reversible && !use_mct {
774 options.guard_bits
775 } else {
776 options.guard_bits.max(2)
777 }
778}
779
780enum PreparedCodeBlockCoefficients {
781 I32(Vec<i32>),
782 I64(Vec<i64>),
783 Empty,
784}
785
786#[cfg(test)]
787impl PreparedCodeBlockCoefficients {
788 fn is_empty(&self) -> bool {
789 match self {
790 Self::I32(values) => values.is_empty(),
791 Self::I64(values) => values.is_empty(),
792 Self::Empty => true,
793 }
794 }
795}
796
797struct PreparedEncodeCodeBlock {
798 coefficients: PreparedCodeBlockCoefficients,
799 width: u32,
800 height: u32,
801}
802
803struct PreparedEncodeSubband {
804 origin: (u32, u32),
805 code_blocks: Vec<PreparedEncodeCodeBlock>,
806 preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
807 preencoded_ht_maximum_cleanup_magnitude: Option<u64>,
808 num_cbs_x: u32,
809 num_cbs_y: u32,
810 code_block_width: u32,
811 code_block_height: u32,
812 width: u32,
813 height: u32,
814 sub_band_type: SubBandType,
815 total_bitplanes: u8,
816 block_coding_mode: BlockCodingMode,
817 ht_target_coding_passes: u8,
818}
819
820struct PreparedResolutionPacket {
821 component: u16,
822 resolution: u32,
823 precinct: u64,
824 subbands: Vec<PreparedEncodeSubband>,
825}
826
827struct PreparedCompactCodeBlock<'a> {
828 data: &'a [u8],
829 cleanup_length: u32,
830 refinement_length: u32,
831 num_coding_passes: u8,
832 num_zero_bitplanes: u8,
833}
834
835struct PreparedCompactSubband<'a> {
836 code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
837 num_cbs_x: u32,
838 num_cbs_y: u32,
839}
840
841struct PreparedCompactResolutionPacket<'a> {
842 component: u16,
843 resolution: u32,
844 precinct: u64,
845 subbands: Vec<PreparedCompactSubband<'a>>,
846}
847
848#[cfg(test)]
849#[path = "encode_tests.rs"]
850mod tests;