Skip to main content

j2k_native/j2c/
encode.rs

1//! Top-level JPEG 2000 encode orchestration.
2//!
3//! Coordinates the full encoding pipeline:
4//!   pixels → MCT → DWT → quantize → EBCOT T1 → T2 → codestream
5//!
6//! Supports both lossless (5-3 reversible) and lossy (9-7 irreversible) encoding.
7
8use 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;
52// Classic packet headers can signal at most 164 coding passes, i.e.
53// 1 cleanup pass for the first bitplane plus 3 passes for each additional
54// bitplane: 1 + 3 * (55 - 1) = 163.
55const 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
203/// Encode pixel data into a JPEG 2000 codestream.
204///
205/// # Arguments
206/// * `pixels` — Raw pixel data. For 8-bit: one byte per sample. For >8-bit: two bytes per sample (little-endian u16).
207/// * `width` — Image width in pixels.
208/// * `height` — Image height in pixels.
209/// * `num_components` — Number of components (1 for grayscale, 3 for RGB).
210/// * `bit_depth` — Bits per sample (e.g., 8, 12, 16).
211/// * `signed` — Whether samples are signed.
212/// * `options` — Encoding parameters.
213///
214/// # Returns
215/// The encoded JPEG 2000 codestream bytes (`.j2c` format).
216///
217/// # Errors
218///
219/// Returns an error when dimensions, sample data, component metadata, or
220/// encoding options are invalid, or when a codec stage cannot encode them.
221pub 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/// Encode pixel data into a JPEG 2000 codestream using optional encode-stage hooks.
244///
245/// Stage hooks may accelerate forward RCT, forward 5/3 DWT, Tier-1 code-block
246/// encode, and packetization. Returning fallback from a hook preserves the CPU
247/// baseline for that stage.
248#[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/// Encode a complete HTJ2K tile whose input pixels remain backend-resident.
277///
278/// This implementation-facing entry point reuses native request planning and
279/// codestream finalization, but has no CPU fallback because no host samples are
280/// present. A declined resident hook is returned as an explicit error.
281#[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/// Encode pixel data into a JPEG 2000 codestream with rectangular ROI maxshift.
375///
376/// This uses the normal native encoder pipeline. Non-empty `roi_regions`
377/// produce RGN markers and shift selected quantized coefficients before
378/// code-block encoding.
379///
380/// # Errors
381///
382/// Returns an error for invalid image/sample metadata, invalid ROI regions or
383/// options, or a failure in any codec stage.
384#[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/// Encode pixel data with rectangular ROI maxshift and optional stage hooks.
413#[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
466/// Encode pixel data into an HTJ2K codestream.
467///
468/// Lossless HTJ2K output is self-validated before it is returned.
469///
470/// # Errors
471///
472/// Returns an error when the input or options are invalid, encoding fails, or
473/// the requested output fails HTJ2K self-validation.
474pub 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
500/// Encode reversible 5/3 component planes into a classic J2K or HTJ2K
501/// codestream.
502///
503/// Plane buffers are supplied at each component's own SIZ sampling grid. Set
504/// [`EncodeOptions::use_ht_block_coding`] to select HTJ2K block coding; the
505/// default writes classic Part 1 block coding.
506///
507/// # Errors
508///
509/// Returns an error for invalid component geometry, sampling, sample buffers,
510/// or options, or when a codec stage fails.
511pub 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
555/// Encode reversible 5/3 typed component planes into a classic J2K or HTJ2K
556/// codestream.
557///
558/// This is the component-plane entry point for JPEG 2000 codestreams whose
559/// components have different precision or signedness. Plane buffers are
560/// supplied at each component's own SIZ sampling grid. Components are encoded
561/// without a reversible color transform.
562///
563/// # Errors
564///
565/// Returns an error for invalid component count, dimensions, sampling,
566/// precision, sample buffers, or options, or when a codec stage fails.
567pub 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
578/// Encode precomputed reversible 5/3 wavelet coefficients into a classic
579/// JPEG 2000 Part 1 codestream.
580///
581fn 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;