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