1use alloc::vec::Vec;
9use core::cmp::Ordering;
10use core::ops::Range;
11use j2k_codec_math::dwt::max_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 J2kDeinterleaveToF32Job, J2kEncodeStageAccelerator, J2kForwardDwt53Job, J2kForwardDwt53Level,
29 J2kForwardDwt53Output, J2kForwardDwt97Job, J2kForwardDwt97Level, J2kForwardDwt97Output,
30 J2kForwardIctJob, J2kForwardRctJob, J2kHtSubbandEncodeJob, J2kHtj2kTileEncodeJob,
31 J2kPacketizationBlockCodingMode, J2kPacketizationCodeBlock, J2kPacketizationEncodeJob,
32 J2kPacketizationPacketDescriptor, J2kPacketizationResolution, J2kPacketizationSubband,
33 J2kQuantizeSubbandJob, J2kResidentEncodeInput, J2kResidentHtj2kTileEncodeJob,
34 J2kTier1CodeBlockEncodeJob, PrecomputedHtj2k53Component, PrecomputedHtj2k53Image,
35 PrecomputedHtj2k97Component, PrecomputedHtj2k97Image, PreencodedHtj2k97CodeBlock,
36 PreencodedHtj2k97CompactCodeBlock, PreencodedHtj2k97CompactComponent,
37 PreencodedHtj2k97CompactImage, PreencodedHtj2k97CompactResolution,
38 PreencodedHtj2k97CompactSubband, PreencodedHtj2k97Component, PreencodedHtj2k97Image,
39 PreencodedHtj2k97Resolution, PreencodedHtj2k97Subband, PrequantizedHtj2k97Component,
40 PrequantizedHtj2k97Image, PrequantizedHtj2k97Resolution, PrequantizedHtj2k97Subband,
41 MAX_J2K_SPEC_COMPONENTS,
42};
43
44const HT_CPU_PARALLEL_FALLBACK_MIN_JOBS: usize = 4;
45const MAX_RAW_PIXEL_ENCODE_BIT_DEPTH: u8 = 24;
46const MAX_PART1_SAMPLE_BIT_DEPTH: u8 = j2k_types::MAX_JPEG2000_PART1_SAMPLE_BIT_DEPTH;
47const MAX_REVERSIBLE_NO_QUANT_EXPONENT: u16 = 31;
48const MAX_REVERSIBLE_NO_QUANT_GUARD_BITS: u8 = 7;
49const MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES: u16 =
50 MAX_REVERSIBLE_NO_QUANT_GUARD_BITS as u16 + MAX_REVERSIBLE_NO_QUANT_EXPONENT - 1;
51const MAX_CLASSIC_ROI_CODED_BITPLANES: u8 = 55;
55const MAX_HT_ROI_CODED_BITPLANES: u8 = 31;
56
57mod api_helpers;
58pub(crate) use self::api_helpers::try_deinterleave_to_f32;
59use self::api_helpers::{
60 default_public_code_block_style, internal_sub_band_type, public_sub_band_type,
61};
62#[cfg(test)]
63pub(crate) use self::api_helpers::{deinterleave_rgb8_unsigned_to_f32, deinterleave_to_f32};
64pub(crate) mod allocation;
65mod code_block_metadata;
66use self::allocation::{checked_add_bytes, checked_element_bytes, host_allocation_failed};
67mod retained_api;
68pub(crate) use self::retained_api::encode_with_accelerator_and_retained_input;
69mod retained_input;
70pub(crate) use self::retained_input::NativeEncodeRetainedInput;
71use self::retained_input::{
72 NativeEncodePhase, NativeEncodePipelineError, NativeEncodePipelineResult, NativeEncodeSession,
73};
74mod options;
75use self::options::{
76 validate_code_block_geometry, validate_irreversible_quantization_profile,
77 validate_precinct_exponents_for_options, CodeBlockGeometry,
78};
79pub use self::options::{
80 EncodeComponentPlane, EncodeOptions, EncodeProgressionOrder, EncodeRoiRegion,
81 EncodeTypedComponentPlane,
82};
83mod resident_contract;
84#[doc(hidden)]
85pub use self::resident_contract::ResidentHtj2kEncodeError;
86mod exact;
87use self::exact::{
88 forward_rct_i64, validate_htj2k_codestream, validate_reversible_i64_encode_options,
89};
90mod i64_packetize;
91use self::i64_packetize::{packetize_i64_component_resolution_packets, I64PacketizeRequest};
92mod multitile;
93mod tile_parts;
94use self::tile_parts::{
95 validate_packet_header_marker_payloads, write_single_tile_packetized_codestream_for_session,
96};
97mod precomputed;
98use self::precomputed::encode_precomputed_53_with_component_sample_info_for_session;
99pub(in crate::j2c) use self::precomputed::encode_precomputed_htj2k_53_with_mct_and_retained_owner;
100#[cfg(test)]
101use self::precomputed::prepared_subband_from_preencoded_owned_for_tests as prepared_subband_from_preencoded_owned;
102pub use self::precomputed::{
103 encode_precomputed_htj2k_53, encode_precomputed_htj2k_53_with_accelerator,
104 encode_precomputed_htj2k_53_with_accelerator_and_max_host_bytes,
105 encode_precomputed_htj2k_53_with_mct, encode_precomputed_htj2k_53_with_mct_and_accelerator,
106 encode_precomputed_htj2k_97, encode_precomputed_htj2k_97_batch_owned_with_accelerator,
107 encode_precomputed_htj2k_97_batch_owned_with_accelerator_and_max_host_bytes,
108 encode_precomputed_htj2k_97_batch_with_accelerator,
109 encode_precomputed_htj2k_97_with_accelerator,
110 encode_precomputed_htj2k_97_with_accelerator_and_max_host_bytes, encode_precomputed_j2k_53,
111 encode_precomputed_j2k_53_with_accelerator, encode_precomputed_j2k_53_with_mct,
112 encode_precomputed_j2k_53_with_mct_and_accelerator, encode_preencoded_htj2k_97,
113 encode_preencoded_htj2k_97_compact_owned_with_accelerator,
114 encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes,
115 encode_preencoded_htj2k_97_owned_with_accelerator,
116 encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes,
117 encode_preencoded_htj2k_97_with_accelerator, encode_prequantized_htj2k_97,
118 encode_prequantized_htj2k_97_with_accelerator,
119 encode_prequantized_htj2k_97_with_accelerator_and_max_host_bytes,
120};
121#[cfg(test)]
122use self::precomputed::{validate_precomputed_dwt97_geometry, validate_precomputed_dwt_geometry};
123mod precomputed_batch;
124use self::precomputed_batch::prepare_precomputed_htj2k97_image_for_batch;
125#[cfg(test)]
126use self::precomputed_batch::{copy_code_block_coefficients, downcast_i64_coefficients_to_i32};
127mod prepared_packets;
128use self::prepared_packets::{
129 encode_prepared_resolution_packets_for_session,
130 encode_prepared_resolution_packets_layered_for_session,
131};
132mod packet_plan;
133use self::packet_plan::{
134 count_compact_code_blocks, ordered_prepared_resolution_packets_for_session,
135 packet_descriptors_for_order_for_session, packetization_requires_scalar,
136 packetize_resolution_packets_with_options_for_session, public_packetization_progression_order,
137 split_component_resolution_packets_by_precinct_for_session,
138};
139mod rate_control;
140#[cfg(test)]
141use self::rate_control::{
142 assign_classic_segment_layers_by_slope, assign_ht_segment_layers_by_budget,
143 ht_layer_contributions,
144};
145use self::rate_control::{
146 assign_classic_segment_layers_by_slope_accounted, assign_ht_segment_layers_by_budget_accounted,
147 classic_layer_contributions_accounted, classic_multilayer_code_block_style,
148 classic_unbudgeted_segment_layers_accounted, enforce_classic_segment_layer_monotonicity,
149 enforce_ht_segment_layer_monotonicity, ht_layer_contributions_accounted, ht_segment_count,
150 ht_segment_rate, ht_unbudgeted_segment_layers_accounted, ClassicSegmentAssignmentCandidate,
151 ClassicSegmentLocation, HtSegmentAssignmentCandidate, HtSegmentLocation, LayeredPreparedBlock,
152 LayeredPreparedPacket, LayeredPreparedSubband,
153};
154mod roi_plan;
155use self::roi_plan::{
156 max_total_bitplanes_for_components, roi_subband_scale,
157 validate_roi_encode_options_nonallocating, ComponentRoiEncodePlan, ComponentRoiEncodeRegion,
158};
159mod samples;
160use self::samples::{
161 native_samples_equal, raw_pixel_bytes_per_sample, read_le_sample_value, sign_extend_sample,
162};
163mod single_tile;
164use self::single_tile::ownership::{cpu_dwt_transient_bytes, dwt_decompositions_retained_bytes};
165use self::single_tile::{
166 encode_impl, encode_precomputed_53_single_tile, encode_precomputed_97_single_tile,
167 encode_resident_impl,
168};
169mod subband;
170#[cfg(test)]
171use self::subband::prepare_subband;
172use self::subband::{
173 prepare_subband_for_session, F32SubbandEncodeRequest, I64SubbandEncodeSettings,
174};
175mod tier1_allocation;
176mod tier1_driver;
177use self::tier1_driver::encode_prepared_subbands_for_session;
178#[cfg(test)]
179use self::tier1_driver::{
180 encode_all_ht_code_blocks_parallel, encode_all_ht_code_blocks_serial_cpu,
181 encode_prepared_subbands,
182};
183mod transform;
184#[cfg(test)]
185use self::transform::forward_dwt53_output_from_decomposition;
186use self::transform::{
187 adjust_component_step_sizes_for_guard_delta, adjust_reversible_step_sizes_for_guard_delta,
188 encode_forward_dwt, forward_dwt53_output_retained_bytes,
189 reversible_guard_bits_for_marker_limit, try_component_plane_to_f32_for_session,
190 try_encode_forward_ict, try_encode_forward_rct, try_forward_dwt53_output_from_decomposition,
191 validate_band_len, validate_component_sample_info, validate_deinterleaved_components,
192 ForwardDwtRequest,
193};
194mod typed_i64;
195use self::typed_i64::encode_typed_component_planes_53_i64;
196mod typed_components;
197use self::typed_components::encode_typed_component_planes_53_for_session;
198
199pub fn encode(
218 pixels: &[u8],
219 width: u32,
220 height: u32,
221 num_components: u16,
222 bit_depth: u8,
223 signed: bool,
224 options: &EncodeOptions,
225) -> crate::EncodeResult<Vec<u8>> {
226 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
227 encode_with_accelerator(
228 pixels,
229 width,
230 height,
231 num_components,
232 bit_depth,
233 signed,
234 options,
235 &mut accelerator,
236 )
237}
238
239#[doc(hidden)]
245#[expect(
246 clippy::too_many_arguments,
247 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
248)]
249pub fn encode_with_accelerator(
250 pixels: &[u8],
251 width: u32,
252 height: u32,
253 num_components: u16,
254 bit_depth: u8,
255 signed: bool,
256 options: &EncodeOptions,
257 accelerator: &mut impl J2kEncodeStageAccelerator,
258) -> crate::EncodeResult<Vec<u8>> {
259 encode_with_accelerator_and_retained_input(
260 pixels,
261 width,
262 height,
263 num_components,
264 bit_depth,
265 signed,
266 options,
267 NativeEncodeRetainedInput::none(),
268 accelerator,
269 )
270}
271
272#[doc(hidden)]
278pub fn encode_resident_htj2k_with_accelerator(
279 input: J2kResidentEncodeInput,
280 options: &EncodeOptions,
281 accelerator: &mut impl J2kEncodeStageAccelerator,
282) -> Result<Vec<u8>, ResidentHtj2kEncodeError> {
283 encode_resident_impl(input, options, block_coding_mode(options), accelerator)
284}
285
286#[expect(
287 clippy::too_many_arguments,
288 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
289)]
290fn encode_with_accelerator_and_component_sample_info_for_session(
291 pixels: &[u8],
292 width: u32,
293 height: u32,
294 num_components: u16,
295 bit_depth: u8,
296 signed: bool,
297 options: &EncodeOptions,
298 component_sample_info: &[EncodeComponentSampleInfo],
299 session: &NativeEncodeSession<'_>,
300 accelerator: &mut impl J2kEncodeStageAccelerator,
301) -> crate::EncodeResult<Vec<u8>> {
302 let block_coding_mode = block_coding_mode(options);
303 encode_with_accelerator_and_mode_for_session(
304 pixels,
305 width,
306 height,
307 num_components,
308 bit_depth,
309 signed,
310 options,
311 component_sample_info,
312 block_coding_mode,
313 session,
314 accelerator,
315 )
316}
317
318#[expect(
319 clippy::too_many_arguments,
320 reason = "this internal mode boundary keeps caller geometry and validated coding policy explicit"
321)]
322fn encode_with_accelerator_and_mode_for_session(
323 pixels: &[u8],
324 width: u32,
325 height: u32,
326 num_components: u16,
327 bit_depth: u8,
328 signed: bool,
329 options: &EncodeOptions,
330 component_sample_info: &[EncodeComponentSampleInfo],
331 block_coding_mode: BlockCodingMode,
332 session: &NativeEncodeSession<'_>,
333 accelerator: &mut impl J2kEncodeStageAccelerator,
334) -> crate::EncodeResult<Vec<u8>> {
335 let codestream = encode_impl(
336 pixels,
337 width,
338 height,
339 num_components,
340 bit_depth,
341 signed,
342 options,
343 block_coding_mode,
344 &[],
345 component_sample_info,
346 session,
347 accelerator,
348 )
349 .map_err(NativeEncodePipelineError::into_encode_error)?;
350
351 if block_coding_mode == BlockCodingMode::HighThroughput
352 && options.validate_high_throughput_codestream
353 {
354 validate_htj2k_codestream(
355 &codestream,
356 codestream.capacity(),
357 pixels,
358 width,
359 height,
360 num_components,
361 bit_depth,
362 signed,
363 options.reversible,
364 )?;
365 }
366
367 Ok(codestream)
368}
369
370#[expect(
381 clippy::too_many_arguments,
382 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
383)]
384pub fn encode_with_roi_regions(
385 pixels: &[u8],
386 width: u32,
387 height: u32,
388 num_components: u16,
389 bit_depth: u8,
390 signed: bool,
391 options: &EncodeOptions,
392 roi_regions: &[EncodeRoiRegion],
393) -> crate::EncodeResult<Vec<u8>> {
394 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
395 encode_with_accelerator_and_roi_regions(
396 pixels,
397 width,
398 height,
399 num_components,
400 bit_depth,
401 signed,
402 options,
403 roi_regions,
404 &mut accelerator,
405 )
406}
407
408#[doc(hidden)]
410#[expect(
411 clippy::too_many_arguments,
412 reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
413)]
414pub fn encode_with_accelerator_and_roi_regions(
415 pixels: &[u8],
416 width: u32,
417 height: u32,
418 num_components: u16,
419 bit_depth: u8,
420 signed: bool,
421 options: &EncodeOptions,
422 roi_regions: &[EncodeRoiRegion],
423 accelerator: &mut impl J2kEncodeStageAccelerator,
424) -> crate::EncodeResult<Vec<u8>> {
425 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
426 let block_coding_mode = block_coding_mode(options);
427 let codestream = encode_impl(
428 pixels,
429 width,
430 height,
431 num_components,
432 bit_depth,
433 signed,
434 options,
435 block_coding_mode,
436 roi_regions,
437 &[],
438 &session,
439 accelerator,
440 )
441 .map_err(NativeEncodePipelineError::into_encode_error)?;
442
443 if block_coding_mode == BlockCodingMode::HighThroughput
444 && options.validate_high_throughput_codestream
445 {
446 validate_htj2k_codestream(
447 &codestream,
448 codestream.capacity(),
449 pixels,
450 width,
451 height,
452 num_components,
453 bit_depth,
454 signed,
455 options.reversible,
456 )?;
457 }
458
459 Ok(codestream)
460}
461
462pub fn encode_htj2k(
471 pixels: &[u8],
472 width: u32,
473 height: u32,
474 num_components: u16,
475 bit_depth: u8,
476 signed: bool,
477 options: &EncodeOptions,
478) -> crate::EncodeResult<Vec<u8>> {
479 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
480 let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
481 encode_with_accelerator_and_mode_for_session(
482 pixels,
483 width,
484 height,
485 num_components,
486 bit_depth,
487 signed,
488 options,
489 &[],
490 BlockCodingMode::HighThroughput,
491 &session,
492 &mut accelerator,
493 )
494}
495
496pub fn encode_component_planes_53(
508 planes: &[EncodeComponentPlane<'_>],
509 width: u32,
510 height: u32,
511 bit_depth: u8,
512 signed: bool,
513 options: &EncodeOptions,
514) -> crate::EncodeResult<Vec<u8>> {
515 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
516 let requested_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
517 planes.len(),
518 "component-plane typed descriptor owners",
519 )?;
520 session.checked_phase(requested_bytes, "component-plane typed descriptor owners")?;
521 let mut typed_planes = Vec::new();
522 typed_planes.try_reserve_exact(planes.len()).map_err(|_| {
523 host_allocation_failed("component-plane typed descriptor owners", requested_bytes)
524 })?;
525 typed_planes.extend(planes.iter().map(|plane| EncodeTypedComponentPlane {
526 data: plane.data,
527 x_rsiz: plane.x_rsiz,
528 y_rsiz: plane.y_rsiz,
529 bit_depth,
530 signed,
531 }));
532 let actual_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
533 typed_planes.capacity(),
534 "component-plane typed descriptor owners",
535 )?;
536 let typed_session = session.checked_child_session(
537 &typed_planes,
538 actual_bytes,
539 "component-plane typed descriptor owners",
540 )?;
541 encode_typed_component_planes_53_for_session(
542 &typed_planes,
543 width,
544 height,
545 options,
546 &typed_session,
547 )
548 .map_err(NativeEncodePipelineError::into_encode_error)
549}
550
551pub fn encode_typed_component_planes_53(
564 planes: &[EncodeTypedComponentPlane<'_>],
565 width: u32,
566 height: u32,
567 options: &EncodeOptions,
568) -> crate::EncodeResult<Vec<u8>> {
569 let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
570 encode_typed_component_planes_53_for_session(planes, width, height, options, &session)
571 .map_err(NativeEncodePipelineError::into_encode_error)
572}
573
574fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
578 if options.use_ht_block_coding {
579 BlockCodingMode::HighThroughput
580 } else {
581 BlockCodingMode::Classic
582 }
583}
584
585fn ht_target_coding_passes_for_options(
586 options: &EncodeOptions,
587 block_coding_mode: BlockCodingMode,
588) -> u8 {
589 if block_coding_mode == BlockCodingMode::HighThroughput && options.num_layers > 1 {
590 options.num_layers.min(3)
591 } else {
592 1
593 }
594}
595
596enum PreparedCodeBlockCoefficients {
597 I32(Vec<i32>),
598 I64(Vec<i64>),
599 Empty,
600}
601
602#[cfg(test)]
603impl PreparedCodeBlockCoefficients {
604 fn is_empty(&self) -> bool {
605 match self {
606 Self::I32(values) => values.is_empty(),
607 Self::I64(values) => values.is_empty(),
608 Self::Empty => true,
609 }
610 }
611}
612
613struct PreparedEncodeCodeBlock {
614 coefficients: PreparedCodeBlockCoefficients,
615 width: u32,
616 height: u32,
617}
618
619struct PreparedEncodeSubband {
620 code_blocks: Vec<PreparedEncodeCodeBlock>,
621 preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
622 num_cbs_x: u32,
623 num_cbs_y: u32,
624 code_block_width: u32,
625 code_block_height: u32,
626 width: u32,
627 height: u32,
628 sub_band_type: SubBandType,
629 total_bitplanes: u8,
630 block_coding_mode: BlockCodingMode,
631 ht_target_coding_passes: u8,
632}
633
634struct PreparedResolutionPacket {
635 component: u16,
636 resolution: u32,
637 precinct: u64,
638 subbands: Vec<PreparedEncodeSubband>,
639}
640
641struct PreparedCompactCodeBlock<'a> {
642 data: &'a [u8],
643 cleanup_length: u32,
644 refinement_length: u32,
645 num_coding_passes: u8,
646 num_zero_bitplanes: u8,
647}
648
649struct PreparedCompactSubband<'a> {
650 code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
651 num_cbs_x: u32,
652 num_cbs_y: u32,
653}
654
655struct PreparedCompactResolutionPacket<'a> {
656 component: u16,
657 resolution: u32,
658 precinct: u64,
659 subbands: Vec<PreparedCompactSubband<'a>>,
660}
661
662#[cfg(test)]
663#[path = "encode_tests.rs"]
664mod tests;