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 an irreversible HTJ2K codestream using OpenHTJ2K-compatible
244/// Qfactor quantization.
245///
246/// Qfactor is an opt-in visual quantization profile for grayscale or
247/// three-component RGB input. It changes the expounded QCD/QCC step tuples;
248/// it is not a byte-rate target and is not signaled separately in the
249/// codestream.
250///
251/// # Errors
252///
253/// Returns an error unless `qfactor` is in `1..=100`, irreversible HT block
254/// coding is selected, and the input is grayscale or three-component RGB
255/// with the multi-component transform enabled.
256#[expect(
257    clippy::too_many_arguments,
258    reason = "this codec boundary keeps geometry, sample representation, and quantization policy explicit"
259)]
260pub fn encode_htj2k_with_qfactor(
261    pixels: &[u8],
262    width: u32,
263    height: u32,
264    num_components: u16,
265    bit_depth: u8,
266    signed: bool,
267    qfactor: u8,
268    options: &EncodeOptions,
269) -> crate::EncodeResult<Vec<u8>> {
270    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
271    encode_htj2k_with_qfactor_and_accelerator(
272        pixels,
273        width,
274        height,
275        num_components,
276        bit_depth,
277        signed,
278        qfactor,
279        options,
280        &mut accelerator,
281    )
282}
283
284/// Accelerator-aware counterpart to [`encode_htj2k_with_qfactor`].
285#[doc(hidden)]
286#[expect(
287    clippy::too_many_arguments,
288    reason = "this codec boundary keeps geometry, sample representation, quantization policy, and accelerator explicit"
289)]
290pub fn encode_htj2k_with_qfactor_and_accelerator(
291    pixels: &[u8],
292    width: u32,
293    height: u32,
294    num_components: u16,
295    bit_depth: u8,
296    signed: bool,
297    qfactor: u8,
298    options: &EncodeOptions,
299    accelerator: &mut impl J2kEncodeStageAccelerator,
300) -> crate::EncodeResult<Vec<u8>> {
301    validate_openhtj2k_qfactor_request(qfactor, num_components, options)?;
302    let session = NativeEncodeSession::try_new_with_openhtj2k_qfactor(
303        NativeEncodeRetainedInput::none(),
304        qfactor,
305    )?;
306    let component_sample_info = [EncodeComponentSampleInfo { bit_depth, signed }; 3];
307    encode_with_accelerator_and_component_sample_info_for_session(
308        pixels,
309        width,
310        height,
311        num_components,
312        bit_depth,
313        signed,
314        options,
315        &component_sample_info[..usize::from(num_components)],
316        &session,
317        accelerator,
318    )
319}
320
321fn validate_openhtj2k_qfactor_request(
322    qfactor: u8,
323    num_components: u16,
324    options: &EncodeOptions,
325) -> crate::EncodeResult<()> {
326    if !(1..=100).contains(&qfactor) {
327        return Err(crate::EncodeError::InvalidInput {
328            what: "OpenHTJ2K Qfactor must be in 1..=100",
329        });
330    }
331    if options.reversible || !options.use_ht_block_coding {
332        return Err(crate::EncodeError::InvalidInput {
333            what: "OpenHTJ2K Qfactor requires irreversible HT block coding",
334        });
335    }
336    if options.guard_bits != 1 {
337        return Err(crate::EncodeError::InvalidInput {
338            what: "OpenHTJ2K Qfactor requires one quantization guard bit",
339        });
340    }
341    if !matches!(num_components, 1 | 3) || (num_components == 3 && !options.use_mct) {
342        return Err(crate::EncodeError::InvalidInput {
343            what: "OpenHTJ2K Qfactor requires grayscale or three-component RGB with MCT",
344        });
345    }
346    Ok(())
347}
348
349/// Encode pixel data into a JPEG 2000 codestream using optional encode-stage hooks.
350///
351/// Stage hooks may accelerate forward RCT, forward 5/3 DWT, Tier-1 code-block
352/// encode, and packetization. Returning fallback from a hook preserves the CPU
353/// baseline for that stage.
354#[doc(hidden)]
355#[expect(
356    clippy::too_many_arguments,
357    reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
358)]
359pub fn encode_with_accelerator(
360    pixels: &[u8],
361    width: u32,
362    height: u32,
363    num_components: u16,
364    bit_depth: u8,
365    signed: bool,
366    options: &EncodeOptions,
367    accelerator: &mut impl J2kEncodeStageAccelerator,
368) -> crate::EncodeResult<Vec<u8>> {
369    encode_with_accelerator_and_retained_input(
370        pixels,
371        width,
372        height,
373        num_components,
374        bit_depth,
375        signed,
376        options,
377        NativeEncodeRetainedInput::none(),
378        accelerator,
379    )
380}
381
382/// Encode a complete HTJ2K tile whose input pixels remain backend-resident.
383///
384/// This implementation-facing entry point reuses native request planning and
385/// codestream finalization, but has no CPU fallback because no host samples are
386/// present. A declined resident hook is returned as an explicit error.
387#[doc(hidden)]
388pub fn encode_resident_htj2k_with_accelerator(
389    input: J2kResidentEncodeInput,
390    options: &EncodeOptions,
391    accelerator: &mut impl J2kEncodeStageAccelerator,
392) -> Result<Vec<u8>, ResidentHtj2kEncodeError> {
393    encode_resident_impl(input, options, block_coding_mode(options), accelerator)
394}
395
396#[expect(
397    clippy::too_many_arguments,
398    reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
399)]
400fn encode_with_accelerator_and_component_sample_info_for_session(
401    pixels: &[u8],
402    width: u32,
403    height: u32,
404    num_components: u16,
405    bit_depth: u8,
406    signed: bool,
407    options: &EncodeOptions,
408    component_sample_info: &[EncodeComponentSampleInfo],
409    session: &NativeEncodeSession<'_>,
410    accelerator: &mut impl J2kEncodeStageAccelerator,
411) -> crate::EncodeResult<Vec<u8>> {
412    let block_coding_mode = block_coding_mode(options);
413    encode_with_accelerator_and_mode_for_session(
414        pixels,
415        width,
416        height,
417        num_components,
418        bit_depth,
419        signed,
420        options,
421        component_sample_info,
422        block_coding_mode,
423        session,
424        accelerator,
425    )
426}
427
428#[expect(
429    clippy::too_many_arguments,
430    reason = "this internal mode boundary keeps caller geometry and validated coding policy explicit"
431)]
432fn encode_with_accelerator_and_mode_for_session(
433    pixels: &[u8],
434    width: u32,
435    height: u32,
436    num_components: u16,
437    bit_depth: u8,
438    signed: bool,
439    options: &EncodeOptions,
440    component_sample_info: &[EncodeComponentSampleInfo],
441    block_coding_mode: BlockCodingMode,
442    session: &NativeEncodeSession<'_>,
443    accelerator: &mut impl J2kEncodeStageAccelerator,
444) -> crate::EncodeResult<Vec<u8>> {
445    let codestream = encode_impl(
446        pixels,
447        width,
448        height,
449        num_components,
450        bit_depth,
451        signed,
452        options,
453        block_coding_mode,
454        &[],
455        component_sample_info,
456        session,
457        accelerator,
458    )
459    .map_err(NativeEncodePipelineError::into_encode_error)?;
460
461    if block_coding_mode == BlockCodingMode::HighThroughput
462        && options.validate_high_throughput_codestream
463    {
464        validate_htj2k_codestream(
465            &codestream,
466            codestream.capacity(),
467            pixels,
468            width,
469            height,
470            num_components,
471            bit_depth,
472            signed,
473            options.reversible,
474        )?;
475    }
476
477    Ok(codestream)
478}
479
480/// Encode pixel data into a JPEG 2000 codestream with rectangular ROI maxshift.
481///
482/// This uses the normal native encoder pipeline. Non-empty `roi_regions`
483/// produce RGN markers and shift selected quantized coefficients before
484/// code-block encoding.
485///
486/// # Errors
487///
488/// Returns an error for invalid image/sample metadata, invalid ROI regions or
489/// options, or a failure in any codec stage.
490#[expect(
491    clippy::too_many_arguments,
492    reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
493)]
494pub fn encode_with_roi_regions(
495    pixels: &[u8],
496    width: u32,
497    height: u32,
498    num_components: u16,
499    bit_depth: u8,
500    signed: bool,
501    options: &EncodeOptions,
502    roi_regions: &[EncodeRoiRegion],
503) -> crate::EncodeResult<Vec<u8>> {
504    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
505    encode_with_accelerator_and_roi_regions(
506        pixels,
507        width,
508        height,
509        num_components,
510        bit_depth,
511        signed,
512        options,
513        roi_regions,
514        &mut accelerator,
515    )
516}
517
518/// Encode pixel data with rectangular ROI maxshift and optional stage hooks.
519#[doc(hidden)]
520#[expect(
521    clippy::too_many_arguments,
522    reason = "this codec boundary keeps geometry, state buffers, and validated options explicit without allocation or indirection"
523)]
524pub fn encode_with_accelerator_and_roi_regions(
525    pixels: &[u8],
526    width: u32,
527    height: u32,
528    num_components: u16,
529    bit_depth: u8,
530    signed: bool,
531    options: &EncodeOptions,
532    roi_regions: &[EncodeRoiRegion],
533    accelerator: &mut impl J2kEncodeStageAccelerator,
534) -> crate::EncodeResult<Vec<u8>> {
535    let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
536    let block_coding_mode = block_coding_mode(options);
537    let codestream = encode_impl(
538        pixels,
539        width,
540        height,
541        num_components,
542        bit_depth,
543        signed,
544        options,
545        block_coding_mode,
546        roi_regions,
547        &[],
548        &session,
549        accelerator,
550    )
551    .map_err(NativeEncodePipelineError::into_encode_error)?;
552
553    if block_coding_mode == BlockCodingMode::HighThroughput
554        && options.validate_high_throughput_codestream
555    {
556        validate_htj2k_codestream(
557            &codestream,
558            codestream.capacity(),
559            pixels,
560            width,
561            height,
562            num_components,
563            bit_depth,
564            signed,
565            options.reversible,
566        )?;
567    }
568
569    Ok(codestream)
570}
571
572/// Encode pixel data into an HTJ2K codestream.
573///
574/// Lossless HTJ2K output is self-validated before it is returned.
575///
576/// # Errors
577///
578/// Returns an error when the input or options are invalid, encoding fails, or
579/// the requested output fails HTJ2K self-validation.
580pub fn encode_htj2k(
581    pixels: &[u8],
582    width: u32,
583    height: u32,
584    num_components: u16,
585    bit_depth: u8,
586    signed: bool,
587    options: &EncodeOptions,
588) -> crate::EncodeResult<Vec<u8>> {
589    let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
590    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
591    encode_with_accelerator_and_mode_for_session(
592        pixels,
593        width,
594        height,
595        num_components,
596        bit_depth,
597        signed,
598        options,
599        &[],
600        BlockCodingMode::HighThroughput,
601        &session,
602        &mut accelerator,
603    )
604}
605
606/// Encode reversible 5/3 component planes into a classic J2K or HTJ2K
607/// codestream.
608///
609/// Plane buffers are supplied at each component's own SIZ sampling grid. Set
610/// [`EncodeOptions::use_ht_block_coding`] to select HTJ2K block coding; the
611/// default writes classic Part 1 block coding.
612///
613/// # Errors
614///
615/// Returns an error for invalid component geometry, sampling, sample buffers,
616/// or options, or when a codec stage fails.
617pub fn encode_component_planes_53(
618    planes: &[EncodeComponentPlane<'_>],
619    width: u32,
620    height: u32,
621    bit_depth: u8,
622    signed: bool,
623    options: &EncodeOptions,
624) -> crate::EncodeResult<Vec<u8>> {
625    let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
626    let requested_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
627        planes.len(),
628        "component-plane typed descriptor owners",
629    )?;
630    session.checked_phase(requested_bytes, "component-plane typed descriptor owners")?;
631    let mut typed_planes = Vec::new();
632    typed_planes.try_reserve_exact(planes.len()).map_err(|_| {
633        host_allocation_failed("component-plane typed descriptor owners", requested_bytes)
634    })?;
635    typed_planes.extend(planes.iter().map(|plane| EncodeTypedComponentPlane {
636        data: plane.data,
637        x_rsiz: plane.x_rsiz,
638        y_rsiz: plane.y_rsiz,
639        bit_depth,
640        signed,
641    }));
642    let actual_bytes = checked_element_bytes::<EncodeTypedComponentPlane<'_>>(
643        typed_planes.capacity(),
644        "component-plane typed descriptor owners",
645    )?;
646    let typed_session = session.checked_child_session(
647        &typed_planes,
648        actual_bytes,
649        "component-plane typed descriptor owners",
650    )?;
651    encode_typed_component_planes_53_for_session(
652        &typed_planes,
653        width,
654        height,
655        options,
656        &typed_session,
657    )
658    .map_err(NativeEncodePipelineError::into_encode_error)
659}
660
661/// Encode reversible 5/3 typed component planes into a classic J2K or HTJ2K
662/// codestream.
663///
664/// This is the component-plane entry point for JPEG 2000 codestreams whose
665/// components have different precision or signedness. Plane buffers are
666/// supplied at each component's own SIZ sampling grid. Components are encoded
667/// without a reversible color transform.
668///
669/// # Errors
670///
671/// Returns an error for invalid component count, dimensions, sampling,
672/// precision, sample buffers, or options, or when a codec stage fails.
673pub fn encode_typed_component_planes_53(
674    planes: &[EncodeTypedComponentPlane<'_>],
675    width: u32,
676    height: u32,
677    options: &EncodeOptions,
678) -> crate::EncodeResult<Vec<u8>> {
679    let session = NativeEncodeSession::try_new(NativeEncodeRetainedInput::none())?;
680    encode_typed_component_planes_53_for_session(planes, width, height, options, &session)
681        .map_err(NativeEncodePipelineError::into_encode_error)
682}
683
684/// Encode precomputed reversible 5/3 wavelet coefficients into a classic
685/// JPEG 2000 Part 1 codestream.
686///
687fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
688    if options.use_ht_block_coding {
689        BlockCodingMode::HighThroughput
690    } else {
691        BlockCodingMode::Classic
692    }
693}
694
695fn ht_target_coding_passes_for_options(
696    options: &EncodeOptions,
697    block_coding_mode: BlockCodingMode,
698) -> u8 {
699    if block_coding_mode == BlockCodingMode::HighThroughput
700        && !options.reversible
701        && options.num_layers > 1
702    {
703        options.num_layers.min(3)
704    } else {
705        1
706    }
707}
708
709fn requested_guard_bits(options: &EncodeOptions, use_mct: bool, openhtj2k_qfactor: bool) -> u8 {
710    if openhtj2k_qfactor {
711        1
712    } else if options.reversible && !use_mct {
713        options.guard_bits
714    } else {
715        options.guard_bits.max(2)
716    }
717}
718
719enum PreparedCodeBlockCoefficients {
720    I32(Vec<i32>),
721    I64(Vec<i64>),
722    Empty,
723}
724
725#[cfg(test)]
726impl PreparedCodeBlockCoefficients {
727    fn is_empty(&self) -> bool {
728        match self {
729            Self::I32(values) => values.is_empty(),
730            Self::I64(values) => values.is_empty(),
731            Self::Empty => true,
732        }
733    }
734}
735
736struct PreparedEncodeCodeBlock {
737    coefficients: PreparedCodeBlockCoefficients,
738    width: u32,
739    height: u32,
740}
741
742struct PreparedEncodeSubband {
743    code_blocks: Vec<PreparedEncodeCodeBlock>,
744    preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
745    preencoded_ht_maximum_cleanup_magnitude: Option<u64>,
746    num_cbs_x: u32,
747    num_cbs_y: u32,
748    code_block_width: u32,
749    code_block_height: u32,
750    width: u32,
751    height: u32,
752    sub_band_type: SubBandType,
753    total_bitplanes: u8,
754    block_coding_mode: BlockCodingMode,
755    ht_target_coding_passes: u8,
756}
757
758struct PreparedResolutionPacket {
759    component: u16,
760    resolution: u32,
761    precinct: u64,
762    subbands: Vec<PreparedEncodeSubband>,
763}
764
765struct PreparedCompactCodeBlock<'a> {
766    data: &'a [u8],
767    cleanup_length: u32,
768    refinement_length: u32,
769    num_coding_passes: u8,
770    num_zero_bitplanes: u8,
771}
772
773struct PreparedCompactSubband<'a> {
774    code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
775    num_cbs_x: u32,
776    num_cbs_y: u32,
777}
778
779struct PreparedCompactResolutionPacket<'a> {
780    component: u16,
781    resolution: u32,
782    precinct: u64,
783    subbands: Vec<PreparedCompactSubband<'a>>,
784}
785
786#[cfg(test)]
787#[path = "encode_tests.rs"]
788mod tests;