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