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_parts;
96use self::tile_parts::{
97 validate_packet_header_marker_payloads, write_single_tile_packetized_codestream_for_session,
98};
99mod precomputed;
100use self::precomputed::encode_precomputed_53_with_component_sample_info_for_session;
101pub(in crate::j2c) use self::precomputed::encode_precomputed_htj2k_53_with_mct_and_retained_owner;
102#[cfg(test)]
103use self::precomputed::prepared_subband_from_preencoded_owned_for_tests as prepared_subband_from_preencoded_owned;
104pub use self::precomputed::{
105 encode_precomputed_htj2k_53, encode_precomputed_htj2k_53_with_accelerator,
106 encode_precomputed_htj2k_53_with_accelerator_and_max_host_bytes,
107 encode_precomputed_htj2k_53_with_mct, encode_precomputed_htj2k_53_with_mct_and_accelerator,
108 encode_precomputed_htj2k_97, encode_precomputed_htj2k_97_batch_owned_with_accelerator,
109 encode_precomputed_htj2k_97_batch_owned_with_accelerator_and_max_host_bytes,
110 encode_precomputed_htj2k_97_batch_with_accelerator,
111 encode_precomputed_htj2k_97_with_accelerator,
112 encode_precomputed_htj2k_97_with_accelerator_and_max_host_bytes, encode_precomputed_j2k_53,
113 encode_precomputed_j2k_53_with_accelerator, encode_precomputed_j2k_53_with_mct,
114 encode_precomputed_j2k_53_with_mct_and_accelerator, encode_preencoded_htj2k_97,
115 encode_preencoded_htj2k_97_compact_owned_with_accelerator,
116 encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes,
117 encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes_and_required_magnitude_bound,
118 encode_preencoded_htj2k_97_owned_with_accelerator,
119 encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes,
120 encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes_and_required_magnitude_bound,
121 encode_preencoded_htj2k_97_with_accelerator, encode_prequantized_htj2k_97,
122 encode_prequantized_htj2k_97_with_accelerator,
123 encode_prequantized_htj2k_97_with_accelerator_and_max_host_bytes,
124};
125#[cfg(test)]
126use self::precomputed::{validate_precomputed_dwt97_geometry, validate_precomputed_dwt_geometry};
127mod precomputed_batch;
128use self::precomputed_batch::prepare_precomputed_htj2k97_image_for_batch;
129#[cfg(test)]
130use self::precomputed_batch::{copy_code_block_coefficients, downcast_i64_coefficients_to_i32};
131mod prepared_packets;
132use self::prepared_packets::{
133 encode_prepared_resolution_packets_for_session,
134 encode_prepared_resolution_packets_layered_for_session,
135};
136mod packet_plan;
137use self::packet_plan::{
138 count_compact_code_blocks, ordered_prepared_resolution_packets_for_session,
139 packet_descriptors_for_order_for_session, packetization_requires_scalar,
140 packetize_resolution_packets_with_options_for_session,
141 split_component_resolution_packets_by_precinct_for_session,
142};
143mod rate_control;
144#[cfg(test)]
145use self::rate_control::{
146 assign_classic_segment_layers_by_slope, assign_ht_segment_layers_by_budget,
147 ht_layer_contributions,
148};
149use self::rate_control::{
150 assign_classic_segment_layers_by_slope_accounted, assign_ht_segment_layers_by_budget_accounted,
151 classic_layer_contributions_accounted, classic_multilayer_code_block_style,
152 classic_unbudgeted_segment_layers_accounted, enforce_classic_segment_layer_monotonicity,
153 enforce_ht_segment_layer_monotonicity, ht_layer_contributions_accounted, ht_segment_count,
154 ht_segment_rate, ht_unbudgeted_segment_layers_accounted, ClassicSegmentAssignmentCandidate,
155 ClassicSegmentLocation, HtSegmentAssignmentCandidate, HtSegmentLocation, LayeredPreparedBlock,
156 LayeredPreparedPacket, LayeredPreparedSubband,
157};
158mod roi_plan;
159use self::roi_plan::{
160 max_total_bitplanes_for_components, roi_subband_scale,
161 validate_roi_encode_options_nonallocating, ComponentRoiEncodePlan, ComponentRoiEncodeRegion,
162};
163mod samples;
164use self::samples::{
165 native_samples_equal, raw_pixel_bytes_per_sample, read_le_sample_value, sign_extend_sample,
166};
167mod single_tile;
168use self::single_tile::ownership::{cpu_dwt_transient_bytes, dwt_decompositions_retained_bytes};
169use self::single_tile::{
170 encode_impl, encode_precomputed_53_single_tile, encode_precomputed_97_single_tile,
171 encode_resident_impl,
172};
173mod subband;
174#[cfg(test)]
175use self::subband::prepare_subband;
176use self::subband::{
177 prepare_subband_for_session, F32SubbandEncodeRequest, I64SubbandEncodeSettings,
178};
179mod tier1_allocation;
180mod tier1_driver;
181use self::tier1_driver::encode_prepared_subbands_for_session;
182#[cfg(test)]
183use self::tier1_driver::{
184 encode_all_ht_code_blocks_parallel, encode_all_ht_code_blocks_serial_cpu,
185 encode_prepared_subbands,
186};
187mod transform;
188#[cfg(test)]
189use self::transform::forward_dwt53_output_from_decomposition;
190use self::transform::{
191 adjust_component_step_sizes_for_guard_delta, adjust_reversible_step_sizes_for_guard_delta,
192 encode_forward_dwt, forward_dwt53_output_retained_bytes,
193 reversible_guard_bits_for_marker_limit, try_component_plane_to_f32_for_session,
194 try_encode_forward_ict, try_encode_forward_rct, try_forward_dwt53_output_from_decomposition,
195 validate_band_len, validate_component_sample_info, validate_deinterleaved_components,
196 ForwardDwtRequest,
197};
198mod typed_i64;
199use self::typed_i64::encode_typed_component_planes_53_i64;
200mod typed_components;
201use self::typed_components::encode_typed_component_planes_53_for_session;
202
203pub fn encode(
222 pixels: &[u8],
223 width: u32,
224 height: u32,
225 num_components: u16,
226 bit_depth: u8,
227 signed: bool,
228 options: &EncodeOptions,
229) -> crate::EncodeResult<Vec<u8>> {
230 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
231 encode_with_accelerator(
232 pixels,
233 width,
234 height,
235 num_components,
236 bit_depth,
237 signed,
238 options,
239 &mut accelerator,
240 )
241}
242
243#[doc(hidden)]
249#[expect(
250 clippy::too_many_arguments,
251 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
252)]
253pub fn encode_with_accelerator(
254 pixels: &[u8],
255 width: u32,
256 height: u32,
257 num_components: u16,
258 bit_depth: u8,
259 signed: bool,
260 options: &EncodeOptions,
261 accelerator: &mut impl J2kEncodeStageAccelerator,
262) -> crate::EncodeResult<Vec<u8>> {
263 encode_with_accelerator_and_retained_input(
264 pixels,
265 width,
266 height,
267 num_components,
268 bit_depth,
269 signed,
270 options,
271 NativeEncodeRetainedInput::none(),
272 accelerator,
273 )
274}
275
276#[doc(hidden)]
282pub fn encode_resident_htj2k_with_accelerator(
283 input: J2kResidentEncodeInput,
284 options: &EncodeOptions,
285 accelerator: &mut impl J2kEncodeStageAccelerator,
286) -> Result<Vec<u8>, ResidentHtj2kEncodeError> {
287 encode_resident_impl(input, options, block_coding_mode(options), accelerator)
288}
289
290#[expect(
291 clippy::too_many_arguments,
292 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
293)]
294fn encode_with_accelerator_and_component_sample_info_for_session(
295 pixels: &[u8],
296 width: u32,
297 height: u32,
298 num_components: u16,
299 bit_depth: u8,
300 signed: bool,
301 options: &EncodeOptions,
302 component_sample_info: &[EncodeComponentSampleInfo],
303 session: &NativeEncodeSession<'_>,
304 accelerator: &mut impl J2kEncodeStageAccelerator,
305) -> crate::EncodeResult<Vec<u8>> {
306 let block_coding_mode = block_coding_mode(options);
307 encode_with_accelerator_and_mode_for_session(
308 pixels,
309 width,
310 height,
311 num_components,
312 bit_depth,
313 signed,
314 options,
315 component_sample_info,
316 block_coding_mode,
317 session,
318 accelerator,
319 )
320}
321
322#[expect(
323 clippy::too_many_arguments,
324 reason = "this internal mode boundary keeps caller geometry and validated coding policy explicit"
325)]
326fn encode_with_accelerator_and_mode_for_session(
327 pixels: &[u8],
328 width: u32,
329 height: u32,
330 num_components: u16,
331 bit_depth: u8,
332 signed: bool,
333 options: &EncodeOptions,
334 component_sample_info: &[EncodeComponentSampleInfo],
335 block_coding_mode: BlockCodingMode,
336 session: &NativeEncodeSession<'_>,
337 accelerator: &mut impl J2kEncodeStageAccelerator,
338) -> crate::EncodeResult<Vec<u8>> {
339 let codestream = encode_impl(
340 pixels,
341 width,
342 height,
343 num_components,
344 bit_depth,
345 signed,
346 options,
347 block_coding_mode,
348 &[],
349 component_sample_info,
350 session,
351 accelerator,
352 )
353 .map_err(NativeEncodePipelineError::into_encode_error)?;
354
355 if block_coding_mode == BlockCodingMode::HighThroughput
356 && options.validate_high_throughput_codestream
357 {
358 validate_htj2k_codestream(
359 &codestream,
360 codestream.capacity(),
361 pixels,
362 width,
363 height,
364 num_components,
365 bit_depth,
366 signed,
367 options.reversible,
368 )?;
369 }
370
371 Ok(codestream)
372}
373
374#[expect(
385 clippy::too_many_arguments,
386 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
387)]
388pub fn encode_with_roi_regions(
389 pixels: &[u8],
390 width: u32,
391 height: u32,
392 num_components: u16,
393 bit_depth: u8,
394 signed: bool,
395 options: &EncodeOptions,
396 roi_regions: &[EncodeRoiRegion],
397) -> crate::EncodeResult<Vec<u8>> {
398 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
399 encode_with_accelerator_and_roi_regions(
400 pixels,
401 width,
402 height,
403 num_components,
404 bit_depth,
405 signed,
406 options,
407 roi_regions,
408 &mut accelerator,
409 )
410}
411
412#[doc(hidden)]
414#[expect(
415 clippy::too_many_arguments,
416 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
417)]
418pub fn encode_with_accelerator_and_roi_regions(
419 pixels: &[u8],
420 width: u32,
421 height: u32,
422 num_components: u16,
423 bit_depth: u8,
424 signed: bool,
425 options: &EncodeOptions,
426 roi_regions: &[EncodeRoiRegion],
427 accelerator: &mut impl J2kEncodeStageAccelerator,
428) -> crate::EncodeResult<Vec<u8>> {
429 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
430 let block_coding_mode = block_coding_mode(options);
431 let codestream = encode_impl(
432 pixels,
433 width,
434 height,
435 num_components,
436 bit_depth,
437 signed,
438 options,
439 block_coding_mode,
440 roi_regions,
441 &[],
442 &session,
443 accelerator,
444 )
445 .map_err(NativeEncodePipelineError::into_encode_error)?;
446
447 if block_coding_mode == BlockCodingMode::HighThroughput
448 && options.validate_high_throughput_codestream
449 {
450 validate_htj2k_codestream(
451 &codestream,
452 codestream.capacity(),
453 pixels,
454 width,
455 height,
456 num_components,
457 bit_depth,
458 signed,
459 options.reversible,
460 )?;
461 }
462
463 Ok(codestream)
464}
465
466pub fn encode_htj2k(
475 pixels: &[u8],
476 width: u32,
477 height: u32,
478 num_components: u16,
479 bit_depth: u8,
480 signed: bool,
481 options: &EncodeOptions,
482) -> crate::EncodeResult<Vec<u8>> {
483 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
484 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
485 encode_with_accelerator_and_mode_for_session(
486 pixels,
487 width,
488 height,
489 num_components,
490 bit_depth,
491 signed,
492 options,
493 &[],
494 BlockCodingMode::HighThroughput,
495 &session,
496 &mut accelerator,
497 )
498}
499
500pub fn encode_component_planes_53(
512 planes: &[EncodeComponentPlane<'_>],
513 width: u32,
514 height: u32,
515 bit_depth: u8,
516 signed: bool,
517 options: &EncodeOptions,
518) -> crate::EncodeResult<Vec<u8>> {
519 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
520 let requested_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
521 planes.len(),
522 "component-plane typed descriptor owners",
523 )?;
524 session.checked_phase(requested_bytes, "component-plane typed descriptor owners")?;
525 let mut typed_planes = Vec::new();
526 typed_planes.try_reserve_exact(planes.len()).map_err(|_| {
527 host_allocation_failed("component-plane typed descriptor owners", requested_bytes)
528 })?;
529 typed_planes.extend(planes.iter().map(|plane| EncodeTypedComponentPlane {
530 data: plane.data,
531 x_rsiz: plane.x_rsiz,
532 y_rsiz: plane.y_rsiz,
533 bit_depth,
534 signed,
535 }));
536 let actual_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
537 typed_planes.capacity(),
538 "component-plane typed descriptor owners",
539 )?;
540 let typed_session = session.checked_child_session(
541 &typed_planes,
542 actual_bytes,
543 "component-plane typed descriptor owners",
544 )?;
545 encode_typed_component_planes_53_for_session(
546 &typed_planes,
547 width,
548 height,
549 options,
550 &typed_session,
551 )
552 .map_err(NativeEncodePipelineError::into_encode_error)
553}
554
555pub fn encode_typed_component_planes_53(
568 planes: &[EncodeTypedComponentPlane<'_>],
569 width: u32,
570 height: u32,
571 options: &EncodeOptions,
572) -> crate::EncodeResult<Vec<u8>> {
573 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
574 encode_typed_component_planes_53_for_session(planes, width, height, options, &session)
575 .map_err(NativeEncodePipelineError::into_encode_error)
576}
577
578fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
582 if options.use_ht_block_coding {
583 BlockCodingMode::HighThroughput
584 } else {
585 BlockCodingMode::Classic
586 }
587}
588
589fn ht_target_coding_passes_for_options(
590 options: &EncodeOptions,
591 block_coding_mode: BlockCodingMode,
592) -> u8 {
593 if block_coding_mode == BlockCodingMode::HighThroughput
594 && !options.reversible
595 && options.num_layers > 1
596 {
597 options.num_layers.min(3)
598 } else {
599 1
600 }
601}
602
603enum PreparedCodeBlockCoefficients {
604 I32(Vec<i32>),
605 I64(Vec<i64>),
606 Empty,
607}
608
609#[cfg(test)]
610impl PreparedCodeBlockCoefficients {
611 fn is_empty(&self) -> bool {
612 match self {
613 Self::I32(values) => values.is_empty(),
614 Self::I64(values) => values.is_empty(),
615 Self::Empty => true,
616 }
617 }
618}
619
620struct PreparedEncodeCodeBlock {
621 coefficients: PreparedCodeBlockCoefficients,
622 width: u32,
623 height: u32,
624}
625
626struct PreparedEncodeSubband {
627 code_blocks: Vec<PreparedEncodeCodeBlock>,
628 preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
629 preencoded_ht_maximum_cleanup_magnitude: Option<u64>,
630 num_cbs_x: u32,
631 num_cbs_y: u32,
632 code_block_width: u32,
633 code_block_height: u32,
634 width: u32,
635 height: u32,
636 sub_band_type: SubBandType,
637 total_bitplanes: u8,
638 block_coding_mode: BlockCodingMode,
639 ht_target_coding_passes: u8,
640}
641
642struct PreparedResolutionPacket {
643 component: u16,
644 resolution: u32,
645 precinct: u64,
646 subbands: Vec<PreparedEncodeSubband>,
647}
648
649struct PreparedCompactCodeBlock<'a> {
650 data: &'a [u8],
651 cleanup_length: u32,
652 refinement_length: u32,
653 num_coding_passes: u8,
654 num_zero_bitplanes: u8,
655}
656
657struct PreparedCompactSubband<'a> {
658 code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
659 num_cbs_x: u32,
660 num_cbs_y: u32,
661}
662
663struct PreparedCompactResolutionPacket<'a> {
664 component: u16,
665 resolution: u32,
666 precinct: u64,
667 subbands: Vec<PreparedCompactSubband<'a>>,
668}
669
670#[cfg(test)]
671#[path = "encode_tests.rs"]
672mod tests;