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;
9use alloc::vec::Vec;
10use core::cmp::Ordering;
11use core::ops::Range;
12
13#[cfg(feature = "parallel")]
14use rayon::prelude::*;
15
16use super::bitplane_encode;
17use super::build::SubBandType;
18use super::codestream::CodeBlockStyle;
19use super::codestream_write::{self, BlockCodingMode, EncodeComponentSampleInfo, EncodeParams};
20use super::fdwt::{self, DwtDecomposition};
21use super::forward_mct;
22use super::ht_block_encode;
23use super::packet_encode::{self, CodeBlockPacketData, ResolutionPacket, SubbandPrecinct};
24pub use super::quantize::irreversible_quantization_step_for_subband;
25use super::quantize::{self, QuantStepSize};
26use crate::math::{floor_f32, log2_f32};
27use crate::profile;
28use crate::{
29    CpuOnlyJ2kEncodeStageAccelerator, EncodedHtJ2kCodeBlock, EncodedJ2kCodeBlock,
30    IrreversibleQuantizationSubbandScales, J2kDeinterleaveToF32Job, J2kEncodeStageAccelerator,
31    J2kForwardDwt53Job, J2kForwardDwt53Level, J2kForwardDwt53Output, J2kForwardDwt97Job,
32    J2kForwardDwt97Level, J2kForwardDwt97Output, J2kForwardIctJob, J2kForwardRctJob,
33    J2kHtSubbandEncodeJob, J2kHtj2kTileEncodeJob, J2kPacketizationBlockCodingMode,
34    J2kPacketizationCodeBlock, J2kPacketizationEncodeJob, J2kPacketizationPacketDescriptor,
35    J2kPacketizationResolution, J2kPacketizationSubband, J2kQuantizeSubbandJob, J2kSubBandType,
36    J2kTier1CodeBlockEncodeJob, PrecomputedHtj2k53Component, PrecomputedHtj2k53Image,
37    PrecomputedHtj2k97Component, PrecomputedHtj2k97Image, PreencodedHtj2k97CodeBlock,
38    PreencodedHtj2k97CompactCodeBlock, PreencodedHtj2k97CompactComponent,
39    PreencodedHtj2k97CompactImage, PreencodedHtj2k97CompactResolution,
40    PreencodedHtj2k97CompactSubband, PreencodedHtj2k97Component, PreencodedHtj2k97Image,
41    PreencodedHtj2k97Resolution, PreencodedHtj2k97Subband, PrequantizedHtj2k97Component,
42    PrequantizedHtj2k97Image, PrequantizedHtj2k97Resolution, PrequantizedHtj2k97Subband,
43    MAX_J2K_SPEC_COMPONENTS,
44};
45use crate::{DecodeSettings, Image};
46
47const HT_CPU_PARALLEL_FALLBACK_MIN_JOBS: usize = 4;
48const MAX_RAW_PIXEL_ENCODE_BIT_DEPTH: u8 = 24;
49const MAX_PART1_SAMPLE_BIT_DEPTH: u8 = 38;
50const MAX_REVERSIBLE_NO_QUANT_EXPONENT: u16 = 31;
51const MAX_REVERSIBLE_NO_QUANT_GUARD_BITS: u8 = 7;
52const MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES: u16 =
53    MAX_REVERSIBLE_NO_QUANT_GUARD_BITS as u16 + MAX_REVERSIBLE_NO_QUANT_EXPONENT - 1;
54// Classic packet headers can signal at most 164 coding passes, i.e.
55// 1 cleanup pass for the first bitplane plus 3 passes for each additional
56// bitplane: 1 + 3 * (55 - 1) = 163.
57const MAX_CLASSIC_ROI_CODED_BITPLANES: u8 = 55;
58const MAX_HT_ROI_CODED_BITPLANES: u8 = 31;
59
60/// Encoding options for JPEG 2000.
61#[derive(Debug, Clone)]
62pub struct EncodeOptions {
63    /// Number of decomposition levels (default: 5).
64    pub num_decomposition_levels: u8,
65    /// Use reversible (lossless) transform (default: true).
66    pub reversible: bool,
67    /// Code-block width exponent minus 2 (default: 4, meaning 2^6=64).
68    pub code_block_width_exp: u8,
69    /// Code-block height exponent minus 2 (default: 4, meaning 2^6=64).
70    pub code_block_height_exp: u8,
71    /// Number of guard bits (default: 1 for reversible, 2 for irreversible).
72    pub guard_bits: u8,
73    /// Encode using HT block coding (HTJ2K / Part 15) instead of classic EBCOT.
74    pub use_ht_block_coding: bool,
75    /// Packet progression order to write in COD and use for packetization.
76    pub progression_order: EncodeProgressionOrder,
77    /// Write a TLM marker segment for the single tile-part.
78    pub write_tlm: bool,
79    /// Write PLT packet-length marker segments in the tile-part header.
80    pub write_plt: bool,
81    /// Write PLM packet-length marker segments in the main header.
82    pub write_plm: bool,
83    /// Write PPM packed packet-header marker segments in the main header.
84    pub write_ppm: bool,
85    /// Write PPT packed packet-header marker segments in tile-part headers.
86    pub write_ppt: bool,
87    /// Write SOP marker segments before packets.
88    pub write_sop: bool,
89    /// Write EPH markers after packet headers.
90    pub write_eph: bool,
91    /// Apply the JPEG 2000 multi-component color transform for 3+ component inputs.
92    pub use_mct: bool,
93    /// Number of cumulative quality layers to emit.
94    pub num_layers: u8,
95    /// Optional cumulative packet-body byte targets for each quality layer.
96    pub quality_layer_byte_targets: Vec<u64>,
97    /// Decode and verify HTJ2K codestreams inside the native encoder.
98    pub validate_high_throughput_codestream: bool,
99    /// Multiplier applied to irreversible 9/7 scalar quantization step sizes.
100    ///
101    /// `1.0` preserves the near-lossless default step sizes. Larger values
102    /// produce smaller codestreams by coarsening quantization.
103    pub irreversible_quantization_scale: f32,
104    /// Per-subband multipliers applied on top of
105    /// `irreversible_quantization_scale`.
106    pub irreversible_quantization_subband_scales: IrreversibleQuantizationSubbandScales,
107    /// Optional per-component SIZ sampling factors (`XRsiz`, `YRsiz`).
108    ///
109    /// `None` means every component is stored at the reference-grid
110    /// resolution. This is experimental and primarily intended for precomputed
111    /// coefficient encoders that preserve JPEG-native chroma subsampling.
112    pub component_sampling: Option<Vec<(u8, u8)>>,
113    /// Optional per-component whole-component ROI maxshift values.
114    ///
115    /// Non-zero entries emit RGN markers and encode every coefficient in that
116    /// component with the requested maxshift. Rectangular ROI authoring is not
117    /// represented by this field.
118    pub roi_component_shifts: Vec<u8>,
119    /// Optional tile width and height for multi-tile codestream output.
120    pub tile_size: Option<(u32, u32)>,
121    /// Optional maximum number of complete packets to place in each tile-part.
122    pub tile_part_packet_limit: Option<u16>,
123    /// Optional precinct exponents in COD order, one per resolution level.
124    pub precinct_exponents: Vec<(u8, u8)>,
125}
126
127/// Borrowed component-plane samples for reversible 5/3 component-plane encode.
128#[derive(Debug, Clone, Copy)]
129pub struct EncodeComponentPlane<'a> {
130    /// Row-major little-endian component samples at this component's own grid.
131    pub data: &'a [u8],
132    /// Horizontal SIZ sampling factor (`XRsiz`).
133    pub x_rsiz: u8,
134    /// Vertical SIZ sampling factor (`YRsiz`).
135    pub y_rsiz: u8,
136}
137
138/// Borrowed component-plane samples with per-component precision metadata.
139#[derive(Debug, Clone, Copy)]
140pub struct EncodeTypedComponentPlane<'a> {
141    /// Row-major little-endian component samples at this component's own grid.
142    pub data: &'a [u8],
143    /// Horizontal SIZ sampling factor (`XRsiz`).
144    pub x_rsiz: u8,
145    /// Vertical SIZ sampling factor (`YRsiz`).
146    pub y_rsiz: u8,
147    /// Significant bits per sample for this component.
148    pub bit_depth: u8,
149    /// Whether samples in this component are signed.
150    pub signed: bool,
151}
152
153/// Rectangular region-of-interest request for JPEG 2000 maxshift encoding.
154///
155/// The rectangle is expressed in full-resolution reference-grid pixels. For
156/// sampled components, the encoder maps the rectangle to that component's SIZ
157/// grid before selecting wavelet coefficients. All regions for the same
158/// component must use the same non-zero `shift`, because JPEG 2000 RGN stores
159/// one maxshift value per component.
160#[derive(Debug, Clone, Copy)]
161pub struct EncodeRoiRegion {
162    /// Component index to which the ROI applies.
163    pub component: u16,
164    /// Left edge in reference-grid pixels.
165    pub x: u32,
166    /// Top edge in reference-grid pixels.
167    pub y: u32,
168    /// Width in reference-grid pixels.
169    pub width: u32,
170    /// Height in reference-grid pixels.
171    pub height: u32,
172    /// Maxshift value to write in the component's RGN marker.
173    pub shift: u8,
174}
175
176impl Default for EncodeOptions {
177    fn default() -> Self {
178        Self {
179            num_decomposition_levels: 5,
180            reversible: true,
181            code_block_width_exp: 4,
182            code_block_height_exp: 4,
183            guard_bits: 1,
184            use_ht_block_coding: false,
185            progression_order: EncodeProgressionOrder::Lrcp,
186            write_tlm: false,
187            write_plt: false,
188            write_plm: false,
189            write_ppm: false,
190            write_ppt: false,
191            write_sop: false,
192            write_eph: false,
193            use_mct: true,
194            num_layers: 1,
195            quality_layer_byte_targets: Vec::new(),
196            validate_high_throughput_codestream: true,
197            irreversible_quantization_scale: 1.0,
198            irreversible_quantization_subband_scales:
199                IrreversibleQuantizationSubbandScales::default(),
200            component_sampling: None,
201            roi_component_shifts: Vec::new(),
202            tile_size: None,
203            tile_part_packet_limit: None,
204            precinct_exponents: Vec::new(),
205        }
206    }
207}
208
209/// JPEG 2000 packet progression orders supported by the encoder.
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
211pub enum EncodeProgressionOrder {
212    /// Layer-resolution-component-position progression.
213    #[default]
214    Lrcp,
215    /// Resolution-layer-component-position progression.
216    Rlcp,
217    /// Resolution-position-component-layer progression.
218    Rpcl,
219    /// Position-component-resolution-layer progression.
220    Pcrl,
221    /// Component-position-resolution-layer progression.
222    Cprl,
223}
224
225/// Encode pixel data into a JPEG 2000 codestream.
226///
227/// # Arguments
228/// * `pixels` — Raw pixel data. For 8-bit: one byte per sample. For >8-bit: two bytes per sample (little-endian u16).
229/// * `width` — Image width in pixels.
230/// * `height` — Image height in pixels.
231/// * `num_components` — Number of components (1 for grayscale, 3 for RGB).
232/// * `bit_depth` — Bits per sample (e.g., 8, 12, 16).
233/// * `signed` — Whether samples are signed.
234/// * `options` — Encoding parameters.
235///
236/// # Returns
237/// The encoded JPEG 2000 codestream bytes (`.j2c` format).
238pub fn encode(
239    pixels: &[u8],
240    width: u32,
241    height: u32,
242    num_components: u16,
243    bit_depth: u8,
244    signed: bool,
245    options: &EncodeOptions,
246) -> Result<Vec<u8>, &'static str> {
247    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
248    encode_with_accelerator(
249        pixels,
250        width,
251        height,
252        num_components,
253        bit_depth,
254        signed,
255        options,
256        &mut accelerator,
257    )
258}
259
260/// Encode pixel data into a JPEG 2000 codestream using optional encode-stage hooks.
261///
262/// Stage hooks may accelerate forward RCT, forward 5/3 DWT, Tier-1 code-block
263/// encode, and packetization. Returning fallback from a hook preserves the CPU
264/// baseline for that stage.
265pub fn encode_with_accelerator(
266    pixels: &[u8],
267    width: u32,
268    height: u32,
269    num_components: u16,
270    bit_depth: u8,
271    signed: bool,
272    options: &EncodeOptions,
273    accelerator: &mut impl J2kEncodeStageAccelerator,
274) -> Result<Vec<u8>, &'static str> {
275    encode_with_accelerator_and_component_sample_info(
276        pixels,
277        width,
278        height,
279        num_components,
280        bit_depth,
281        signed,
282        options,
283        &[],
284        accelerator,
285    )
286}
287
288fn encode_with_accelerator_and_component_sample_info(
289    pixels: &[u8],
290    width: u32,
291    height: u32,
292    num_components: u16,
293    bit_depth: u8,
294    signed: bool,
295    options: &EncodeOptions,
296    component_sample_info: &[EncodeComponentSampleInfo],
297    accelerator: &mut impl J2kEncodeStageAccelerator,
298) -> Result<Vec<u8>, &'static str> {
299    let block_coding_mode = block_coding_mode(options);
300    let codestream = encode_impl(
301        pixels,
302        width,
303        height,
304        num_components,
305        bit_depth,
306        signed,
307        options,
308        block_coding_mode,
309        &[],
310        component_sample_info,
311        accelerator,
312    )?;
313
314    if block_coding_mode == BlockCodingMode::HighThroughput
315        && options.validate_high_throughput_codestream
316    {
317        validate_htj2k_codestream(
318            &codestream,
319            pixels,
320            width,
321            height,
322            num_components,
323            bit_depth,
324            signed,
325            options.reversible,
326        )?;
327    }
328
329    Ok(codestream)
330}
331
332/// Encode pixel data into a JPEG 2000 codestream with rectangular ROI maxshift.
333///
334/// This uses the normal native encoder pipeline. Non-empty `roi_regions`
335/// produce RGN markers and shift selected quantized coefficients before
336/// code-block encoding.
337pub fn encode_with_roi_regions(
338    pixels: &[u8],
339    width: u32,
340    height: u32,
341    num_components: u16,
342    bit_depth: u8,
343    signed: bool,
344    options: &EncodeOptions,
345    roi_regions: &[EncodeRoiRegion],
346) -> Result<Vec<u8>, &'static str> {
347    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
348    encode_with_accelerator_and_roi_regions(
349        pixels,
350        width,
351        height,
352        num_components,
353        bit_depth,
354        signed,
355        options,
356        roi_regions,
357        &mut accelerator,
358    )
359}
360
361/// Encode pixel data with rectangular ROI maxshift and optional stage hooks.
362pub fn encode_with_accelerator_and_roi_regions(
363    pixels: &[u8],
364    width: u32,
365    height: u32,
366    num_components: u16,
367    bit_depth: u8,
368    signed: bool,
369    options: &EncodeOptions,
370    roi_regions: &[EncodeRoiRegion],
371    accelerator: &mut impl J2kEncodeStageAccelerator,
372) -> Result<Vec<u8>, &'static str> {
373    let block_coding_mode = block_coding_mode(options);
374    let codestream = encode_impl(
375        pixels,
376        width,
377        height,
378        num_components,
379        bit_depth,
380        signed,
381        options,
382        block_coding_mode,
383        roi_regions,
384        &[],
385        accelerator,
386    )?;
387
388    if block_coding_mode == BlockCodingMode::HighThroughput
389        && options.validate_high_throughput_codestream
390    {
391        validate_htj2k_codestream(
392            &codestream,
393            pixels,
394            width,
395            height,
396            num_components,
397            bit_depth,
398            signed,
399            options.reversible,
400        )?;
401    }
402
403    Ok(codestream)
404}
405
406/// Encode pixel data into an HTJ2K codestream.
407///
408/// Lossless HTJ2K output is self-validated before it is returned.
409pub fn encode_htj2k(
410    pixels: &[u8],
411    width: u32,
412    height: u32,
413    num_components: u16,
414    bit_depth: u8,
415    signed: bool,
416    options: &EncodeOptions,
417) -> Result<Vec<u8>, &'static str> {
418    let mut options = options.clone();
419    options.use_ht_block_coding = true;
420    encode(
421        pixels,
422        width,
423        height,
424        num_components,
425        bit_depth,
426        signed,
427        &options,
428    )
429}
430
431/// Encode reversible 5/3 component planes into a classic J2K or HTJ2K
432/// codestream.
433///
434/// Plane buffers are supplied at each component's own SIZ sampling grid. Set
435/// [`EncodeOptions::use_ht_block_coding`] to select HTJ2K block coding; the
436/// default writes classic Part 1 block coding.
437pub fn encode_component_planes_53(
438    planes: &[EncodeComponentPlane<'_>],
439    width: u32,
440    height: u32,
441    bit_depth: u8,
442    signed: bool,
443    options: &EncodeOptions,
444) -> Result<Vec<u8>, &'static str> {
445    let typed_planes = planes
446        .iter()
447        .map(|plane| EncodeTypedComponentPlane {
448            data: plane.data,
449            x_rsiz: plane.x_rsiz,
450            y_rsiz: plane.y_rsiz,
451            bit_depth,
452            signed,
453        })
454        .collect::<Vec<_>>();
455    encode_typed_component_planes_53(&typed_planes, width, height, options)
456}
457
458/// Encode reversible 5/3 typed component planes into a classic J2K or HTJ2K
459/// codestream.
460///
461/// This is the component-plane entry point for JPEG 2000 codestreams whose
462/// components have different precision or signedness. Plane buffers are
463/// supplied at each component's own SIZ sampling grid. Components are encoded
464/// without a reversible color transform.
465pub fn encode_typed_component_planes_53(
466    planes: &[EncodeTypedComponentPlane<'_>],
467    width: u32,
468    height: u32,
469    options: &EncodeOptions,
470) -> Result<Vec<u8>, &'static str> {
471    if width == 0 || height == 0 {
472        return Err("invalid dimensions");
473    }
474    if planes.is_empty() || planes.len() > usize::from(MAX_J2K_SPEC_COMPONENTS) {
475        return Err("unsupported component count");
476    }
477    if planes
478        .iter()
479        .any(|plane| plane.x_rsiz == 0 || plane.y_rsiz == 0)
480    {
481        return Err("component sampling factors must be non-zero");
482    }
483    if planes.iter().any(|plane| plane.bit_depth == 0) {
484        return Err("unsupported bit depth");
485    }
486    if planes
487        .iter()
488        .any(|plane| plane.bit_depth > MAX_PART1_SAMPLE_BIT_DEPTH)
489    {
490        return Err("unsupported bit depth");
491    }
492    if planes
493        .iter()
494        .any(|plane| plane.bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH)
495    {
496        return encode_typed_component_planes_53_i64(planes, width, height, options);
497    }
498
499    let max_levels = planes
500        .iter()
501        .map(|plane| {
502            let component_width = width.div_ceil(u32::from(plane.x_rsiz));
503            let component_height = height.div_ceil(u32::from(plane.y_rsiz));
504            max_decomposition_levels(component_width, component_height)
505        })
506        .min()
507        .unwrap_or(0);
508    let num_levels = options.num_decomposition_levels.min(max_levels);
509    let components = planes
510        .iter()
511        .map(|plane| {
512            let component_width = width.div_ceil(u32::from(plane.x_rsiz));
513            let component_height = height.div_ceil(u32::from(plane.y_rsiz));
514            let samples = component_plane_to_f32(
515                plane.data,
516                component_width,
517                component_height,
518                plane.bit_depth,
519                plane.signed,
520            )?;
521            let dwt = fdwt::forward_dwt(
522                &samples,
523                component_width,
524                component_height,
525                num_levels,
526                true,
527            );
528            Ok(PrecomputedHtj2k53Component {
529                x_rsiz: plane.x_rsiz,
530                y_rsiz: plane.y_rsiz,
531                dwt: forward_dwt53_output_from_decomposition(dwt),
532            })
533        })
534        .collect::<Result<Vec<_>, &'static str>>()?;
535    let max_bit_depth = planes
536        .iter()
537        .map(|plane| plane.bit_depth)
538        .max()
539        .ok_or("unsupported component count")?;
540    let component_sample_info = planes
541        .iter()
542        .map(|plane| EncodeComponentSampleInfo {
543            bit_depth: plane.bit_depth,
544            signed: plane.signed,
545        })
546        .collect::<Vec<_>>();
547    let image = PrecomputedHtj2k53Image {
548        width,
549        height,
550        bit_depth: max_bit_depth,
551        signed: planes.iter().all(|plane| plane.signed),
552        components,
553    };
554
555    if options.use_ht_block_coding {
556        let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
557        encode_precomputed_53_with_component_sample_info_and_accelerator(
558            &image,
559            options,
560            false,
561            true,
562            &component_sample_info,
563            &mut accelerator,
564        )
565    } else {
566        let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
567        encode_precomputed_53_with_component_sample_info_and_accelerator(
568            &image,
569            options,
570            false,
571            false,
572            &component_sample_info,
573            &mut accelerator,
574        )
575    }
576}
577
578fn encode_typed_component_planes_53_i64(
579    planes: &[EncodeTypedComponentPlane<'_>],
580    width: u32,
581    height: u32,
582    options: &EncodeOptions,
583) -> Result<Vec<u8>, &'static str> {
584    if options.num_layers == 0 || options.num_layers > 32 {
585        return Err("unsupported quality layer count");
586    }
587    if options.write_ppm && options.write_ppt {
588        return Err("PPM and PPT packet header markers are mutually exclusive");
589    }
590    if matches!(options.tile_part_packet_limit, Some(0)) {
591        return Err("tile-part packet limit must be non-zero");
592    }
593    if !options.quality_layer_byte_targets.is_empty()
594        && options.quality_layer_byte_targets.len() != usize::from(options.num_layers)
595    {
596        return Err("quality layer byte target count must match quality layer count");
597    }
598    if let Some((tile_width, tile_height)) = options.tile_size {
599        if tile_width == 0 || tile_height == 0 {
600            return Err("invalid tile dimensions");
601        }
602    }
603
604    let num_components = u16::try_from(planes.len()).map_err(|_| "unsupported component count")?;
605    if let Some((tile_width, tile_height)) = options.tile_size {
606        if tile_width < width || tile_height < height {
607            return encode_typed_component_planes_53_i64_multitile(
608                planes,
609                width,
610                height,
611                options,
612                tile_width,
613                tile_height,
614                num_components,
615            );
616        }
617    }
618    let max_bit_depth = planes
619        .iter()
620        .map(|plane| plane.bit_depth)
621        .max()
622        .ok_or("unsupported component count")?;
623    let num_levels = planes
624        .iter()
625        .map(|plane| {
626            let component_width = width.div_ceil(u32::from(plane.x_rsiz));
627            let component_height = height.div_ceil(u32::from(plane.y_rsiz));
628            max_decomposition_levels(component_width, component_height)
629        })
630        .min()
631        .unwrap_or(0)
632        .min(options.num_decomposition_levels);
633    let requested_guard_bits = options.guard_bits;
634    let guard_bits =
635        reversible_guard_bits_for_marker_limit(max_bit_depth, num_levels, requested_guard_bits)?;
636    let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
637    let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
638        max_bit_depth,
639        num_levels,
640        true,
641        guard_bits,
642        options.irreversible_quantization_scale,
643        options.irreversible_quantization_subband_scales,
644    );
645    if reversible_guard_delta != 0 {
646        adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
647    }
648    let component_sample_info = planes
649        .iter()
650        .map(|plane| EncodeComponentSampleInfo {
651            bit_depth: plane.bit_depth,
652            signed: plane.signed,
653        })
654        .collect::<Vec<_>>();
655    let mut component_step_sizes = component_step_sizes(
656        &component_sample_info,
657        num_levels,
658        true,
659        guard_bits,
660        options.irreversible_quantization_scale,
661        options.irreversible_quantization_subband_scales,
662    );
663    if reversible_guard_delta != 0 {
664        adjust_component_step_sizes_for_guard_delta(
665            &mut component_step_sizes,
666            reversible_guard_delta,
667        )?;
668    }
669    if step_sizes.iter().any(|step| step.exponent > 31)
670        || component_step_sizes
671            .iter()
672            .flatten()
673            .any(|step| step.exponent > 31)
674    {
675        return Err("25-38 bit typed component-plane encode exceeds the current no-quantization guard/exponent signaling limit");
676    }
677
678    let quant_params = step_sizes
679        .iter()
680        .map(|step| (step.exponent, step.mantissa))
681        .collect::<Vec<_>>();
682    let component_quantization_step_sizes = component_step_sizes
683        .iter()
684        .map(|steps| {
685            steps
686                .iter()
687                .map(|step| (step.exponent, step.mantissa))
688                .collect::<Vec<_>>()
689        })
690        .collect::<Vec<_>>();
691    let cb_width = 1u32 << (options.code_block_width_exp + 2);
692    let cb_height = 1u32 << (options.code_block_height_exp + 2);
693    let block_coding_mode = if options.use_ht_block_coding {
694        BlockCodingMode::HighThroughput
695    } else {
696        BlockCodingMode::Classic
697    };
698    let component_sampling = planes
699        .iter()
700        .map(|plane| (plane.x_rsiz, plane.y_rsiz))
701        .collect::<Vec<_>>();
702    let mut high_bit_options = options.clone();
703    high_bit_options.reversible = true;
704    high_bit_options.use_mct = false;
705    high_bit_options.component_sampling = Some(component_sampling.clone());
706    let precinct_exponents = precinct_exponents_for_options(&high_bit_options, num_levels)?;
707    let params = EncodeParams {
708        width,
709        height,
710        tile_width: options
711            .tile_size
712            .map_or(width, |(tile_width, _)| tile_width),
713        tile_height: options
714            .tile_size
715            .map_or(height, |(_, tile_height)| tile_height),
716        num_components,
717        bit_depth: max_bit_depth,
718        signed: planes.iter().all(|plane| plane.signed),
719        component_sample_info,
720        component_quantization_step_sizes,
721        num_decomposition_levels: num_levels,
722        reversible: true,
723        code_block_width_exp: options.code_block_width_exp,
724        code_block_height_exp: options.code_block_height_exp,
725        num_layers: options.num_layers,
726        use_mct: false,
727        guard_bits,
728        block_coding_mode,
729        progression_order: options.progression_order,
730        write_tlm: options.write_tlm,
731        write_plt: options.write_plt,
732        write_plm: options.write_plm,
733        write_ppm: options.write_ppm,
734        write_ppt: options.write_ppt,
735        write_sop: options.write_sop,
736        write_eph: options.write_eph,
737        terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
738            && options.num_layers > 1,
739        component_sampling,
740        roi_component_shifts: vec![0; usize::from(num_components)],
741        precinct_exponents,
742    };
743
744    let ht_target_coding_passes = ht_target_coding_passes_for_options(options);
745    let mut component_resolution_packets = Vec::with_capacity(planes.len());
746    for (component_idx, plane) in planes.iter().enumerate() {
747        let component_width = width.div_ceil(u32::from(plane.x_rsiz));
748        let component_height = height.div_ceil(u32::from(plane.y_rsiz));
749        let samples = typed_component_plane_to_i64(plane, component_width, component_height)?;
750        let decomp = fdwt::forward_dwt_i64(&samples, component_width, component_height, num_levels);
751        let steps = component_step_sizes
752            .get(component_idx)
753            .ok_or("component quantization step count mismatch")?;
754        let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
755        let mut packets = Vec::with_capacity(num_levels as usize + 1);
756
757        let ll_subband = prepare_subband_i64(
758            &decomp.ll,
759            decomp.ll_width,
760            decomp.ll_height,
761            steps
762                .first()
763                .ok_or("reversible quantization step missing")?,
764            guard_bits,
765            cb_width,
766            cb_height,
767            SubBandType::LowLow,
768            0,
769            &[],
770            1,
771            block_coding_mode,
772            ht_target_coding_passes,
773        )?;
774        packets.push(PreparedResolutionPacket {
775            component,
776            resolution: 0,
777            precinct: 0,
778            subbands: vec![ll_subband],
779        });
780
781        for (level_idx, level) in decomp.levels.iter().enumerate() {
782            let step_base = 1 + level_idx * 3;
783            let hl_subband = prepare_subband_i64(
784                &level.hl,
785                level.high_width,
786                level.low_height,
787                steps
788                    .get(step_base)
789                    .ok_or("reversible quantization step missing")?,
790                guard_bits,
791                cb_width,
792                cb_height,
793                SubBandType::HighLow,
794                0,
795                &[],
796                1,
797                block_coding_mode,
798                ht_target_coding_passes,
799            )?;
800            let lh_subband = prepare_subband_i64(
801                &level.lh,
802                level.low_width,
803                level.high_height,
804                steps
805                    .get(step_base + 1)
806                    .ok_or("reversible quantization step missing")?,
807                guard_bits,
808                cb_width,
809                cb_height,
810                SubBandType::LowHigh,
811                0,
812                &[],
813                1,
814                block_coding_mode,
815                ht_target_coding_passes,
816            )?;
817            let hh_subband = prepare_subband_i64(
818                &level.hh,
819                level.high_width,
820                level.high_height,
821                steps
822                    .get(step_base + 2)
823                    .ok_or("reversible quantization step missing")?,
824                guard_bits,
825                cb_width,
826                cb_height,
827                SubBandType::HighHigh,
828                0,
829                &[],
830                1,
831                block_coding_mode,
832                ht_target_coding_passes,
833            )?;
834            packets.push(PreparedResolutionPacket {
835                component,
836                resolution: u32::try_from(level_idx + 1)
837                    .map_err(|_| "resolution index exceeds u32")?,
838                precinct: 0,
839                subbands: vec![hl_subband, lh_subband, hh_subband],
840            });
841        }
842        component_resolution_packets.push(packets);
843    }
844
845    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
846    encode_i64_component_resolution_packets(
847        component_resolution_packets,
848        width,
849        height,
850        num_components,
851        num_levels,
852        &params,
853        &quant_params,
854        &high_bit_options,
855        &mut accelerator,
856    )
857}
858
859#[allow(clippy::too_many_arguments)]
860fn encode_typed_component_planes_53_i64_multitile(
861    planes: &[EncodeTypedComponentPlane<'_>],
862    width: u32,
863    height: u32,
864    options: &EncodeOptions,
865    tile_width: u32,
866    tile_height: u32,
867    num_components: u16,
868) -> Result<Vec<u8>, &'static str> {
869    let num_x_tiles = width.div_ceil(tile_width);
870    let num_y_tiles = height.div_ceil(tile_height);
871    let num_tiles = num_x_tiles
872        .checked_mul(num_y_tiles)
873        .ok_or("tile count overflow")?;
874    if num_tiles > u32::from(u16::MAX) + 1 {
875        return Err("multi-tile encode supports at most 65536 tiles");
876    }
877
878    let num_levels = min_sampled_tile_component_decomposition_levels(
879        planes,
880        width,
881        height,
882        tile_width,
883        tile_height,
884    )?
885    .min(options.num_decomposition_levels);
886    let max_bit_depth = planes
887        .iter()
888        .map(|plane| plane.bit_depth)
889        .max()
890        .ok_or("unsupported component count")?;
891    let requested_guard_bits = options.guard_bits;
892    let guard_bits =
893        reversible_guard_bits_for_marker_limit(max_bit_depth, num_levels, requested_guard_bits)?;
894    let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
895    let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
896        max_bit_depth,
897        num_levels,
898        true,
899        guard_bits,
900        options.irreversible_quantization_scale,
901        options.irreversible_quantization_subband_scales,
902    );
903    if reversible_guard_delta != 0 {
904        adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
905    }
906    let component_sample_info = planes
907        .iter()
908        .map(|plane| EncodeComponentSampleInfo {
909            bit_depth: plane.bit_depth,
910            signed: plane.signed,
911        })
912        .collect::<Vec<_>>();
913    let mut component_step_sizes = component_step_sizes(
914        &component_sample_info,
915        num_levels,
916        true,
917        guard_bits,
918        options.irreversible_quantization_scale,
919        options.irreversible_quantization_subband_scales,
920    );
921    if reversible_guard_delta != 0 {
922        adjust_component_step_sizes_for_guard_delta(
923            &mut component_step_sizes,
924            reversible_guard_delta,
925        )?;
926    }
927    if step_sizes.iter().any(|step| step.exponent > 31)
928        || component_step_sizes
929            .iter()
930            .flatten()
931            .any(|step| step.exponent > 31)
932    {
933        return Err("25-38 bit typed component-plane encode exceeds the current no-quantization guard/exponent signaling limit");
934    }
935
936    let quant_params = step_sizes
937        .iter()
938        .map(|step| (step.exponent, step.mantissa))
939        .collect::<Vec<_>>();
940    let component_quantization_step_sizes = component_step_sizes
941        .iter()
942        .map(|steps| {
943            steps
944                .iter()
945                .map(|step| (step.exponent, step.mantissa))
946                .collect::<Vec<_>>()
947        })
948        .collect::<Vec<_>>();
949    let component_sampling = planes
950        .iter()
951        .map(|plane| (plane.x_rsiz, plane.y_rsiz))
952        .collect::<Vec<_>>();
953    let mut high_bit_options = options.clone();
954    high_bit_options.num_decomposition_levels = num_levels;
955    high_bit_options.reversible = true;
956    high_bit_options.use_mct = false;
957    high_bit_options.component_sampling = Some(component_sampling.clone());
958    let precinct_exponents = precinct_exponents_for_options(&high_bit_options, num_levels)?;
959
960    let mut child_options = high_bit_options.clone();
961    child_options.tile_size = None;
962    child_options.write_tlm = false;
963    child_options.write_plt = false;
964    child_options.write_plm = false;
965    child_options.write_ppm = false;
966    child_options.write_ppt = false;
967
968    let block_coding_mode = if options.use_ht_block_coding {
969        BlockCodingMode::HighThroughput
970    } else {
971        BlockCodingMode::Classic
972    };
973    let params = EncodeParams {
974        width,
975        height,
976        tile_width,
977        tile_height,
978        num_components,
979        bit_depth: max_bit_depth,
980        signed: planes.iter().all(|plane| plane.signed),
981        component_sample_info,
982        component_quantization_step_sizes,
983        num_decomposition_levels: num_levels,
984        reversible: true,
985        code_block_width_exp: options.code_block_width_exp,
986        code_block_height_exp: options.code_block_height_exp,
987        num_layers: options.num_layers,
988        use_mct: false,
989        guard_bits,
990        block_coding_mode,
991        progression_order: options.progression_order,
992        write_tlm: options.write_tlm,
993        write_plt: options.write_plt,
994        write_plm: options.write_plm,
995        write_ppm: options.write_ppm,
996        write_ppt: options.write_ppt,
997        write_sop: options.write_sop,
998        write_eph: options.write_eph,
999        terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
1000            && options.num_layers > 1,
1001        component_sampling,
1002        roi_component_shifts: vec![0; usize::from(num_components)],
1003        precinct_exponents,
1004    };
1005
1006    let mut tile_bodies = Vec::with_capacity(num_tiles as usize);
1007    for tile_y in 0..num_y_tiles {
1008        for tile_x in 0..num_x_tiles {
1009            let tile_index = tile_y
1010                .checked_mul(num_x_tiles)
1011                .and_then(|base| base.checked_add(tile_x))
1012                .ok_or("tile index overflow")?;
1013            let tile_index = u16::try_from(tile_index).map_err(|_| "tile index exceeds u16")?;
1014            let x0 = tile_x * tile_width;
1015            let y0 = tile_y * tile_height;
1016            let actual_width = (width - x0).min(tile_width);
1017            let actual_height = (height - y0).min(tile_height);
1018            let tile_plane_data = planes
1019                .iter()
1020                .map(|plane| {
1021                    let x_rsiz = u32::from(plane.x_rsiz);
1022                    let y_rsiz = u32::from(plane.y_rsiz);
1023                    let component_image_width = width.div_ceil(x_rsiz);
1024                    let component_image_height = height.div_ceil(y_rsiz);
1025                    let (component_x0, component_tile_width) = sampled_tile_component_axis(
1026                        x0,
1027                        actual_width,
1028                        x_rsiz,
1029                        component_image_width,
1030                    )?;
1031                    let (component_y0, component_tile_height) = sampled_tile_component_axis(
1032                        y0,
1033                        actual_height,
1034                        y_rsiz,
1035                        component_image_height,
1036                    )?;
1037                    let data = extract_component_plane_tile(
1038                        plane.data,
1039                        component_image_width,
1040                        component_x0,
1041                        component_y0,
1042                        component_tile_width,
1043                        component_tile_height,
1044                        plane.bit_depth,
1045                    )?;
1046                    Ok((data, component_tile_width, component_tile_height))
1047                })
1048                .collect::<Result<Vec<_>, &'static str>>()?;
1049            let tile_planes = planes
1050                .iter()
1051                .zip(tile_plane_data.iter())
1052                .map(|(plane, (data, _, _))| EncodeTypedComponentPlane {
1053                    data,
1054                    x_rsiz: plane.x_rsiz,
1055                    y_rsiz: plane.y_rsiz,
1056                    bit_depth: plane.bit_depth,
1057                    signed: plane.signed,
1058                })
1059                .collect::<Vec<_>>();
1060            let component_dimensions = tile_planes
1061                .iter()
1062                .zip(tile_plane_data.iter())
1063                .map(|(_, (_, component_width, component_height))| {
1064                    (*component_width, *component_height)
1065                })
1066                .collect::<Vec<_>>();
1067            let component_resolution_packets = prepare_typed_component_planes_i64_packets(
1068                &tile_planes,
1069                &component_dimensions,
1070                &component_step_sizes,
1071                guard_bits,
1072                num_levels,
1073                1u32 << (options.code_block_width_exp + 2),
1074                1u32 << (options.code_block_height_exp + 2),
1075                block_coding_mode,
1076                ht_target_coding_passes_for_options(options),
1077            )?;
1078            let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1079            let packetized_tile = packetize_i64_component_resolution_packets(
1080                component_resolution_packets,
1081                actual_width,
1082                actual_height,
1083                num_components,
1084                num_levels,
1085                &params,
1086                &child_options,
1087                &mut accelerator,
1088            )?;
1089            tile_bodies.extend(split_packetized_tile_into_tile_parts(
1090                tile_index,
1091                &packetized_tile.data,
1092                &packetized_tile.packet_lengths,
1093                &packetized_tile.packet_headers,
1094                options.tile_part_packet_limit,
1095            )?);
1096        }
1097    }
1098
1099    let tile_packet_headers = tile_bodies
1100        .iter()
1101        .map(|tile| tile.packet_headers.as_slice())
1102        .collect::<Vec<_>>();
1103    validate_packet_header_marker_payloads(
1104        params.write_ppm,
1105        params.write_ppt,
1106        &tile_packet_headers,
1107    )?;
1108    let tile_parts = tile_bodies
1109        .iter()
1110        .map(|tile| codestream_write::TilePartData {
1111            tile_index: tile.tile_index,
1112            tile_part_index: tile.tile_part_index,
1113            num_tile_parts: tile.num_tile_parts,
1114            data: &tile.data,
1115            packet_lengths: &tile.packet_lengths,
1116            packet_headers: &tile.packet_headers,
1117        })
1118        .collect::<Vec<_>>();
1119
1120    Ok(codestream_write::write_codestream_tiles(
1121        &params,
1122        &tile_parts,
1123        &quant_params,
1124    ))
1125}
1126
1127fn min_sampled_tile_component_decomposition_levels(
1128    planes: &[EncodeTypedComponentPlane<'_>],
1129    width: u32,
1130    height: u32,
1131    tile_width: u32,
1132    tile_height: u32,
1133) -> Result<u8, &'static str> {
1134    let num_x_tiles = width.div_ceil(tile_width);
1135    let num_y_tiles = height.div_ceil(tile_height);
1136    let mut levels: Option<u8> = None;
1137    for tile_y in 0..num_y_tiles {
1138        for tile_x in 0..num_x_tiles {
1139            let x0 = tile_x * tile_width;
1140            let y0 = tile_y * tile_height;
1141            let actual_width = (width - x0).min(tile_width);
1142            let actual_height = (height - y0).min(tile_height);
1143            for plane in planes {
1144                let x_rsiz = u32::from(plane.x_rsiz);
1145                let y_rsiz = u32::from(plane.y_rsiz);
1146                let component_image_width = width.div_ceil(x_rsiz);
1147                let component_image_height = height.div_ceil(y_rsiz);
1148                let (_, component_tile_width) =
1149                    sampled_tile_component_axis(x0, actual_width, x_rsiz, component_image_width)?;
1150                let (_, component_tile_height) =
1151                    sampled_tile_component_axis(y0, actual_height, y_rsiz, component_image_height)?;
1152                let component_levels =
1153                    max_decomposition_levels(component_tile_width, component_tile_height);
1154                levels = Some(levels.map_or(component_levels, |min| min.min(component_levels)));
1155            }
1156        }
1157    }
1158    Ok(levels.unwrap_or(0))
1159}
1160
1161fn sampled_tile_component_axis(
1162    tile_origin: u32,
1163    tile_extent: u32,
1164    sampling: u32,
1165    component_extent: u32,
1166) -> Result<(u32, u32), &'static str> {
1167    let tile_end = tile_origin
1168        .checked_add(tile_extent)
1169        .ok_or("tile component bounds overflow")?;
1170    let start = tile_origin.div_ceil(sampling).min(component_extent);
1171    let end = tile_end.div_ceil(sampling).min(component_extent);
1172    Ok((start, end.saturating_sub(start)))
1173}
1174
1175#[allow(clippy::too_many_arguments)]
1176fn prepare_typed_component_planes_i64_packets(
1177    planes: &[EncodeTypedComponentPlane<'_>],
1178    component_dimensions: &[(u32, u32)],
1179    component_step_sizes: &[Vec<QuantStepSize>],
1180    guard_bits: u8,
1181    num_levels: u8,
1182    cb_width: u32,
1183    cb_height: u32,
1184    block_coding_mode: BlockCodingMode,
1185    ht_target_coding_passes: u8,
1186) -> Result<Vec<Vec<PreparedResolutionPacket>>, &'static str> {
1187    if component_dimensions.len() != planes.len() {
1188        return Err("component dimensions count does not match component count");
1189    }
1190    let mut component_resolution_packets = Vec::with_capacity(planes.len());
1191    for (component_idx, (plane, &(component_width, component_height))) in
1192        planes.iter().zip(component_dimensions).enumerate()
1193    {
1194        let samples = typed_component_plane_to_i64(plane, component_width, component_height)?;
1195        let decomp = fdwt::forward_dwt_i64(&samples, component_width, component_height, num_levels);
1196        let steps = component_step_sizes
1197            .get(component_idx)
1198            .ok_or("component quantization step count mismatch")?;
1199        let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
1200        let mut packets = Vec::with_capacity(num_levels as usize + 1);
1201
1202        let ll_subband = prepare_subband_i64(
1203            &decomp.ll,
1204            decomp.ll_width,
1205            decomp.ll_height,
1206            steps
1207                .first()
1208                .ok_or("reversible quantization step missing")?,
1209            guard_bits,
1210            cb_width,
1211            cb_height,
1212            SubBandType::LowLow,
1213            0,
1214            &[],
1215            1,
1216            block_coding_mode,
1217            ht_target_coding_passes,
1218        )?;
1219        packets.push(PreparedResolutionPacket {
1220            component,
1221            resolution: 0,
1222            precinct: 0,
1223            subbands: vec![ll_subband],
1224        });
1225
1226        for (level_idx, level) in decomp.levels.iter().enumerate() {
1227            let step_base = 1 + level_idx * 3;
1228            let hl_subband = prepare_subband_i64(
1229                &level.hl,
1230                level.high_width,
1231                level.low_height,
1232                steps
1233                    .get(step_base)
1234                    .ok_or("reversible quantization step missing")?,
1235                guard_bits,
1236                cb_width,
1237                cb_height,
1238                SubBandType::HighLow,
1239                0,
1240                &[],
1241                1,
1242                block_coding_mode,
1243                ht_target_coding_passes,
1244            )?;
1245            let lh_subband = prepare_subband_i64(
1246                &level.lh,
1247                level.low_width,
1248                level.high_height,
1249                steps
1250                    .get(step_base + 1)
1251                    .ok_or("reversible quantization step missing")?,
1252                guard_bits,
1253                cb_width,
1254                cb_height,
1255                SubBandType::LowHigh,
1256                0,
1257                &[],
1258                1,
1259                block_coding_mode,
1260                ht_target_coding_passes,
1261            )?;
1262            let hh_subband = prepare_subband_i64(
1263                &level.hh,
1264                level.high_width,
1265                level.high_height,
1266                steps
1267                    .get(step_base + 2)
1268                    .ok_or("reversible quantization step missing")?,
1269                guard_bits,
1270                cb_width,
1271                cb_height,
1272                SubBandType::HighHigh,
1273                0,
1274                &[],
1275                1,
1276                block_coding_mode,
1277                ht_target_coding_passes,
1278            )?;
1279            packets.push(PreparedResolutionPacket {
1280                component,
1281                resolution: u32::try_from(level_idx + 1)
1282                    .map_err(|_| "resolution index exceeds u32")?,
1283                precinct: 0,
1284                subbands: vec![hl_subband, lh_subband, hh_subband],
1285            });
1286        }
1287        component_resolution_packets.push(packets);
1288    }
1289
1290    Ok(component_resolution_packets)
1291}
1292
1293/// Encode precomputed reversible 5/3 wavelet coefficients into a classic
1294/// JPEG 2000 Part 1 codestream.
1295///
1296/// This mirrors [`encode_precomputed_htj2k_53`] while selecting classic EBCOT
1297/// block coding. It reuses the same quantization, packetization, and codestream
1298/// writer stages as the normal encoder and is primarily intended for fixtures
1299/// and coefficient-domain workflows that need JPEG-native component sampling.
1300pub fn encode_precomputed_j2k_53(
1301    image: &PrecomputedHtj2k53Image,
1302    options: &EncodeOptions,
1303) -> Result<Vec<u8>, &'static str> {
1304    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1305    encode_precomputed_j2k_53_with_mct_and_accelerator(image, options, false, &mut accelerator)
1306}
1307
1308/// Encode precomputed reversible 5/3 wavelet coefficients into a classic
1309/// JPEG 2000 Part 1 codestream using optional block encode and packetization
1310/// hooks.
1311pub fn encode_precomputed_j2k_53_with_accelerator(
1312    image: &PrecomputedHtj2k53Image,
1313    options: &EncodeOptions,
1314    accelerator: &mut impl J2kEncodeStageAccelerator,
1315) -> Result<Vec<u8>, &'static str> {
1316    encode_precomputed_j2k_53_with_mct_and_accelerator(image, options, false, accelerator)
1317}
1318
1319/// Encode precomputed reversible 5/3 wavelet coefficients into a classic
1320/// JPEG 2000 Part 1 codestream while controlling the output COD
1321/// multi-component transform flag.
1322pub fn encode_precomputed_j2k_53_with_mct(
1323    image: &PrecomputedHtj2k53Image,
1324    options: &EncodeOptions,
1325    use_mct: bool,
1326) -> Result<Vec<u8>, &'static str> {
1327    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1328    encode_precomputed_j2k_53_with_mct_and_accelerator(image, options, use_mct, &mut accelerator)
1329}
1330
1331/// Encode precomputed reversible 5/3 wavelet coefficients into a classic
1332/// JPEG 2000 Part 1 codestream while controlling the output COD
1333/// multi-component transform flag and using optional encode stage hooks.
1334pub fn encode_precomputed_j2k_53_with_mct_and_accelerator(
1335    image: &PrecomputedHtj2k53Image,
1336    options: &EncodeOptions,
1337    use_mct: bool,
1338    accelerator: &mut impl J2kEncodeStageAccelerator,
1339) -> Result<Vec<u8>, &'static str> {
1340    encode_precomputed_53_with_mct_and_accelerator(image, options, use_mct, false, accelerator)
1341}
1342
1343/// Encode precomputed reversible 5/3 wavelet coefficients into an HTJ2K
1344/// codestream.
1345///
1346/// This experimental entry point reuses the existing quantization, HT block
1347/// coding, packetization, and codestream writer stages. It bypasses the
1348/// encoder's forward DWT stage by supplying precomputed DWT output through the
1349/// internal stage hook. Coefficients are expected in the same sample domain as
1350/// the native encoder's FDWT input: unsigned components are already level
1351/// shifted by subtracting `2^(bit_depth - 1)`.
1352pub fn encode_precomputed_htj2k_53(
1353    image: &PrecomputedHtj2k53Image,
1354    options: &EncodeOptions,
1355) -> Result<Vec<u8>, &'static str> {
1356    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1357    encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, false, &mut accelerator)
1358}
1359
1360/// Encode precomputed reversible 5/3 wavelet coefficients into an HTJ2K
1361/// codestream using optional block encode and packetization hooks.
1362pub fn encode_precomputed_htj2k_53_with_accelerator(
1363    image: &PrecomputedHtj2k53Image,
1364    options: &EncodeOptions,
1365    accelerator: &mut impl J2kEncodeStageAccelerator,
1366) -> Result<Vec<u8>, &'static str> {
1367    encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, false, accelerator)
1368}
1369
1370/// Encode precomputed reversible 5/3 wavelet coefficients into an HTJ2K
1371/// codestream while controlling the output COD multi-component transform flag.
1372///
1373/// This is intended for coefficient-domain JPEG 2000 family recoding, where
1374/// source codestream components may already be reversible-color-transformed.
1375pub fn encode_precomputed_htj2k_53_with_mct(
1376    image: &PrecomputedHtj2k53Image,
1377    options: &EncodeOptions,
1378    use_mct: bool,
1379) -> Result<Vec<u8>, &'static str> {
1380    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1381    encode_precomputed_htj2k_53_with_mct_and_accelerator(image, options, use_mct, &mut accelerator)
1382}
1383
1384/// Encode precomputed reversible 5/3 wavelet coefficients while controlling
1385/// the output COD multi-component transform flag and using optional encode
1386/// stage hooks.
1387pub fn encode_precomputed_htj2k_53_with_mct_and_accelerator(
1388    image: &PrecomputedHtj2k53Image,
1389    options: &EncodeOptions,
1390    use_mct: bool,
1391    accelerator: &mut impl J2kEncodeStageAccelerator,
1392) -> Result<Vec<u8>, &'static str> {
1393    encode_precomputed_53_with_mct_and_accelerator(image, options, use_mct, true, accelerator)
1394}
1395
1396fn encode_precomputed_53_with_mct_and_accelerator(
1397    image: &PrecomputedHtj2k53Image,
1398    options: &EncodeOptions,
1399    use_mct: bool,
1400    use_ht_block_coding: bool,
1401    accelerator: &mut impl J2kEncodeStageAccelerator,
1402) -> Result<Vec<u8>, &'static str> {
1403    encode_precomputed_53_with_component_sample_info_and_accelerator(
1404        image,
1405        options,
1406        use_mct,
1407        use_ht_block_coding,
1408        &[],
1409        accelerator,
1410    )
1411}
1412
1413fn encode_precomputed_53_with_component_sample_info_and_accelerator(
1414    image: &PrecomputedHtj2k53Image,
1415    options: &EncodeOptions,
1416    use_mct: bool,
1417    use_ht_block_coding: bool,
1418    component_sample_info: &[EncodeComponentSampleInfo],
1419    accelerator: &mut impl J2kEncodeStageAccelerator,
1420) -> Result<Vec<u8>, &'static str> {
1421    if image.width == 0 || image.height == 0 {
1422        return Err("invalid dimensions");
1423    }
1424    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1425    {
1426        return Err("unsupported component count");
1427    }
1428    if image.bit_depth == 0 || image.bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH {
1429        return Err("unsupported bit depth");
1430    }
1431    validate_component_sample_info(component_sample_info, image.components.len())?;
1432    if image
1433        .components
1434        .iter()
1435        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1436    {
1437        return Err("component sampling factors must be non-zero");
1438    }
1439    validate_precomputed_dwt_geometry(image)?;
1440
1441    let num_components =
1442        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1443    let num_levels = precomputed_level_count(&image.components)?;
1444    let mut precomputed_options = options.clone();
1445    precomputed_options.num_decomposition_levels = num_levels;
1446    precomputed_options.reversible = true;
1447    precomputed_options.use_ht_block_coding = use_ht_block_coding;
1448    precomputed_options.use_mct = use_mct;
1449    precomputed_options.validate_high_throughput_codestream = false;
1450    precomputed_options.component_sampling = Some(
1451        image
1452            .components
1453            .iter()
1454            .map(|component| (component.x_rsiz, component.y_rsiz))
1455            .collect(),
1456    );
1457
1458    let dummy_pixels =
1459        zero_pixel_buffer(image.width, image.height, num_components, image.bit_depth)?;
1460    let mut precomputed_accelerator = PrecomputedDwtAccelerator {
1461        outputs: image
1462            .components
1463            .iter()
1464            .map(|component| component.dwt.clone())
1465            .collect(),
1466        encode_accelerator: accelerator,
1467    };
1468
1469    encode_with_accelerator_and_component_sample_info(
1470        &dummy_pixels,
1471        image.width,
1472        image.height,
1473        num_components,
1474        image.bit_depth,
1475        image.signed,
1476        &precomputed_options,
1477        component_sample_info,
1478        &mut precomputed_accelerator,
1479    )
1480}
1481
1482/// Encode precomputed irreversible 9/7 wavelet coefficients into an HTJ2K
1483/// codestream.
1484///
1485/// This experimental entry point is the lossy counterpart of
1486/// [`encode_precomputed_htj2k_53`]. It bypasses the encoder's forward 9/7 DWT
1487/// stage by supplying precomputed floating-point DWT output through the
1488/// internal stage hook. Coefficients are expected in the same sample domain as
1489/// the native irreversible FDWT input: unsigned components are already level
1490/// shifted by subtracting `2^(bit_depth - 1)`.
1491pub fn encode_precomputed_htj2k_97(
1492    image: &PrecomputedHtj2k97Image,
1493    options: &EncodeOptions,
1494) -> Result<Vec<u8>, &'static str> {
1495    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1496    encode_precomputed_htj2k_97_with_accelerator(image, options, &mut accelerator)
1497}
1498
1499/// Encode precomputed irreversible 9/7 wavelet coefficients into an HTJ2K
1500/// codestream using optional block encode and packetization hooks.
1501pub fn encode_precomputed_htj2k_97_with_accelerator(
1502    image: &PrecomputedHtj2k97Image,
1503    options: &EncodeOptions,
1504    accelerator: &mut impl J2kEncodeStageAccelerator,
1505) -> Result<Vec<u8>, &'static str> {
1506    if image.width == 0 || image.height == 0 {
1507        return Err("invalid dimensions");
1508    }
1509    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1510    {
1511        return Err("unsupported component count");
1512    }
1513    if image.bit_depth == 0 || image.bit_depth > 16 {
1514        return Err("unsupported bit depth");
1515    }
1516    validate_irreversible_quantization_profile(options)?;
1517    if image
1518        .components
1519        .iter()
1520        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1521    {
1522        return Err("component sampling factors must be non-zero");
1523    }
1524    validate_precomputed_dwt97_geometry(image)?;
1525
1526    let num_components =
1527        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1528    let num_levels = precomputed_97_level_count(&image.components)?;
1529    let mut precomputed_options = options.clone();
1530    precomputed_options.num_decomposition_levels = num_levels;
1531    precomputed_options.reversible = false;
1532    precomputed_options.use_ht_block_coding = true;
1533    precomputed_options.use_mct = false;
1534    precomputed_options.validate_high_throughput_codestream = false;
1535    precomputed_options.component_sampling = Some(
1536        image
1537            .components
1538            .iter()
1539            .map(|component| (component.x_rsiz, component.y_rsiz))
1540            .collect(),
1541    );
1542
1543    let dummy_pixels =
1544        zero_pixel_buffer(image.width, image.height, num_components, image.bit_depth)?;
1545    let mut precomputed_accelerator = PrecomputedDwt97Accelerator {
1546        outputs: image
1547            .components
1548            .iter()
1549            .map(|component| component.dwt.clone())
1550            .collect(),
1551        encode_accelerator: accelerator,
1552    };
1553
1554    encode_with_accelerator(
1555        &dummy_pixels,
1556        image.width,
1557        image.height,
1558        num_components,
1559        image.bit_depth,
1560        image.signed,
1561        &precomputed_options,
1562        &mut precomputed_accelerator,
1563    )
1564}
1565
1566/// Encode multiple precomputed irreversible 9/7 wavelet images while sharing
1567/// one HT code-block batch across all prepared tiles.
1568pub fn encode_precomputed_htj2k_97_batch_with_accelerator(
1569    images: &[PrecomputedHtj2k97Image],
1570    options: &EncodeOptions,
1571    accelerator: &mut impl J2kEncodeStageAccelerator,
1572) -> Result<Vec<Vec<u8>>, &'static str> {
1573    if images.is_empty() {
1574        return Ok(Vec::new());
1575    }
1576    if options.num_layers != 1 {
1577        return Err("batch precomputed 9/7 encode currently supports one quality layer");
1578    }
1579
1580    let mut prepared_images = prepare_precomputed_htj2k97_images_for_batch(images, options)?;
1581    let mut all_packets = Vec::new();
1582    for prepared in &mut prepared_images {
1583        prepared.packet_count = prepared.prepared_packets.len();
1584        all_packets.append(&mut prepared.prepared_packets);
1585    }
1586
1587    let mut encoded_packets =
1588        encode_prepared_resolution_packets(all_packets, accelerator)?.into_iter();
1589    let mut codestreams = Vec::with_capacity(prepared_images.len());
1590    for prepared in prepared_images {
1591        let mut resolution_packets = Vec::with_capacity(prepared.packet_count);
1592        for _ in 0..prepared.packet_count {
1593            resolution_packets.push(
1594                encoded_packets
1595                    .next()
1596                    .ok_or("encoded packet count mismatch")?,
1597            );
1598        }
1599        let scalar_packet_descriptors = scalar_packet_descriptors(&prepared.packet_descriptors);
1600        let packetized_tile =
1601            packet_encode::form_tile_bitstream_with_descriptors_lengths_and_markers(
1602                &mut resolution_packets,
1603                &scalar_packet_descriptors,
1604                packet_encode::PacketMarkerOptions {
1605                    write_sop: prepared.params.write_sop,
1606                    write_eph: prepared.params.write_eph,
1607                    separate_packet_headers: prepared.params.write_ppm || prepared.params.write_ppt,
1608                },
1609            )?;
1610        codestreams.push(write_single_tile_packetized_codestream(
1611            &prepared.params,
1612            &packetized_tile,
1613            &prepared.quant_params,
1614            options.tile_part_packet_limit,
1615        )?);
1616    }
1617    if encoded_packets.next().is_some() {
1618        return Err("encoded packet count mismatch");
1619    }
1620
1621    Ok(codestreams)
1622}
1623
1624#[cfg(feature = "parallel")]
1625fn prepare_precomputed_htj2k97_images_for_batch(
1626    images: &[PrecomputedHtj2k97Image],
1627    options: &EncodeOptions,
1628) -> Result<Vec<PreparedPrecomputedHtj2k97Image>, &'static str> {
1629    images
1630        .par_iter()
1631        .map(|image| prepare_precomputed_htj2k97_image_for_batch(image, options))
1632        .collect()
1633}
1634
1635#[cfg(not(feature = "parallel"))]
1636fn prepare_precomputed_htj2k97_images_for_batch(
1637    images: &[PrecomputedHtj2k97Image],
1638    options: &EncodeOptions,
1639) -> Result<Vec<PreparedPrecomputedHtj2k97Image>, &'static str> {
1640    images
1641        .iter()
1642        .map(|image| prepare_precomputed_htj2k97_image_for_batch(image, options))
1643        .collect()
1644}
1645
1646/// Encode prequantized irreversible 9/7 code-block coefficients into an HTJ2K
1647/// codestream.
1648pub fn encode_prequantized_htj2k_97(
1649    image: &PrequantizedHtj2k97Image,
1650    options: &EncodeOptions,
1651) -> Result<Vec<u8>, &'static str> {
1652    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1653    encode_prequantized_htj2k_97_with_accelerator(image, options, &mut accelerator)
1654}
1655
1656/// Encode prequantized irreversible 9/7 code-block coefficients into an HTJ2K
1657/// codestream using optional block encode and packetization hooks.
1658pub fn encode_prequantized_htj2k_97_with_accelerator(
1659    image: &PrequantizedHtj2k97Image,
1660    options: &EncodeOptions,
1661    accelerator: &mut impl J2kEncodeStageAccelerator,
1662) -> Result<Vec<u8>, &'static str> {
1663    if image.width == 0 || image.height == 0 {
1664        return Err("invalid dimensions");
1665    }
1666    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1667    {
1668        return Err("unsupported component count");
1669    }
1670    if image.bit_depth == 0 || image.bit_depth > 16 {
1671        return Err("unsupported bit depth");
1672    }
1673    validate_irreversible_quantization_profile(options)?;
1674    if image
1675        .components
1676        .iter()
1677        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1678    {
1679        return Err("component sampling factors must be non-zero");
1680    }
1681
1682    let num_components =
1683        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1684    let num_levels = prequantized_97_level_count(&image.components)?;
1685    let guard_bits = options.guard_bits.max(2);
1686    let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
1687        image.bit_depth,
1688        num_levels,
1689        false,
1690        guard_bits,
1691        options.irreversible_quantization_scale,
1692        options.irreversible_quantization_subband_scales,
1693    );
1694    validate_prequantized_htj2k97_image(image, guard_bits, &step_sizes)?;
1695
1696    let mut prequantized_options = options.clone();
1697    prequantized_options.num_decomposition_levels = num_levels;
1698    prequantized_options.reversible = false;
1699    prequantized_options.use_ht_block_coding = true;
1700    prequantized_options.use_mct = false;
1701    prequantized_options.validate_high_throughput_codestream = false;
1702    prequantized_options.component_sampling = Some(
1703        image
1704            .components
1705            .iter()
1706            .map(|component| (component.x_rsiz, component.y_rsiz))
1707            .collect(),
1708    );
1709
1710    let component_resolution_packets = image
1711        .components
1712        .iter()
1713        .enumerate()
1714        .map(|(component_idx, component)| {
1715            prepared_resolution_packets_from_prequantized_component(component_idx, component)
1716        })
1717        .collect::<Result<Vec<_>, &'static str>>()?;
1718    let prepared_resolution_packets =
1719        ordered_prepared_resolution_packets(component_resolution_packets, &prequantized_options)?;
1720    let packet_descriptors = packet_descriptors_for_order(
1721        &prepared_resolution_packets,
1722        1,
1723        prequantized_options.progression_order,
1724    )?;
1725    let mut resolution_packets =
1726        encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
1727    let packetized_tile = packetize_resolution_packets_with_options(
1728        &mut resolution_packets,
1729        &packet_descriptors,
1730        1,
1731        num_components,
1732        prequantized_options.progression_order,
1733        packet_encode::PacketMarkerOptions {
1734            write_sop: prequantized_options.write_sop,
1735            write_eph: prequantized_options.write_eph,
1736            separate_packet_headers: prequantized_options.write_ppm
1737                || prequantized_options.write_ppt,
1738        },
1739        true,
1740        prequantized_options.write_plt
1741            || prequantized_options.write_plm
1742            || prequantized_options.write_ppm
1743            || prequantized_options.write_ppt
1744            || prequantized_options.write_sop
1745            || prequantized_options.write_eph
1746            || prequantized_options.tile_part_packet_limit.is_some(),
1747        accelerator,
1748    )?;
1749
1750    let quant_params: Vec<(u16, u16)> = step_sizes
1751        .iter()
1752        .map(|s| (s.exponent, s.mantissa))
1753        .collect();
1754    let params = EncodeParams {
1755        width: image.width,
1756        height: image.height,
1757        tile_width: image.width,
1758        tile_height: image.height,
1759        num_components,
1760        bit_depth: image.bit_depth,
1761        signed: image.signed,
1762        component_sample_info: Vec::new(),
1763        component_quantization_step_sizes: Vec::new(),
1764        num_decomposition_levels: num_levels,
1765        reversible: false,
1766        code_block_width_exp: prequantized_options.code_block_width_exp,
1767        code_block_height_exp: prequantized_options.code_block_height_exp,
1768        num_layers: 1,
1769        use_mct: false,
1770        guard_bits,
1771        block_coding_mode: BlockCodingMode::HighThroughput,
1772        progression_order: prequantized_options.progression_order,
1773        write_tlm: prequantized_options.write_tlm,
1774        write_plt: prequantized_options.write_plt,
1775        write_plm: prequantized_options.write_plm,
1776        write_ppm: prequantized_options.write_ppm,
1777        write_ppt: prequantized_options.write_ppt,
1778        write_sop: prequantized_options.write_sop,
1779        write_eph: prequantized_options.write_eph,
1780        terminate_coding_passes: false,
1781        component_sampling: prequantized_options
1782            .component_sampling
1783            .clone()
1784            .ok_or("component sampling missing")?,
1785        roi_component_shifts: vec![0; usize::from(num_components)],
1786        precinct_exponents: precinct_exponents_for_options(&prequantized_options, num_levels)?,
1787    };
1788
1789    write_single_tile_packetized_codestream(
1790        &params,
1791        &packetized_tile,
1792        &quant_params,
1793        prequantized_options.tile_part_packet_limit,
1794    )
1795}
1796
1797/// Encode preencoded irreversible 9/7 HTJ2K code-block payloads into a
1798/// codestream.
1799pub fn encode_preencoded_htj2k_97(
1800    image: &PreencodedHtj2k97Image,
1801    options: &EncodeOptions,
1802) -> Result<Vec<u8>, &'static str> {
1803    let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
1804    encode_preencoded_htj2k_97_with_accelerator(image, options, &mut accelerator)
1805}
1806
1807/// Encode preencoded irreversible 9/7 HTJ2K code-block payloads into a
1808/// codestream using optional packetization hooks.
1809pub fn encode_preencoded_htj2k_97_with_accelerator(
1810    image: &PreencodedHtj2k97Image,
1811    options: &EncodeOptions,
1812    accelerator: &mut impl J2kEncodeStageAccelerator,
1813) -> Result<Vec<u8>, &'static str> {
1814    if image.width == 0 || image.height == 0 {
1815        return Err("invalid dimensions");
1816    }
1817    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1818    {
1819        return Err("unsupported component count");
1820    }
1821    if image.bit_depth == 0 || image.bit_depth > 16 {
1822        return Err("unsupported bit depth");
1823    }
1824    validate_irreversible_quantization_profile(options)?;
1825    if image
1826        .components
1827        .iter()
1828        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1829    {
1830        return Err("component sampling factors must be non-zero");
1831    }
1832
1833    let num_components =
1834        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1835    let num_levels = preencoded_97_level_count(&image.components)?;
1836    let guard_bits = options.guard_bits.max(2);
1837    let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
1838        image.bit_depth,
1839        num_levels,
1840        false,
1841        guard_bits,
1842        options.irreversible_quantization_scale,
1843        options.irreversible_quantization_subband_scales,
1844    );
1845    validate_preencoded_htj2k97_image(image, guard_bits, &step_sizes)?;
1846
1847    let mut preencoded_options = options.clone();
1848    preencoded_options.num_decomposition_levels = num_levels;
1849    preencoded_options.reversible = false;
1850    preencoded_options.use_ht_block_coding = true;
1851    preencoded_options.use_mct = false;
1852    preencoded_options.validate_high_throughput_codestream = false;
1853    preencoded_options.component_sampling = Some(
1854        image
1855            .components
1856            .iter()
1857            .map(|component| (component.x_rsiz, component.y_rsiz))
1858            .collect(),
1859    );
1860
1861    let component_resolution_packets = image
1862        .components
1863        .iter()
1864        .enumerate()
1865        .map(|(component_idx, component)| {
1866            prepared_resolution_packets_from_preencoded_component(component_idx, component)
1867        })
1868        .collect::<Result<Vec<_>, &'static str>>()?;
1869    let prepared_resolution_packets =
1870        ordered_prepared_resolution_packets(component_resolution_packets, &preencoded_options)?;
1871    let packet_descriptors = packet_descriptors_for_order(
1872        &prepared_resolution_packets,
1873        1,
1874        preencoded_options.progression_order,
1875    )?;
1876    let mut resolution_packets =
1877        encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
1878    let packetized_tile = packetize_resolution_packets_with_options(
1879        &mut resolution_packets,
1880        &packet_descriptors,
1881        1,
1882        num_components,
1883        preencoded_options.progression_order,
1884        packet_encode::PacketMarkerOptions {
1885            write_sop: preencoded_options.write_sop,
1886            write_eph: preencoded_options.write_eph,
1887            separate_packet_headers: preencoded_options.write_ppm || preencoded_options.write_ppt,
1888        },
1889        true,
1890        preencoded_options.write_plt
1891            || preencoded_options.write_plm
1892            || preencoded_options.write_ppm
1893            || preencoded_options.write_ppt
1894            || preencoded_options.write_sop
1895            || preencoded_options.write_eph
1896            || preencoded_options.tile_part_packet_limit.is_some(),
1897        accelerator,
1898    )?;
1899
1900    let quant_params: Vec<(u16, u16)> = step_sizes
1901        .iter()
1902        .map(|s| (s.exponent, s.mantissa))
1903        .collect();
1904    let params = EncodeParams {
1905        width: image.width,
1906        height: image.height,
1907        tile_width: image.width,
1908        tile_height: image.height,
1909        num_components,
1910        bit_depth: image.bit_depth,
1911        signed: image.signed,
1912        component_sample_info: Vec::new(),
1913        component_quantization_step_sizes: Vec::new(),
1914        num_decomposition_levels: num_levels,
1915        reversible: false,
1916        code_block_width_exp: preencoded_options.code_block_width_exp,
1917        code_block_height_exp: preencoded_options.code_block_height_exp,
1918        num_layers: 1,
1919        use_mct: false,
1920        guard_bits,
1921        block_coding_mode: BlockCodingMode::HighThroughput,
1922        progression_order: preencoded_options.progression_order,
1923        write_tlm: preencoded_options.write_tlm,
1924        write_plt: preencoded_options.write_plt,
1925        write_plm: preencoded_options.write_plm,
1926        write_ppm: preencoded_options.write_ppm,
1927        write_ppt: preencoded_options.write_ppt,
1928        write_sop: preencoded_options.write_sop,
1929        write_eph: preencoded_options.write_eph,
1930        terminate_coding_passes: false,
1931        component_sampling: preencoded_options
1932            .component_sampling
1933            .clone()
1934            .ok_or("component sampling missing")?,
1935        roi_component_shifts: vec![0; usize::from(num_components)],
1936        precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
1937    };
1938
1939    write_single_tile_packetized_codestream(
1940        &params,
1941        &packetized_tile,
1942        &quant_params,
1943        preencoded_options.tile_part_packet_limit,
1944    )
1945}
1946
1947/// Encode preencoded irreversible 9/7 HTJ2K code-block payloads into a
1948/// codestream, consuming the image so code-block payloads can move into packet
1949/// preparation without cloning.
1950pub fn encode_preencoded_htj2k_97_owned_with_accelerator(
1951    image: PreencodedHtj2k97Image,
1952    options: &EncodeOptions,
1953    accelerator: &mut impl J2kEncodeStageAccelerator,
1954) -> Result<Vec<u8>, &'static str> {
1955    if image.width == 0 || image.height == 0 {
1956        return Err("invalid dimensions");
1957    }
1958    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
1959    {
1960        return Err("unsupported component count");
1961    }
1962    if image.bit_depth == 0 || image.bit_depth > 16 {
1963        return Err("unsupported bit depth");
1964    }
1965    validate_irreversible_quantization_profile(options)?;
1966    if image
1967        .components
1968        .iter()
1969        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
1970    {
1971        return Err("component sampling factors must be non-zero");
1972    }
1973
1974    let width = image.width;
1975    let height = image.height;
1976    let bit_depth = image.bit_depth;
1977    let signed = image.signed;
1978    let num_components =
1979        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
1980    let num_levels = preencoded_97_level_count(&image.components)?;
1981    let guard_bits = options.guard_bits.max(2);
1982    let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
1983        bit_depth,
1984        num_levels,
1985        false,
1986        guard_bits,
1987        options.irreversible_quantization_scale,
1988        options.irreversible_quantization_subband_scales,
1989    );
1990    validate_preencoded_htj2k97_image(&image, guard_bits, &step_sizes)?;
1991
1992    let component_sampling = image
1993        .components
1994        .iter()
1995        .map(|component| (component.x_rsiz, component.y_rsiz))
1996        .collect::<Vec<_>>();
1997    let mut preencoded_options = options.clone();
1998    preencoded_options.num_decomposition_levels = num_levels;
1999    preencoded_options.reversible = false;
2000    preencoded_options.use_ht_block_coding = true;
2001    preencoded_options.use_mct = false;
2002    preencoded_options.validate_high_throughput_codestream = false;
2003    preencoded_options.component_sampling = Some(component_sampling);
2004
2005    let component_resolution_packets = image
2006        .components
2007        .into_iter()
2008        .enumerate()
2009        .map(|(component_idx, component)| {
2010            prepared_resolution_packets_from_preencoded_component_owned(component_idx, component)
2011        })
2012        .collect::<Result<Vec<_>, &'static str>>()?;
2013    let prepared_resolution_packets =
2014        ordered_prepared_resolution_packets(component_resolution_packets, &preencoded_options)?;
2015    let packet_descriptors = packet_descriptors_for_order(
2016        &prepared_resolution_packets,
2017        1,
2018        preencoded_options.progression_order,
2019    )?;
2020    let mut resolution_packets =
2021        encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
2022    let packetized_tile = packetize_resolution_packets_with_options(
2023        &mut resolution_packets,
2024        &packet_descriptors,
2025        1,
2026        num_components,
2027        preencoded_options.progression_order,
2028        packet_encode::PacketMarkerOptions {
2029            write_sop: preencoded_options.write_sop,
2030            write_eph: preencoded_options.write_eph,
2031            separate_packet_headers: preencoded_options.write_ppm || preencoded_options.write_ppt,
2032        },
2033        true,
2034        preencoded_options.write_plt
2035            || preencoded_options.write_plm
2036            || preencoded_options.write_ppm
2037            || preencoded_options.write_ppt
2038            || preencoded_options.write_sop
2039            || preencoded_options.write_eph
2040            || preencoded_options.tile_part_packet_limit.is_some(),
2041        accelerator,
2042    )?;
2043
2044    let quant_params: Vec<(u16, u16)> = step_sizes
2045        .iter()
2046        .map(|s| (s.exponent, s.mantissa))
2047        .collect();
2048    let params = EncodeParams {
2049        width,
2050        height,
2051        tile_width: width,
2052        tile_height: height,
2053        num_components,
2054        bit_depth,
2055        signed,
2056        component_sample_info: Vec::new(),
2057        component_quantization_step_sizes: Vec::new(),
2058        num_decomposition_levels: num_levels,
2059        reversible: false,
2060        code_block_width_exp: preencoded_options.code_block_width_exp,
2061        code_block_height_exp: preencoded_options.code_block_height_exp,
2062        num_layers: 1,
2063        use_mct: false,
2064        guard_bits,
2065        block_coding_mode: BlockCodingMode::HighThroughput,
2066        progression_order: preencoded_options.progression_order,
2067        write_tlm: preencoded_options.write_tlm,
2068        write_plt: preencoded_options.write_plt,
2069        write_plm: preencoded_options.write_plm,
2070        write_ppm: preencoded_options.write_ppm,
2071        write_ppt: preencoded_options.write_ppt,
2072        write_sop: preencoded_options.write_sop,
2073        write_eph: preencoded_options.write_eph,
2074        terminate_coding_passes: false,
2075        component_sampling: preencoded_options
2076            .component_sampling
2077            .clone()
2078            .ok_or("component sampling missing")?,
2079        roi_component_shifts: vec![0; usize::from(num_components)],
2080        precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
2081    };
2082
2083    write_single_tile_packetized_codestream(
2084        &params,
2085        &packetized_tile,
2086        &quant_params,
2087        preencoded_options.tile_part_packet_limit,
2088    )
2089}
2090
2091/// Encode compact preencoded irreversible 9/7 HTJ2K code-block payloads into a
2092/// codestream, borrowing code-block ranges from one image-level payload buffer
2093/// during packetization.
2094pub fn encode_preencoded_htj2k_97_compact_owned_with_accelerator(
2095    image: PreencodedHtj2k97CompactImage,
2096    options: &EncodeOptions,
2097    accelerator: &mut impl J2kEncodeStageAccelerator,
2098) -> Result<Vec<u8>, &'static str> {
2099    if image.width == 0 || image.height == 0 {
2100        return Err("invalid dimensions");
2101    }
2102    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
2103    {
2104        return Err("unsupported component count");
2105    }
2106    if image.bit_depth == 0 || image.bit_depth > 16 {
2107        return Err("unsupported bit depth");
2108    }
2109    if options.write_plt
2110        || options.write_plm
2111        || options.write_sop
2112        || options.write_eph
2113        || options.tile_part_packet_limit.is_some()
2114    {
2115        return Err(
2116            "compact preencoded HTJ2K encode does not support packet marker or tile-part options",
2117        );
2118    }
2119    validate_irreversible_quantization_profile(options)?;
2120    if image
2121        .components
2122        .iter()
2123        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
2124    {
2125        return Err("component sampling factors must be non-zero");
2126    }
2127
2128    let width = image.width;
2129    let height = image.height;
2130    let bit_depth = image.bit_depth;
2131    let signed = image.signed;
2132    let num_components =
2133        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
2134    let num_levels = preencoded_compact_97_level_count(&image.components)?;
2135    let guard_bits = options.guard_bits.max(2);
2136    let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
2137        bit_depth,
2138        num_levels,
2139        false,
2140        guard_bits,
2141        options.irreversible_quantization_scale,
2142        options.irreversible_quantization_subband_scales,
2143    );
2144    validate_preencoded_compact_htj2k97_image(&image, guard_bits, &step_sizes)?;
2145
2146    let component_sampling = image
2147        .components
2148        .iter()
2149        .map(|component| (component.x_rsiz, component.y_rsiz))
2150        .collect::<Vec<_>>();
2151    let mut preencoded_options = options.clone();
2152    preencoded_options.num_decomposition_levels = num_levels;
2153    preencoded_options.reversible = false;
2154    preencoded_options.use_ht_block_coding = true;
2155    preencoded_options.use_mct = false;
2156    preencoded_options.validate_high_throughput_codestream = false;
2157    preencoded_options.component_sampling = Some(component_sampling);
2158
2159    let PreencodedHtj2k97CompactImage {
2160        payload,
2161        components,
2162        ..
2163    } = image;
2164    let component_resolution_packets = components
2165        .iter()
2166        .enumerate()
2167        .map(|(component_idx, component)| {
2168            prepared_resolution_packets_from_preencoded_compact_component(
2169                component_idx,
2170                component,
2171                &payload,
2172            )
2173        })
2174        .collect::<Result<Vec<_>, &'static str>>()?;
2175    let prepared_resolution_packets = ordered_prepared_compact_resolution_packets(
2176        component_resolution_packets,
2177        &preencoded_options,
2178    )?;
2179    let packet_descriptors = packet_descriptors_for_compact_order(
2180        &prepared_resolution_packets,
2181        1,
2182        preencoded_options.progression_order,
2183    )?;
2184    let packetization_resolutions =
2185        public_packetization_resolutions_from_compact(&prepared_resolution_packets);
2186    let packetization_job = J2kPacketizationEncodeJob {
2187        resolution_count: packetization_resolutions.len() as u32,
2188        num_layers: 1,
2189        num_components,
2190        code_block_count: count_compact_code_blocks(&prepared_resolution_packets)?,
2191        progression_order: public_packetization_progression_order(
2192            preencoded_options.progression_order,
2193        ),
2194        packet_descriptors: &packet_descriptors,
2195        resolutions: &packetization_resolutions,
2196    };
2197    let tile_data = accelerator
2198        .encode_packetization(packetization_job)?
2199        .map_or_else(
2200            || crate::encode_j2k_packetization_scalar(packetization_job),
2201            Ok,
2202        )?;
2203
2204    let quant_params: Vec<(u16, u16)> = step_sizes
2205        .iter()
2206        .map(|s| (s.exponent, s.mantissa))
2207        .collect();
2208    let params = EncodeParams {
2209        width,
2210        height,
2211        tile_width: width,
2212        tile_height: height,
2213        num_components,
2214        bit_depth,
2215        signed,
2216        component_sample_info: Vec::new(),
2217        component_quantization_step_sizes: Vec::new(),
2218        num_decomposition_levels: num_levels,
2219        reversible: false,
2220        code_block_width_exp: preencoded_options.code_block_width_exp,
2221        code_block_height_exp: preencoded_options.code_block_height_exp,
2222        num_layers: 1,
2223        use_mct: false,
2224        guard_bits,
2225        block_coding_mode: BlockCodingMode::HighThroughput,
2226        progression_order: preencoded_options.progression_order,
2227        write_tlm: preencoded_options.write_tlm,
2228        write_plt: preencoded_options.write_plt,
2229        write_plm: preencoded_options.write_plm,
2230        write_ppm: preencoded_options.write_ppm,
2231        write_ppt: preencoded_options.write_ppt,
2232        write_sop: preencoded_options.write_sop,
2233        write_eph: preencoded_options.write_eph,
2234        terminate_coding_passes: false,
2235        component_sampling: preencoded_options
2236            .component_sampling
2237            .clone()
2238            .ok_or("component sampling missing")?,
2239        roi_component_shifts: vec![0; usize::from(num_components)],
2240        precinct_exponents: precinct_exponents_for_options(&preencoded_options, num_levels)?,
2241    };
2242
2243    Ok(codestream_write::write_codestream(
2244        &params,
2245        &tile_data,
2246        &quant_params,
2247    ))
2248}
2249
2250fn validate_precomputed_dwt_geometry(image: &PrecomputedHtj2k53Image) -> Result<(), &'static str> {
2251    for component in &image.components {
2252        let component_width = image.width.div_ceil(u32::from(component.x_rsiz));
2253        let component_height = image.height.div_ceil(u32::from(component.y_rsiz));
2254        validate_precomputed_component_dwt_geometry(
2255            &component.dwt,
2256            component_width,
2257            component_height,
2258        )?;
2259    }
2260
2261    Ok(())
2262}
2263
2264fn validate_precomputed_dwt97_geometry(
2265    image: &PrecomputedHtj2k97Image,
2266) -> Result<(), &'static str> {
2267    for component in &image.components {
2268        let component_width = image.width.div_ceil(u32::from(component.x_rsiz));
2269        let component_height = image.height.div_ceil(u32::from(component.y_rsiz));
2270        validate_precomputed_component_dwt_geometry(
2271            &component.dwt,
2272            component_width,
2273            component_height,
2274        )?;
2275    }
2276
2277    Ok(())
2278}
2279
2280fn validate_precomputed_component_dwt_geometry(
2281    dwt: &impl PrecomputedDwtGeometryView,
2282    component_width: u32,
2283    component_height: u32,
2284) -> Result<(), &'static str> {
2285    if let Some(highest_level) = dwt.last_level_geometry() {
2286        if highest_level.width != component_width || highest_level.height != component_height {
2287            return Err("precomputed DWT component dimensions mismatch");
2288        }
2289    }
2290
2291    let mut expected_width = component_width;
2292    let mut expected_height = component_height;
2293    for level_index in (0..dwt.level_count()).rev() {
2294        let level = dwt.level_geometry(level_index);
2295        let low_width = expected_width.div_ceil(2);
2296        let low_height = expected_height.div_ceil(2);
2297        let high_width = expected_width / 2;
2298        let high_height = expected_height / 2;
2299
2300        if level.width != expected_width
2301            || level.height != expected_height
2302            || level.low_width != low_width
2303            || level.low_height != low_height
2304            || level.high_width != high_width
2305            || level.high_height != high_height
2306        {
2307            return Err("precomputed DWT recursive geometry mismatch");
2308        }
2309        validate_band_len(level.hl_len, high_width, low_height)?;
2310        validate_band_len(level.lh_len, low_width, high_height)?;
2311        validate_band_len(level.hh_len, high_width, high_height)?;
2312
2313        expected_width = low_width;
2314        expected_height = low_height;
2315    }
2316
2317    if dwt.ll_width() != expected_width || dwt.ll_height() != expected_height {
2318        return Err("precomputed DWT component dimensions mismatch");
2319    }
2320    validate_band_len(dwt.ll_len(), expected_width, expected_height)
2321}
2322
2323#[derive(Debug, Clone, Copy)]
2324struct PrecomputedDwtLevelGeometry {
2325    width: u32,
2326    height: u32,
2327    low_width: u32,
2328    low_height: u32,
2329    high_width: u32,
2330    high_height: u32,
2331    hl_len: usize,
2332    lh_len: usize,
2333    hh_len: usize,
2334}
2335
2336trait PrecomputedDwtGeometryView {
2337    fn ll_len(&self) -> usize;
2338    fn ll_width(&self) -> u32;
2339    fn ll_height(&self) -> u32;
2340    fn level_count(&self) -> usize;
2341    fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry;
2342
2343    fn last_level_geometry(&self) -> Option<PrecomputedDwtLevelGeometry> {
2344        self.level_count()
2345            .checked_sub(1)
2346            .map(|index| self.level_geometry(index))
2347    }
2348}
2349
2350impl PrecomputedDwtGeometryView for J2kForwardDwt53Output {
2351    fn ll_len(&self) -> usize {
2352        self.ll.len()
2353    }
2354
2355    fn ll_width(&self) -> u32 {
2356        self.ll_width
2357    }
2358
2359    fn ll_height(&self) -> u32 {
2360        self.ll_height
2361    }
2362
2363    fn level_count(&self) -> usize {
2364        self.levels.len()
2365    }
2366
2367    fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry {
2368        let level = &self.levels[index];
2369        PrecomputedDwtLevelGeometry {
2370            width: level.width,
2371            height: level.height,
2372            low_width: level.low_width,
2373            low_height: level.low_height,
2374            high_width: level.high_width,
2375            high_height: level.high_height,
2376            hl_len: level.hl.len(),
2377            lh_len: level.lh.len(),
2378            hh_len: level.hh.len(),
2379        }
2380    }
2381}
2382
2383impl PrecomputedDwtGeometryView for J2kForwardDwt97Output {
2384    fn ll_len(&self) -> usize {
2385        self.ll.len()
2386    }
2387
2388    fn ll_width(&self) -> u32 {
2389        self.ll_width
2390    }
2391
2392    fn ll_height(&self) -> u32 {
2393        self.ll_height
2394    }
2395
2396    fn level_count(&self) -> usize {
2397        self.levels.len()
2398    }
2399
2400    fn level_geometry(&self, index: usize) -> PrecomputedDwtLevelGeometry {
2401        let level = &self.levels[index];
2402        PrecomputedDwtLevelGeometry {
2403            width: level.width,
2404            height: level.height,
2405            low_width: level.low_width,
2406            low_height: level.low_height,
2407            high_width: level.high_width,
2408            high_height: level.high_height,
2409            hl_len: level.hl.len(),
2410            lh_len: level.lh.len(),
2411            hh_len: level.hh.len(),
2412        }
2413    }
2414}
2415
2416fn precomputed_level_count(components: &[PrecomputedHtj2k53Component]) -> Result<u8, &'static str> {
2417    let first = components
2418        .first()
2419        .ok_or("unsupported component count")?
2420        .dwt
2421        .levels
2422        .len();
2423    if components
2424        .iter()
2425        .any(|component| component.dwt.levels.len() != first)
2426    {
2427        return Err("precomputed components must use the same decomposition level count");
2428    }
2429    u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2430}
2431
2432fn precomputed_97_level_count(
2433    components: &[PrecomputedHtj2k97Component],
2434) -> Result<u8, &'static str> {
2435    let first = components
2436        .first()
2437        .ok_or("unsupported component count")?
2438        .dwt
2439        .levels
2440        .len();
2441    if components
2442        .iter()
2443        .any(|component| component.dwt.levels.len() != first)
2444    {
2445        return Err("precomputed components must use the same decomposition level count");
2446    }
2447    u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2448}
2449
2450fn prequantized_97_level_count(
2451    components: &[PrequantizedHtj2k97Component],
2452) -> Result<u8, &'static str> {
2453    let first = components
2454        .first()
2455        .ok_or("unsupported component count")?
2456        .resolutions
2457        .len()
2458        .checked_sub(1)
2459        .ok_or("prequantized components must contain at least one decomposition level")?;
2460    if components
2461        .iter()
2462        .any(|component| component.resolutions.len() != first + 1)
2463    {
2464        return Err("prequantized components must use the same decomposition level count");
2465    }
2466    u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2467}
2468
2469fn preencoded_97_level_count(
2470    components: &[PreencodedHtj2k97Component],
2471) -> Result<u8, &'static str> {
2472    let first = components
2473        .first()
2474        .ok_or("unsupported component count")?
2475        .resolutions
2476        .len()
2477        .checked_sub(1)
2478        .ok_or("preencoded components must contain at least one decomposition level")?;
2479    if components
2480        .iter()
2481        .any(|component| component.resolutions.len() != first + 1)
2482    {
2483        return Err("preencoded components must use the same decomposition level count");
2484    }
2485    u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2486}
2487
2488fn preencoded_compact_97_level_count(
2489    components: &[PreencodedHtj2k97CompactComponent],
2490) -> Result<u8, &'static str> {
2491    let first = components
2492        .first()
2493        .ok_or("unsupported component count")?
2494        .resolutions
2495        .len()
2496        .checked_sub(1)
2497        .ok_or("preencoded components must contain at least one decomposition level")?;
2498    if components
2499        .iter()
2500        .any(|component| component.resolutions.len() != first + 1)
2501    {
2502        return Err("preencoded components must use the same decomposition level count");
2503    }
2504    u8::try_from(first).map_err(|_| "decomposition level count exceeds u8")
2505}
2506
2507fn validate_prequantized_htj2k97_image(
2508    image: &PrequantizedHtj2k97Image,
2509    guard_bits: u8,
2510    step_sizes: &[QuantStepSize],
2511) -> Result<(), &'static str> {
2512    for component in &image.components {
2513        if component.resolutions.is_empty() {
2514            return Err("prequantized components must contain at least one resolution");
2515        }
2516        validate_prequantized_resolution(
2517            &component.resolutions[0],
2518            &[J2kSubBandType::LowLow],
2519            guard_bits,
2520            &step_sizes[0..1],
2521        )?;
2522        for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
2523            let step_base = 1 + (level_index - 1) * 3;
2524            validate_prequantized_resolution(
2525                resolution,
2526                &[
2527                    J2kSubBandType::HighLow,
2528                    J2kSubBandType::LowHigh,
2529                    J2kSubBandType::HighHigh,
2530                ],
2531                guard_bits,
2532                &step_sizes[step_base..step_base + 3],
2533            )?;
2534        }
2535    }
2536
2537    Ok(())
2538}
2539
2540fn validate_preencoded_htj2k97_image(
2541    image: &PreencodedHtj2k97Image,
2542    guard_bits: u8,
2543    step_sizes: &[QuantStepSize],
2544) -> Result<(), &'static str> {
2545    for component in &image.components {
2546        if component.resolutions.is_empty() {
2547            return Err("preencoded components must contain at least one resolution");
2548        }
2549        validate_preencoded_resolution(
2550            &component.resolutions[0],
2551            &[J2kSubBandType::LowLow],
2552            guard_bits,
2553            &step_sizes[0..1],
2554        )?;
2555        for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
2556            let step_base = 1 + (level_index - 1) * 3;
2557            validate_preencoded_resolution(
2558                resolution,
2559                &[
2560                    J2kSubBandType::HighLow,
2561                    J2kSubBandType::LowHigh,
2562                    J2kSubBandType::HighHigh,
2563                ],
2564                guard_bits,
2565                &step_sizes[step_base..step_base + 3],
2566            )?;
2567        }
2568    }
2569
2570    Ok(())
2571}
2572
2573fn validate_preencoded_compact_htj2k97_image(
2574    image: &PreencodedHtj2k97CompactImage,
2575    guard_bits: u8,
2576    step_sizes: &[QuantStepSize],
2577) -> Result<(), &'static str> {
2578    for component in &image.components {
2579        if component.resolutions.is_empty() {
2580            return Err("preencoded components must contain at least one resolution");
2581        }
2582        validate_preencoded_compact_resolution(
2583            &component.resolutions[0],
2584            &[J2kSubBandType::LowLow],
2585            guard_bits,
2586            &step_sizes[0..1],
2587            image.payload.len(),
2588        )?;
2589        for (level_index, resolution) in component.resolutions.iter().enumerate().skip(1) {
2590            let step_base = 1 + (level_index - 1) * 3;
2591            validate_preencoded_compact_resolution(
2592                resolution,
2593                &[
2594                    J2kSubBandType::HighLow,
2595                    J2kSubBandType::LowHigh,
2596                    J2kSubBandType::HighHigh,
2597                ],
2598                guard_bits,
2599                &step_sizes[step_base..step_base + 3],
2600                image.payload.len(),
2601            )?;
2602        }
2603    }
2604
2605    Ok(())
2606}
2607
2608fn validate_prequantized_resolution(
2609    resolution: &PrequantizedHtj2k97Resolution,
2610    expected_subbands: &[J2kSubBandType],
2611    guard_bits: u8,
2612    step_sizes: &[QuantStepSize],
2613) -> Result<(), &'static str> {
2614    if resolution.subbands.len() != expected_subbands.len() {
2615        return Err("prequantized resolution subband count mismatch");
2616    }
2617    for ((subband, expected_subband), step_size) in resolution
2618        .subbands
2619        .iter()
2620        .zip(expected_subbands)
2621        .zip(step_sizes)
2622    {
2623        if subband.sub_band_type != *expected_subband {
2624            return Err("prequantized resolution subband order mismatch");
2625        }
2626        let expected_blocks = subband
2627            .num_cbs_x
2628            .checked_mul(subband.num_cbs_y)
2629            .ok_or("prequantized code-block count overflow")?;
2630        if expected_blocks == 0 {
2631            if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
2632                return Err("empty prequantized subbands must not contain code-block data");
2633            }
2634            continue;
2635        }
2636        debug_assert!(step_size.exponent <= u16::from(u8::MAX));
2637        let expected_total_bitplanes = guard_bits
2638            .saturating_add(step_size.exponent as u8)
2639            .saturating_sub(1);
2640        if subband.total_bitplanes != expected_total_bitplanes {
2641            return Err("prequantized subband bitplane count mismatch");
2642        }
2643        if usize::try_from(expected_blocks).map_err(|_| "prequantized code-block count overflow")?
2644            != subband.code_blocks.len()
2645        {
2646            return Err("prequantized code-block count mismatch");
2647        }
2648        for block in &subband.code_blocks {
2649            if block.width == 0 || block.height == 0 {
2650                return Err("prequantized code-block dimensions must be non-zero");
2651            }
2652            validate_band_len(block.coefficients.len(), block.width, block.height)?;
2653        }
2654    }
2655
2656    Ok(())
2657}
2658
2659fn validate_preencoded_resolution(
2660    resolution: &PreencodedHtj2k97Resolution,
2661    expected_subbands: &[J2kSubBandType],
2662    guard_bits: u8,
2663    step_sizes: &[QuantStepSize],
2664) -> Result<(), &'static str> {
2665    if resolution.subbands.len() != expected_subbands.len() {
2666        return Err("preencoded resolution subband count mismatch");
2667    }
2668    for ((subband, expected_subband), step_size) in resolution
2669        .subbands
2670        .iter()
2671        .zip(expected_subbands)
2672        .zip(step_sizes)
2673    {
2674        if subband.sub_band_type != *expected_subband {
2675            return Err("preencoded resolution subband order mismatch");
2676        }
2677        let expected_blocks = subband
2678            .num_cbs_x
2679            .checked_mul(subband.num_cbs_y)
2680            .ok_or("preencoded code-block count overflow")?;
2681        if expected_blocks == 0 {
2682            if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
2683                return Err("empty preencoded subbands must not contain code-block data");
2684            }
2685            continue;
2686        }
2687        debug_assert!(step_size.exponent <= u16::from(u8::MAX));
2688        let expected_total_bitplanes = guard_bits
2689            .saturating_add(step_size.exponent as u8)
2690            .saturating_sub(1);
2691        if subband.total_bitplanes != expected_total_bitplanes {
2692            return Err("preencoded subband bitplane count mismatch");
2693        }
2694        if usize::try_from(expected_blocks).map_err(|_| "preencoded code-block count overflow")?
2695            != subband.code_blocks.len()
2696        {
2697            return Err("preencoded code-block count mismatch");
2698        }
2699        for block in &subband.code_blocks {
2700            if block.width == 0 || block.height == 0 {
2701                return Err("preencoded code-block dimensions must be non-zero");
2702            }
2703            validate_preencoded_code_block_payload(&block.encoded, subband.total_bitplanes)?;
2704        }
2705    }
2706
2707    Ok(())
2708}
2709
2710fn validate_preencoded_compact_resolution(
2711    resolution: &PreencodedHtj2k97CompactResolution,
2712    expected_subbands: &[J2kSubBandType],
2713    guard_bits: u8,
2714    step_sizes: &[QuantStepSize],
2715    payload_len: usize,
2716) -> Result<(), &'static str> {
2717    if resolution.subbands.len() != expected_subbands.len() {
2718        return Err("preencoded resolution subband count mismatch");
2719    }
2720    for ((subband, expected_subband), step_size) in resolution
2721        .subbands
2722        .iter()
2723        .zip(expected_subbands)
2724        .zip(step_sizes)
2725    {
2726        if subband.sub_band_type != *expected_subband {
2727            return Err("preencoded resolution subband order mismatch");
2728        }
2729        let expected_blocks = subband
2730            .num_cbs_x
2731            .checked_mul(subband.num_cbs_y)
2732            .ok_or("preencoded code-block count overflow")?;
2733        if expected_blocks == 0 {
2734            if subband.total_bitplanes != 0 || !subband.code_blocks.is_empty() {
2735                return Err("empty preencoded subbands must not contain code-block data");
2736            }
2737            continue;
2738        }
2739        debug_assert!(step_size.exponent <= u16::from(u8::MAX));
2740        let expected_total_bitplanes = guard_bits
2741            .saturating_add(step_size.exponent as u8)
2742            .saturating_sub(1);
2743        if subband.total_bitplanes != expected_total_bitplanes {
2744            return Err("preencoded subband bitplane count mismatch");
2745        }
2746        if usize::try_from(expected_blocks).map_err(|_| "preencoded code-block count overflow")?
2747            != subband.code_blocks.len()
2748        {
2749            return Err("preencoded code-block count mismatch");
2750        }
2751        for block in &subband.code_blocks {
2752            if block.width == 0 || block.height == 0 {
2753                return Err("preencoded code-block dimensions must be non-zero");
2754            }
2755            validate_preencoded_compact_code_block_payload(
2756                block,
2757                payload_len,
2758                subband.total_bitplanes,
2759            )?;
2760        }
2761    }
2762
2763    Ok(())
2764}
2765
2766fn validate_preencoded_code_block_payload(
2767    block: &EncodedHtJ2kCodeBlock,
2768    total_bitplanes: u8,
2769) -> Result<(), &'static str> {
2770    let data_len = u32::try_from(block.data.len()).map_err(|_| "HTJ2K payload too large")?;
2771    if block.num_coding_passes == 0 {
2772        if data_len != 0 || block.cleanup_length != 0 || block.refinement_length != 0 {
2773            return Err("empty HTJ2K code-block payload metadata mismatch");
2774        }
2775        if block.num_zero_bitplanes != total_bitplanes {
2776            return Err("empty HTJ2K code-block zero-bitplane count mismatch");
2777        }
2778        return Ok(());
2779    }
2780    if block.num_coding_passes > 164 {
2781        return Err("HTJ2K code-block coding pass count out of range");
2782    }
2783    if block.num_zero_bitplanes >= total_bitplanes {
2784        return Err("HTJ2K code-block zero-bitplane count out of range");
2785    }
2786    let segment_len = block
2787        .cleanup_length
2788        .checked_add(block.refinement_length)
2789        .ok_or("HTJ2K payload segment length overflow")?;
2790    if segment_len != data_len {
2791        return Err("HTJ2K payload segment length mismatch");
2792    }
2793    Ok(())
2794}
2795
2796fn validate_preencoded_compact_code_block_payload(
2797    block: &PreencodedHtj2k97CompactCodeBlock,
2798    payload_len: usize,
2799    total_bitplanes: u8,
2800) -> Result<(), &'static str> {
2801    if block.payload_range.start > block.payload_range.end || block.payload_range.end > payload_len
2802    {
2803        return Err("HTJ2K payload range out of bounds");
2804    }
2805    let data_len = u32::try_from(block.payload_range.end - block.payload_range.start)
2806        .map_err(|_| "HTJ2K payload too large")?;
2807    if block.num_coding_passes == 0 {
2808        if data_len != 0 || block.cleanup_length != 0 || block.refinement_length != 0 {
2809            return Err("empty HTJ2K code-block payload metadata mismatch");
2810        }
2811        if block.num_zero_bitplanes != total_bitplanes {
2812            return Err("empty HTJ2K code-block zero-bitplane count mismatch");
2813        }
2814        return Ok(());
2815    }
2816    if block.num_coding_passes > 164 {
2817        return Err("HTJ2K code-block coding pass count out of range");
2818    }
2819    if block.num_zero_bitplanes >= total_bitplanes {
2820        return Err("HTJ2K code-block zero-bitplane count out of range");
2821    }
2822    let segment_len = block
2823        .cleanup_length
2824        .checked_add(block.refinement_length)
2825        .ok_or("HTJ2K payload segment length overflow")?;
2826    if segment_len != data_len {
2827        return Err("HTJ2K payload segment length mismatch");
2828    }
2829    Ok(())
2830}
2831
2832fn prepared_resolution_packets_from_prequantized_component(
2833    component_idx: usize,
2834    component: &PrequantizedHtj2k97Component,
2835) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
2836    let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2837    component
2838        .resolutions
2839        .iter()
2840        .enumerate()
2841        .map(|(resolution_idx, resolution)| {
2842            Ok(PreparedResolutionPacket {
2843                component: component_idx,
2844                resolution: u32::try_from(resolution_idx)
2845                    .map_err(|_| "resolution index exceeds u32")?,
2846                precinct: 0,
2847                subbands: resolution
2848                    .subbands
2849                    .iter()
2850                    .map(prepared_subband_from_prequantized)
2851                    .collect::<Result<Vec<_>, &'static str>>()?,
2852            })
2853        })
2854        .collect()
2855}
2856
2857fn prepared_subband_from_prequantized(
2858    subband: &PrequantizedHtj2k97Subband,
2859) -> Result<PreparedEncodeSubband, &'static str> {
2860    Ok(PreparedEncodeSubband {
2861        code_blocks: subband
2862            .code_blocks
2863            .iter()
2864            .map(|block| PreparedEncodeCodeBlock {
2865                coefficients: block.coefficients.iter().copied().map(i64::from).collect(),
2866                width: block.width,
2867                height: block.height,
2868            })
2869            .collect(),
2870        preencoded_ht_code_blocks: None,
2871        num_cbs_x: subband.num_cbs_x,
2872        num_cbs_y: subband.num_cbs_y,
2873        code_block_width: subband
2874            .code_blocks
2875            .iter()
2876            .map(|block| block.width)
2877            .max()
2878            .unwrap_or(0),
2879        code_block_height: subband
2880            .code_blocks
2881            .iter()
2882            .map(|block| block.height)
2883            .max()
2884            .unwrap_or(0),
2885        width: precomputed_subband_width(
2886            subband.num_cbs_x,
2887            subband.code_blocks.iter().map(|block| block.width),
2888        ),
2889        height: precomputed_subband_height(
2890            subband.num_cbs_x,
2891            subband.num_cbs_y,
2892            subband.code_blocks.iter().map(|block| block.height),
2893        ),
2894        sub_band_type: internal_sub_band_type(subband.sub_band_type),
2895        total_bitplanes: subband.total_bitplanes,
2896        block_coding_mode: BlockCodingMode::HighThroughput,
2897        ht_target_coding_passes: 1,
2898    })
2899}
2900
2901fn precomputed_subband_width(width_in_blocks: u32, widths: impl Iterator<Item = u32>) -> u32 {
2902    if width_in_blocks == 0 {
2903        return 0;
2904    }
2905
2906    widths.take(width_in_blocks as usize).sum()
2907}
2908
2909fn precomputed_subband_height(
2910    width_in_blocks: u32,
2911    height_in_blocks: u32,
2912    heights: impl Iterator<Item = u32>,
2913) -> u32 {
2914    if width_in_blocks == 0 || height_in_blocks == 0 {
2915        return 0;
2916    }
2917
2918    heights
2919        .step_by(width_in_blocks as usize)
2920        .take(height_in_blocks as usize)
2921        .sum()
2922}
2923
2924fn prepared_resolution_packets_from_preencoded_component(
2925    component_idx: usize,
2926    component: &PreencodedHtj2k97Component,
2927) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
2928    let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2929    component
2930        .resolutions
2931        .iter()
2932        .enumerate()
2933        .map(|(resolution_idx, resolution)| {
2934            Ok(PreparedResolutionPacket {
2935                component: component_idx,
2936                resolution: u32::try_from(resolution_idx)
2937                    .map_err(|_| "resolution index exceeds u32")?,
2938                precinct: 0,
2939                subbands: resolution
2940                    .subbands
2941                    .iter()
2942                    .map(prepared_subband_from_preencoded)
2943                    .collect::<Result<Vec<_>, &'static str>>()?,
2944            })
2945        })
2946        .collect()
2947}
2948
2949fn prepared_resolution_packets_from_preencoded_component_owned(
2950    component_idx: usize,
2951    component: PreencodedHtj2k97Component,
2952) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
2953    let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2954    component
2955        .resolutions
2956        .into_iter()
2957        .enumerate()
2958        .map(|(resolution_idx, resolution)| {
2959            Ok(PreparedResolutionPacket {
2960                component: component_idx,
2961                resolution: u32::try_from(resolution_idx)
2962                    .map_err(|_| "resolution index exceeds u32")?,
2963                precinct: 0,
2964                subbands: resolution
2965                    .subbands
2966                    .into_iter()
2967                    .map(prepared_subband_from_preencoded_owned)
2968                    .collect::<Result<Vec<_>, &'static str>>()?,
2969            })
2970        })
2971        .collect()
2972}
2973
2974fn prepared_resolution_packets_from_preencoded_compact_component<'a>(
2975    component_idx: usize,
2976    component: &'a PreencodedHtj2k97CompactComponent,
2977    payload: &'a [u8],
2978) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
2979    let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
2980    component
2981        .resolutions
2982        .iter()
2983        .enumerate()
2984        .map(|(resolution_idx, resolution)| {
2985            Ok(PreparedCompactResolutionPacket {
2986                component: component_idx,
2987                resolution: u32::try_from(resolution_idx)
2988                    .map_err(|_| "resolution index exceeds u32")?,
2989                precinct: 0,
2990                subbands: resolution
2991                    .subbands
2992                    .iter()
2993                    .map(|subband| prepared_subband_from_preencoded_compact(subband, payload))
2994                    .collect::<Result<Vec<_>, &'static str>>()?,
2995            })
2996        })
2997        .collect()
2998}
2999
3000fn prepared_subband_from_preencoded(
3001    subband: &PreencodedHtj2k97Subband,
3002) -> Result<PreparedEncodeSubband, &'static str> {
3003    Ok(PreparedEncodeSubband {
3004        code_blocks: subband
3005            .code_blocks
3006            .iter()
3007            .map(|block| PreparedEncodeCodeBlock {
3008                coefficients: Vec::new(),
3009                width: block.width,
3010                height: block.height,
3011            })
3012            .collect(),
3013        preencoded_ht_code_blocks: Some(
3014            subband
3015                .code_blocks
3016                .iter()
3017                .map(|block| block.encoded.clone())
3018                .collect(),
3019        ),
3020        num_cbs_x: subband.num_cbs_x,
3021        num_cbs_y: subband.num_cbs_y,
3022        code_block_width: subband
3023            .code_blocks
3024            .iter()
3025            .map(|block| block.width)
3026            .max()
3027            .unwrap_or(0),
3028        code_block_height: subband
3029            .code_blocks
3030            .iter()
3031            .map(|block| block.height)
3032            .max()
3033            .unwrap_or(0),
3034        width: precomputed_subband_width(
3035            subband.num_cbs_x,
3036            subband.code_blocks.iter().map(|block| block.width),
3037        ),
3038        height: precomputed_subband_height(
3039            subband.num_cbs_x,
3040            subband.num_cbs_y,
3041            subband.code_blocks.iter().map(|block| block.height),
3042        ),
3043        sub_band_type: internal_sub_band_type(subband.sub_band_type),
3044        total_bitplanes: subband.total_bitplanes,
3045        block_coding_mode: BlockCodingMode::HighThroughput,
3046        ht_target_coding_passes: 1,
3047    })
3048}
3049
3050fn prepared_subband_from_preencoded_owned(
3051    subband: PreencodedHtj2k97Subband,
3052) -> Result<PreparedEncodeSubband, &'static str> {
3053    let code_block_width = subband
3054        .code_blocks
3055        .iter()
3056        .map(|block| block.width)
3057        .max()
3058        .unwrap_or(0);
3059    let code_block_height = subband
3060        .code_blocks
3061        .iter()
3062        .map(|block| block.height)
3063        .max()
3064        .unwrap_or(0);
3065    let width = precomputed_subband_width(
3066        subband.num_cbs_x,
3067        subband.code_blocks.iter().map(|block| block.width),
3068    );
3069    let height = precomputed_subband_height(
3070        subband.num_cbs_x,
3071        subband.num_cbs_y,
3072        subband.code_blocks.iter().map(|block| block.height),
3073    );
3074    let code_blocks = subband
3075        .code_blocks
3076        .into_iter()
3077        .map(|block| {
3078            let PreencodedHtj2k97CodeBlock {
3079                width,
3080                height,
3081                encoded,
3082            } = block;
3083            (
3084                PreparedEncodeCodeBlock {
3085                    coefficients: Vec::new(),
3086                    width,
3087                    height,
3088                },
3089                encoded,
3090            )
3091        })
3092        .collect::<Vec<_>>();
3093    let (code_blocks, preencoded_ht_code_blocks): (Vec<_>, Vec<_>) =
3094        code_blocks.into_iter().unzip();
3095
3096    Ok(PreparedEncodeSubband {
3097        code_blocks,
3098        preencoded_ht_code_blocks: Some(preencoded_ht_code_blocks),
3099        num_cbs_x: subband.num_cbs_x,
3100        num_cbs_y: subband.num_cbs_y,
3101        code_block_width,
3102        code_block_height,
3103        width,
3104        height,
3105        sub_band_type: internal_sub_band_type(subband.sub_band_type),
3106        total_bitplanes: subband.total_bitplanes,
3107        block_coding_mode: BlockCodingMode::HighThroughput,
3108        ht_target_coding_passes: 1,
3109    })
3110}
3111
3112fn prepared_subband_from_preencoded_compact<'a>(
3113    subband: &'a PreencodedHtj2k97CompactSubband,
3114    payload: &'a [u8],
3115) -> Result<PreparedCompactSubband<'a>, &'static str> {
3116    let code_blocks = subband
3117        .code_blocks
3118        .iter()
3119        .map(|block| {
3120            Ok(PreparedCompactCodeBlock {
3121                data: compact_payload_slice(payload, &block.payload_range)?,
3122                cleanup_length: block.cleanup_length,
3123                refinement_length: block.refinement_length,
3124                num_coding_passes: block.num_coding_passes,
3125                num_zero_bitplanes: block.num_zero_bitplanes,
3126            })
3127        })
3128        .collect::<Result<Vec<_>, &'static str>>()?;
3129
3130    Ok(PreparedCompactSubband {
3131        code_blocks,
3132        num_cbs_x: subband.num_cbs_x,
3133        num_cbs_y: subband.num_cbs_y,
3134    })
3135}
3136
3137fn compact_payload_slice<'a>(
3138    payload: &'a [u8],
3139    range: &Range<usize>,
3140) -> Result<&'a [u8], &'static str> {
3141    if range.start > range.end || range.end > payload.len() {
3142        return Err("HTJ2K payload range out of bounds");
3143    }
3144    Ok(&payload[range.clone()])
3145}
3146
3147fn zero_pixel_buffer(
3148    width: u32,
3149    height: u32,
3150    num_components: u16,
3151    bit_depth: u8,
3152) -> Result<Vec<u8>, &'static str> {
3153    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
3154    let len = width as usize;
3155    let len = len
3156        .checked_mul(height as usize)
3157        .and_then(|value| value.checked_mul(usize::from(num_components)))
3158        .and_then(|value| value.checked_mul(bytes_per_sample))
3159        .ok_or("pixel buffer dimensions overflow")?;
3160    Ok(vec![0; len])
3161}
3162
3163struct PrecomputedDwtAccelerator<'a, A: J2kEncodeStageAccelerator> {
3164    outputs: Vec<J2kForwardDwt53Output>,
3165    encode_accelerator: &'a mut A,
3166}
3167
3168struct PrecomputedDwt97Accelerator<'a, A: J2kEncodeStageAccelerator> {
3169    outputs: Vec<J2kForwardDwt97Output>,
3170    encode_accelerator: &'a mut A,
3171}
3172
3173impl<A: J2kEncodeStageAccelerator> J2kEncodeStageAccelerator for PrecomputedDwtAccelerator<'_, A> {
3174    fn dispatch_report(&self) -> crate::J2kEncodeDispatchReport {
3175        self.encode_accelerator.dispatch_report()
3176    }
3177
3178    fn encode_forward_dwt53(
3179        &mut self,
3180        _job: J2kForwardDwt53Job<'_>,
3181    ) -> Result<Option<J2kForwardDwt53Output>, &'static str> {
3182        if self.outputs.is_empty() {
3183            return Err("precomputed DWT output exhausted");
3184        }
3185
3186        Ok(Some(self.outputs.remove(0)))
3187    }
3188
3189    fn encode_quantize_subband(
3190        &mut self,
3191        job: J2kQuantizeSubbandJob<'_>,
3192    ) -> Result<Option<Vec<i32>>, &'static str> {
3193        self.encode_accelerator.encode_quantize_subband(job)
3194    }
3195
3196    fn encode_tier1_code_block(
3197        &mut self,
3198        job: J2kTier1CodeBlockEncodeJob<'_>,
3199    ) -> Result<Option<EncodedJ2kCodeBlock>, &'static str> {
3200        self.encode_accelerator.encode_tier1_code_block(job)
3201    }
3202
3203    fn encode_tier1_code_blocks(
3204        &mut self,
3205        jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
3206    ) -> Result<Option<Vec<EncodedJ2kCodeBlock>>, &'static str> {
3207        self.encode_accelerator.encode_tier1_code_blocks(jobs)
3208    }
3209
3210    fn encode_ht_code_block(
3211        &mut self,
3212        job: crate::J2kHtCodeBlockEncodeJob<'_>,
3213    ) -> Result<Option<EncodedHtJ2kCodeBlock>, &'static str> {
3214        self.encode_accelerator.encode_ht_code_block(job)
3215    }
3216
3217    fn encode_ht_code_blocks(
3218        &mut self,
3219        jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
3220    ) -> Result<Option<Vec<EncodedHtJ2kCodeBlock>>, &'static str> {
3221        self.encode_accelerator.encode_ht_code_blocks(jobs)
3222    }
3223
3224    fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
3225        self.encode_accelerator
3226            .prefer_parallel_cpu_code_block_fallback()
3227    }
3228
3229    fn prefer_parallel_cpu_tile_encode(&self) -> bool {
3230        self.encode_accelerator.prefer_parallel_cpu_tile_encode()
3231    }
3232
3233    fn encode_packetization(
3234        &mut self,
3235        job: J2kPacketizationEncodeJob<'_>,
3236    ) -> Result<Option<Vec<u8>>, &'static str> {
3237        self.encode_accelerator.encode_packetization(job)
3238    }
3239}
3240
3241impl<A: J2kEncodeStageAccelerator> J2kEncodeStageAccelerator
3242    for PrecomputedDwt97Accelerator<'_, A>
3243{
3244    fn dispatch_report(&self) -> crate::J2kEncodeDispatchReport {
3245        self.encode_accelerator.dispatch_report()
3246    }
3247
3248    fn encode_forward_dwt97(
3249        &mut self,
3250        _job: J2kForwardDwt97Job<'_>,
3251    ) -> Result<Option<J2kForwardDwt97Output>, &'static str> {
3252        if self.outputs.is_empty() {
3253            return Err("precomputed DWT output exhausted");
3254        }
3255
3256        Ok(Some(self.outputs.remove(0)))
3257    }
3258
3259    fn encode_quantize_subband(
3260        &mut self,
3261        job: J2kQuantizeSubbandJob<'_>,
3262    ) -> Result<Option<Vec<i32>>, &'static str> {
3263        self.encode_accelerator.encode_quantize_subband(job)
3264    }
3265
3266    fn encode_tier1_code_block(
3267        &mut self,
3268        job: J2kTier1CodeBlockEncodeJob<'_>,
3269    ) -> Result<Option<EncodedJ2kCodeBlock>, &'static str> {
3270        self.encode_accelerator.encode_tier1_code_block(job)
3271    }
3272
3273    fn encode_tier1_code_blocks(
3274        &mut self,
3275        jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
3276    ) -> Result<Option<Vec<EncodedJ2kCodeBlock>>, &'static str> {
3277        self.encode_accelerator.encode_tier1_code_blocks(jobs)
3278    }
3279
3280    fn encode_ht_code_block(
3281        &mut self,
3282        job: crate::J2kHtCodeBlockEncodeJob<'_>,
3283    ) -> Result<Option<EncodedHtJ2kCodeBlock>, &'static str> {
3284        self.encode_accelerator.encode_ht_code_block(job)
3285    }
3286
3287    fn encode_ht_code_blocks(
3288        &mut self,
3289        jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
3290    ) -> Result<Option<Vec<EncodedHtJ2kCodeBlock>>, &'static str> {
3291        self.encode_accelerator.encode_ht_code_blocks(jobs)
3292    }
3293
3294    fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
3295        self.encode_accelerator
3296            .prefer_parallel_cpu_code_block_fallback()
3297    }
3298
3299    fn prefer_parallel_cpu_tile_encode(&self) -> bool {
3300        self.encode_accelerator.prefer_parallel_cpu_tile_encode()
3301    }
3302
3303    fn encode_packetization(
3304        &mut self,
3305        job: J2kPacketizationEncodeJob<'_>,
3306    ) -> Result<Option<Vec<u8>>, &'static str> {
3307        self.encode_accelerator.encode_packetization(job)
3308    }
3309}
3310
3311fn block_coding_mode(options: &EncodeOptions) -> BlockCodingMode {
3312    if options.use_ht_block_coding {
3313        BlockCodingMode::HighThroughput
3314    } else {
3315        BlockCodingMode::Classic
3316    }
3317}
3318
3319fn ht_target_coding_passes_for_options(options: &EncodeOptions) -> u8 {
3320    if options.use_ht_block_coding && options.num_layers > 1 {
3321        options.num_layers.min(3)
3322    } else {
3323        1
3324    }
3325}
3326
3327fn validate_irreversible_quantization_scale(scale: f32) -> Result<(), &'static str> {
3328    if scale.is_finite() && scale > 0.0 {
3329        Ok(())
3330    } else {
3331        Err("irreversible quantization scale must be finite and greater than zero")
3332    }
3333}
3334
3335fn validate_irreversible_quantization_profile(options: &EncodeOptions) -> Result<(), &'static str> {
3336    validate_irreversible_quantization_scale(options.irreversible_quantization_scale)?;
3337    if quantize::subband_scales_all_valid(options.irreversible_quantization_subband_scales) {
3338        Ok(())
3339    } else {
3340        Err("irreversible quantization subband scales must be finite and greater than zero")
3341    }
3342}
3343
3344fn validate_htj2k_codestream(
3345    codestream: &[u8],
3346    pixels: &[u8],
3347    width: u32,
3348    height: u32,
3349    num_components: u16,
3350    bit_depth: u8,
3351    signed: bool,
3352    reversible: bool,
3353) -> Result<(), &'static str> {
3354    let image = Image::new(codestream, &DecodeSettings::default())
3355        .map_err(|_| "generated HTJ2K codestream failed self-validation")?;
3356    let decoded = image
3357        .decode_native()
3358        .map_err(|_| "generated HTJ2K codestream failed self-validation")?;
3359
3360    if decoded.width != width
3361        || decoded.height != height
3362        || decoded.bit_depth != bit_depth
3363        || decoded.num_components != num_components
3364    {
3365        return Err("generated HTJ2K codestream failed self-validation");
3366    }
3367
3368    if reversible && !native_samples_equal(pixels, &decoded.data, bit_depth, signed) {
3369        return Err("generated HTJ2K codestream did not roundtrip");
3370    }
3371
3372    Ok(())
3373}
3374
3375fn native_samples_equal(expected: &[u8], actual: &[u8], bit_depth: u8, signed: bool) -> bool {
3376    if expected.len() != actual.len() {
3377        return false;
3378    }
3379
3380    let Ok(bytes_per_sample) = raw_pixel_bytes_per_sample(bit_depth) else {
3381        return false;
3382    };
3383    let sample_count = expected.len() / bytes_per_sample;
3384    (0..sample_count).all(|sample_index| {
3385        decode_native_sample(expected, sample_index, bit_depth, signed)
3386            == decode_native_sample(actual, sample_index, bit_depth, signed)
3387    })
3388}
3389
3390fn decode_native_sample(bytes: &[u8], sample_index: usize, bit_depth: u8, signed: bool) -> i64 {
3391    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth).unwrap_or(1);
3392    let byte_offset = sample_index * bytes_per_sample;
3393    let raw = read_le_sample_value(
3394        &bytes[byte_offset..byte_offset + bytes_per_sample],
3395        bit_depth,
3396    );
3397
3398    if signed {
3399        sign_extend_sample(raw, bit_depth)
3400    } else {
3401        raw as i64
3402    }
3403}
3404
3405fn raw_pixel_bytes_per_sample(bit_depth: u8) -> Result<usize, &'static str> {
3406    if bit_depth == 0 || bit_depth > MAX_PART1_SAMPLE_BIT_DEPTH {
3407        return Err("unsupported bit depth");
3408    }
3409    Ok(usize::from(bit_depth).div_ceil(8).max(1))
3410}
3411
3412fn read_le_sample_value(bytes: &[u8], bit_depth: u8) -> u64 {
3413    let mut raw = 0_u64;
3414    for (shift, byte) in bytes.iter().enumerate() {
3415        raw |= u64::from(*byte) << (shift * 8);
3416    }
3417    let mask = (1_u64 << bit_depth) - 1;
3418    raw & mask
3419}
3420
3421fn sign_extend_sample(raw: u64, bit_depth: u8) -> i64 {
3422    let shift = 64 - u32::from(bit_depth);
3423    ((raw << shift) as i64) >> shift
3424}
3425
3426fn encode_impl(
3427    pixels: &[u8],
3428    width: u32,
3429    height: u32,
3430    num_components: u16,
3431    bit_depth: u8,
3432    signed: bool,
3433    options: &EncodeOptions,
3434    block_coding_mode: BlockCodingMode,
3435    roi_regions: &[EncodeRoiRegion],
3436    component_sample_info: &[EncodeComponentSampleInfo],
3437    accelerator: &mut impl J2kEncodeStageAccelerator,
3438) -> Result<Vec<u8>, &'static str> {
3439    if width == 0 || height == 0 {
3440        return Err("invalid dimensions");
3441    }
3442    if num_components == 0 || num_components > MAX_J2K_SPEC_COMPONENTS {
3443        return Err("unsupported component count");
3444    }
3445    if bit_depth == 0 || bit_depth > MAX_PART1_SAMPLE_BIT_DEPTH {
3446        return Err("unsupported bit depth");
3447    }
3448    if options.num_layers == 0 || options.num_layers > 32 {
3449        return Err("unsupported quality layer count");
3450    }
3451    if options.write_ppm && options.write_ppt {
3452        return Err("PPM and PPT packet header markers are mutually exclusive");
3453    }
3454    if matches!(options.tile_part_packet_limit, Some(0)) {
3455        return Err("tile-part packet limit must be non-zero");
3456    }
3457    if !options.quality_layer_byte_targets.is_empty()
3458        && options.quality_layer_byte_targets.len() != usize::from(options.num_layers)
3459    {
3460        return Err("quality layer byte target count must match quality layer count");
3461    }
3462    if !options.reversible {
3463        validate_irreversible_quantization_profile(options)?;
3464    }
3465    validate_component_sample_info(component_sample_info, usize::from(num_components))?;
3466
3467    let num_pixels = (width as usize)
3468        .checked_mul(height as usize)
3469        .ok_or("image dimensions overflow")?;
3470    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
3471    let expected_len = num_pixels
3472        .checked_mul(num_components as usize)
3473        .and_then(|len| len.checked_mul(bytes_per_sample))
3474        .ok_or("image dimensions overflow")?;
3475    if pixels.len() < expected_len {
3476        return Err("pixel data too short");
3477    }
3478    let component_sampling = component_sampling_for_options(options, num_components)?;
3479    let high_bit_exact = bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH;
3480    if high_bit_exact && options.reversible {
3481        validate_reversible_i64_encode_options(
3482            options,
3483            block_coding_mode,
3484            component_sample_info,
3485            &component_sampling,
3486        )?;
3487    }
3488    if let Some((tile_width, tile_height)) = options.tile_size {
3489        if tile_width == 0 || tile_height == 0 {
3490            return Err("invalid tile dimensions");
3491        }
3492        if component_sampling
3493            .iter()
3494            .any(|sampling| *sampling != (1, 1))
3495        {
3496            return Err("multi-tile encode with component sampling is not implemented");
3497        }
3498        if tile_width < width || tile_height < height {
3499            return encode_multitile_impl(
3500                pixels,
3501                width,
3502                height,
3503                num_components,
3504                bit_depth,
3505                signed,
3506                options,
3507                block_coding_mode,
3508                roi_regions,
3509                component_sample_info,
3510                accelerator,
3511                tile_width,
3512                tile_height,
3513            );
3514        }
3515    }
3516
3517    let profile_enabled = profile::profile_stages_enabled();
3518    let total_start = profile::profile_now(profile_enabled);
3519
3520    let use_mct = options.use_mct && matches!(num_components, 3 | 4);
3521    let num_levels = options.num_decomposition_levels.min(
3522        // Don't decompose more than the image supports
3523        max_decomposition_levels(width, height),
3524    );
3525    let requested_guard_bits = if options.reversible {
3526        if use_mct {
3527            options.guard_bits.max(2)
3528        } else {
3529            options.guard_bits
3530        }
3531    } else {
3532        options.guard_bits.max(2)
3533    };
3534    let guard_bits = if high_bit_exact && options.reversible {
3535        reversible_guard_bits_for_marker_limit(bit_depth, num_levels, requested_guard_bits)?
3536    } else {
3537        requested_guard_bits
3538    };
3539    let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
3540    let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
3541        bit_depth,
3542        num_levels,
3543        options.reversible,
3544        guard_bits,
3545        options.irreversible_quantization_scale,
3546        options.irreversible_quantization_subband_scales,
3547    );
3548    if options.reversible && reversible_guard_delta != 0 {
3549        adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
3550    }
3551    let quant_params: Vec<(u16, u16)> = step_sizes
3552        .iter()
3553        .map(|s| (s.exponent, s.mantissa))
3554        .collect();
3555    let mut component_step_sizes = component_step_sizes(
3556        component_sample_info,
3557        num_levels,
3558        options.reversible,
3559        guard_bits,
3560        options.irreversible_quantization_scale,
3561        options.irreversible_quantization_subband_scales,
3562    );
3563    if options.reversible && reversible_guard_delta != 0 {
3564        adjust_component_step_sizes_for_guard_delta(
3565            &mut component_step_sizes,
3566            reversible_guard_delta,
3567        )?;
3568    }
3569    let component_quantization_step_sizes = component_step_sizes
3570        .iter()
3571        .map(|steps| {
3572            steps
3573                .iter()
3574                .map(|step| (step.exponent, step.mantissa))
3575                .collect::<Vec<_>>()
3576        })
3577        .collect::<Vec<_>>();
3578    let cb_width = 1u32 << (options.code_block_width_exp + 2);
3579    let cb_height = 1u32 << (options.code_block_height_exp + 2);
3580    let ht_target_coding_passes = ht_target_coding_passes_for_options(options);
3581    let precinct_exponents = precinct_exponents_for_options(options, num_levels)?;
3582    let max_base_bitplanes =
3583        max_total_bitplanes_for_components(&step_sizes, &component_step_sizes, guard_bits)?;
3584    let roi_plans = roi_encode_plans_for_options(
3585        options,
3586        roi_regions,
3587        num_components,
3588        width,
3589        height,
3590        &component_sampling,
3591        max_base_bitplanes,
3592        block_coding_mode,
3593    )?;
3594    let roi_component_shifts: Vec<u8> = roi_plans.iter().map(|plan| plan.shift).collect();
3595    let params = EncodeParams {
3596        width,
3597        height,
3598        tile_width: options
3599            .tile_size
3600            .map_or(width, |(tile_width, _)| tile_width),
3601        tile_height: options
3602            .tile_size
3603            .map_or(height, |(_, tile_height)| tile_height),
3604        num_components,
3605        bit_depth,
3606        signed,
3607        component_sample_info: component_sample_info.to_vec(),
3608        component_quantization_step_sizes,
3609        num_decomposition_levels: num_levels,
3610        reversible: options.reversible,
3611        code_block_width_exp: options.code_block_width_exp,
3612        code_block_height_exp: options.code_block_height_exp,
3613        num_layers: options.num_layers,
3614        use_mct,
3615        guard_bits,
3616        block_coding_mode,
3617        progression_order: options.progression_order,
3618        write_tlm: options.write_tlm,
3619        write_plt: options.write_plt,
3620        write_plm: options.write_plm,
3621        write_ppm: options.write_ppm,
3622        write_ppt: options.write_ppt,
3623        write_sop: options.write_sop,
3624        write_eph: options.write_eph,
3625        terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
3626            && options.num_layers > 1,
3627        component_sampling,
3628        roi_component_shifts: roi_component_shifts.clone(),
3629        precinct_exponents,
3630    };
3631
3632    if high_bit_exact && options.reversible {
3633        return encode_reversible_i64_single_tile_codestream(
3634            pixels,
3635            width,
3636            height,
3637            num_pixels,
3638            num_components,
3639            bit_depth,
3640            signed,
3641            options,
3642            &params,
3643            &quant_params,
3644            &step_sizes,
3645            &roi_plans,
3646            use_mct,
3647            guard_bits,
3648            num_levels,
3649            cb_width,
3650            cb_height,
3651            ht_target_coding_passes,
3652            accelerator,
3653        );
3654    }
3655
3656    let stage_start = profile::profile_now(profile_enabled);
3657    if block_coding_mode == BlockCodingMode::HighThroughput
3658        && component_sample_info.is_empty()
3659        && roi_component_shifts.iter().all(|shift| *shift == 0)
3660        && roi_regions.is_empty()
3661        && !(params.write_plt
3662            || params.write_plm
3663            || params.write_sop
3664            || params.write_eph
3665            || options.tile_part_packet_limit.is_some())
3666    {
3667        if let Some(tile_data) = accelerator.encode_htj2k_tile(J2kHtj2kTileEncodeJob {
3668            pixels,
3669            width,
3670            height,
3671            num_components,
3672            bit_depth,
3673            signed,
3674            num_decomposition_levels: num_levels,
3675            reversible: options.reversible,
3676            use_mct,
3677            guard_bits,
3678            code_block_width: cb_width,
3679            code_block_height: cb_height,
3680            progression_order: public_packetization_progression_order(options.progression_order),
3681            component_sampling: &params.component_sampling,
3682            quantization_steps: &quant_params,
3683        })? {
3684            let tile_body_us = profile::elapsed_us(stage_start);
3685            let stage_start = profile::profile_now(profile_enabled);
3686            let codestream = codestream_write::write_codestream(&params, &tile_data, &quant_params);
3687            let codestream_us = profile::elapsed_us(stage_start);
3688            if profile_enabled {
3689                profile::emit_profile_row(
3690                    "encode",
3691                    "accelerated",
3692                    &[
3693                        ("tile_body_us", tile_body_us),
3694                        ("codestream_us", codestream_us),
3695                        ("total_us", profile::elapsed_us(total_start)),
3696                    ],
3697                );
3698            }
3699            return Ok(codestream);
3700        }
3701    }
3702
3703    // Step 1: Convert pixel bytes to f32 component arrays
3704    let stage_start = profile::profile_now(profile_enabled);
3705    let mut components = match accelerator.encode_deinterleave(J2kDeinterleaveToF32Job {
3706        pixels,
3707        num_pixels,
3708        num_components,
3709        bit_depth,
3710        signed,
3711    })? {
3712        Some(components) => {
3713            validate_deinterleaved_components(components, num_components, num_pixels)?
3714        }
3715        None => deinterleave_to_f32(pixels, num_pixels, num_components, bit_depth, signed),
3716    };
3717    let deinterleave_us = profile::elapsed_us(stage_start);
3718
3719    // Step 2: Apply forward MCT if RGB with 3+ components
3720    let stage_start = profile::profile_now(profile_enabled);
3721    if use_mct {
3722        if options.reversible {
3723            if !try_encode_forward_rct(&mut components, accelerator)? {
3724                forward_mct::forward_rct(&mut components);
3725            }
3726        } else if !try_encode_forward_ict(&mut components, accelerator)? {
3727            forward_mct::forward_ict(&mut components);
3728        }
3729    }
3730    let mct_us = profile::elapsed_us(stage_start);
3731
3732    // Step 3: Apply forward DWT to each component
3733    let stage_start = profile::profile_now(profile_enabled);
3734    let decompositions: Vec<DwtDecomposition> = components
3735        .iter()
3736        .map(|comp| {
3737            encode_forward_dwt(
3738                comp,
3739                width,
3740                height,
3741                num_levels,
3742                options.reversible,
3743                accelerator,
3744            )
3745        })
3746        .collect::<Result<Vec<_>, &'static str>>()?;
3747    validate_component_sampling_dwt_geometry(
3748        &decompositions,
3749        width,
3750        height,
3751        &params.component_sampling,
3752    )?;
3753    let dwt_us = profile::elapsed_us(stage_start);
3754
3755    // Step 5: Quantize and encode code-blocks for each component
3756    let mut component_resolution_packets: Vec<Vec<PreparedResolutionPacket>> =
3757        Vec::with_capacity(num_components as usize);
3758
3759    let stage_start = profile::profile_now(profile_enabled);
3760    for (component_idx, decomp) in decompositions
3761        .iter()
3762        .take(num_components as usize)
3763        .enumerate()
3764    {
3765        let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
3766        let component_bit_depth = component_sample_info
3767            .get(component_idx)
3768            .map_or(bit_depth, |info| info.bit_depth);
3769        let component_steps = component_step_sizes
3770            .get(component_idx)
3771            .map_or(step_sizes.as_slice(), Vec::as_slice);
3772        let roi_shift = roi_component_shifts
3773            .get(component_idx)
3774            .copied()
3775            .unwrap_or(0);
3776        let roi_plan = roi_plans
3777            .get(component_idx)
3778            .ok_or("ROI plan count does not match component count")?;
3779        let mut packets = Vec::with_capacity(num_levels as usize + 1);
3780
3781        // LL subband (resolution 0)
3782        let ll_roi_scale = roi_subband_scale(num_levels, None)?;
3783        let ll_subband = prepare_subband(
3784            &decomp.ll,
3785            decomp.ll_width,
3786            decomp.ll_height,
3787            &component_steps[0],
3788            component_bit_depth,
3789            guard_bits,
3790            options.reversible,
3791            block_coding_mode,
3792            cb_width,
3793            cb_height,
3794            SubBandType::LowLow,
3795            roi_shift,
3796            &roi_plan.regions,
3797            ll_roi_scale,
3798            ht_target_coding_passes,
3799            accelerator,
3800        )?;
3801        packets.push(PreparedResolutionPacket {
3802            component,
3803            resolution: 0,
3804            precinct: 0,
3805            subbands: vec![ll_subband],
3806        });
3807
3808        // Higher resolution levels
3809        for (level_idx, level) in decomp.levels.iter().enumerate() {
3810            let step_base = 1 + level_idx * 3;
3811            let level_roi_scale = roi_subband_scale(num_levels, Some(level_idx))?;
3812
3813            // HL subband
3814            let hl_subband = prepare_subband(
3815                &level.hl,
3816                level.high_width,
3817                level.low_height,
3818                &component_steps[step_base],
3819                component_bit_depth,
3820                guard_bits,
3821                options.reversible,
3822                block_coding_mode,
3823                cb_width,
3824                cb_height,
3825                SubBandType::HighLow,
3826                roi_shift,
3827                &roi_plan.regions,
3828                level_roi_scale,
3829                ht_target_coding_passes,
3830                accelerator,
3831            )?;
3832
3833            // LH subband
3834            let lh_subband = prepare_subband(
3835                &level.lh,
3836                level.low_width,
3837                level.high_height,
3838                &component_steps[step_base + 1],
3839                component_bit_depth,
3840                guard_bits,
3841                options.reversible,
3842                block_coding_mode,
3843                cb_width,
3844                cb_height,
3845                SubBandType::LowHigh,
3846                roi_shift,
3847                &roi_plan.regions,
3848                level_roi_scale,
3849                ht_target_coding_passes,
3850                accelerator,
3851            )?;
3852
3853            // HH subband
3854            let hh_subband = prepare_subband(
3855                &level.hh,
3856                level.high_width,
3857                level.high_height,
3858                &component_steps[step_base + 2],
3859                component_bit_depth,
3860                guard_bits,
3861                options.reversible,
3862                block_coding_mode,
3863                cb_width,
3864                cb_height,
3865                SubBandType::HighHigh,
3866                roi_shift,
3867                &roi_plan.regions,
3868                level_roi_scale,
3869                ht_target_coding_passes,
3870                accelerator,
3871            )?;
3872
3873            packets.push(PreparedResolutionPacket {
3874                component,
3875                resolution: u32::try_from(level_idx + 1)
3876                    .map_err(|_| "resolution index exceeds u32")?,
3877                precinct: 0,
3878                subbands: vec![hl_subband, lh_subband, hh_subband],
3879            });
3880        }
3881
3882        component_resolution_packets.push(packets);
3883    }
3884    let subband_prepare_us = profile::elapsed_us(stage_start);
3885
3886    let component_resolution_packets = split_component_resolution_packets_by_precinct(
3887        component_resolution_packets,
3888        width,
3889        height,
3890        num_levels,
3891        &params.precinct_exponents,
3892    )?;
3893    let prepared_resolution_packets =
3894        ordered_prepared_resolution_packets(component_resolution_packets, options)?;
3895    let stage_start = profile::profile_now(profile_enabled);
3896    let (resolution_packets, packet_descriptors, allow_packetization_accelerator) =
3897        if options.num_layers > 1 {
3898            let (resolution_packets, packet_descriptors) =
3899                encode_prepared_resolution_packets_layered(
3900                    prepared_resolution_packets,
3901                    options.num_layers,
3902                    options.progression_order,
3903                    &options.quality_layer_byte_targets,
3904                    accelerator,
3905                )?;
3906            (resolution_packets, packet_descriptors, false)
3907        } else {
3908            let packet_descriptors = packet_descriptors_for_order(
3909                &prepared_resolution_packets,
3910                1,
3911                options.progression_order,
3912            )?;
3913            let resolution_packets =
3914                encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
3915            (resolution_packets, packet_descriptors, true)
3916        };
3917    let block_encode_us = profile::elapsed_us(stage_start);
3918
3919    // Step 6: Form tile bitstream (T2)
3920    let stage_start = profile::profile_now(profile_enabled);
3921    let mut resolution_packets = resolution_packets;
3922    let packetized_tile = packetize_resolution_packets_with_options(
3923        &mut resolution_packets,
3924        &packet_descriptors,
3925        options.num_layers,
3926        num_components,
3927        options.progression_order,
3928        packet_encode::PacketMarkerOptions {
3929            write_sop: params.write_sop,
3930            write_eph: params.write_eph,
3931            separate_packet_headers: params.write_ppm || params.write_ppt,
3932        },
3933        allow_packetization_accelerator,
3934        packetization_requires_scalar(&params, options.tile_part_packet_limit),
3935        accelerator,
3936    )?;
3937    let packetize_us = profile::elapsed_us(stage_start);
3938
3939    // Step 7: Write codestream
3940    let stage_start = profile::profile_now(profile_enabled);
3941    let codestream = write_single_tile_packetized_codestream(
3942        &params,
3943        &packetized_tile,
3944        &quant_params,
3945        options.tile_part_packet_limit,
3946    )?;
3947    let codestream_us = profile::elapsed_us(stage_start);
3948
3949    if profile_enabled {
3950        profile::emit_profile_row(
3951            "encode",
3952            "cpu",
3953            &[
3954                ("deinterleave_us", deinterleave_us),
3955                ("mct_us", mct_us),
3956                ("dwt_us", dwt_us),
3957                ("subband_prepare_us", subband_prepare_us),
3958                ("block_encode_us", block_encode_us),
3959                ("packetize_us", packetize_us),
3960                ("codestream_us", codestream_us),
3961                ("total_us", profile::elapsed_us(total_start)),
3962            ],
3963        );
3964    }
3965
3966    Ok(codestream)
3967}
3968
3969fn validate_reversible_i64_encode_options(
3970    options: &EncodeOptions,
3971    block_coding_mode: BlockCodingMode,
3972    component_sample_info: &[EncodeComponentSampleInfo],
3973    component_sampling: &[(u8, u8)],
3974) -> Result<(), &'static str> {
3975    if !options.reversible {
3976        return Err("25-38 bit encode currently requires reversible 5/3 coding");
3977    }
3978    if !matches!(
3979        block_coding_mode,
3980        BlockCodingMode::Classic | BlockCodingMode::HighThroughput
3981    ) {
3982        return Err("25-38 bit encode requires classic J2K or HTJ2K block coding");
3983    }
3984    if !component_sample_info.is_empty() {
3985        return Err("25-38 bit encode currently requires uniform raw-pixel component metadata");
3986    }
3987    if component_sampling
3988        .iter()
3989        .any(|sampling| *sampling != (1, 1))
3990    {
3991        return Err("25-38 bit encode currently requires full-resolution components");
3992    }
3993    Ok(())
3994}
3995
3996#[allow(clippy::too_many_arguments)]
3997fn encode_reversible_i64_single_tile_codestream(
3998    pixels: &[u8],
3999    width: u32,
4000    height: u32,
4001    num_pixels: usize,
4002    num_components: u16,
4003    bit_depth: u8,
4004    signed: bool,
4005    options: &EncodeOptions,
4006    params: &EncodeParams,
4007    quant_params: &[(u16, u16)],
4008    step_sizes: &[QuantStepSize],
4009    roi_plans: &[ComponentRoiEncodePlan],
4010    use_mct: bool,
4011    guard_bits: u8,
4012    num_levels: u8,
4013    cb_width: u32,
4014    cb_height: u32,
4015    ht_target_coding_passes: u8,
4016    accelerator: &mut impl J2kEncodeStageAccelerator,
4017) -> Result<Vec<u8>, &'static str> {
4018    let max_reversible_gain = if num_levels == 0 { 0 } else { 2 };
4019    if u16::from(bit_depth) + max_reversible_gain > MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES {
4020        return Err("25-38 bit reversible encode exceeds the current no-quantization guard/exponent signaling limit");
4021    }
4022
4023    let mut components = deinterleave_to_i64(pixels, num_pixels, num_components, bit_depth, signed);
4024    if use_mct {
4025        forward_rct_i64(&mut components);
4026    }
4027
4028    let decompositions = components
4029        .iter()
4030        .map(|component| fdwt::forward_dwt_i64(component, width, height, num_levels))
4031        .collect::<Vec<_>>();
4032
4033    let mut component_resolution_packets = Vec::with_capacity(num_components as usize);
4034    for (component_idx, decomp) in decompositions
4035        .iter()
4036        .take(num_components as usize)
4037        .enumerate()
4038    {
4039        let component = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
4040        let roi_shift = params
4041            .roi_component_shifts
4042            .get(component_idx)
4043            .copied()
4044            .unwrap_or(0);
4045        let roi_plan = roi_plans
4046            .get(component_idx)
4047            .ok_or("ROI plan count does not match component count")?;
4048        let mut packets = Vec::with_capacity(num_levels as usize + 1);
4049
4050        let ll_roi_scale = roi_subband_scale(num_levels, None)?;
4051        let ll_subband = prepare_subband_i64(
4052            &decomp.ll,
4053            decomp.ll_width,
4054            decomp.ll_height,
4055            step_sizes
4056                .first()
4057                .ok_or("reversible quantization step missing")?,
4058            guard_bits,
4059            cb_width,
4060            cb_height,
4061            SubBandType::LowLow,
4062            roi_shift,
4063            &roi_plan.regions,
4064            ll_roi_scale,
4065            params.block_coding_mode,
4066            ht_target_coding_passes,
4067        )?;
4068        packets.push(PreparedResolutionPacket {
4069            component,
4070            resolution: 0,
4071            precinct: 0,
4072            subbands: vec![ll_subband],
4073        });
4074
4075        for (level_idx, level) in decomp.levels.iter().enumerate() {
4076            let step_base = 1 + level_idx * 3;
4077            let level_roi_scale = roi_subband_scale(num_levels, Some(level_idx))?;
4078            let hl_subband = prepare_subband_i64(
4079                &level.hl,
4080                level.high_width,
4081                level.low_height,
4082                step_sizes
4083                    .get(step_base)
4084                    .ok_or("reversible quantization step missing")?,
4085                guard_bits,
4086                cb_width,
4087                cb_height,
4088                SubBandType::HighLow,
4089                roi_shift,
4090                &roi_plan.regions,
4091                level_roi_scale,
4092                params.block_coding_mode,
4093                ht_target_coding_passes,
4094            )?;
4095            let lh_subband = prepare_subband_i64(
4096                &level.lh,
4097                level.low_width,
4098                level.high_height,
4099                step_sizes
4100                    .get(step_base + 1)
4101                    .ok_or("reversible quantization step missing")?,
4102                guard_bits,
4103                cb_width,
4104                cb_height,
4105                SubBandType::LowHigh,
4106                roi_shift,
4107                &roi_plan.regions,
4108                level_roi_scale,
4109                params.block_coding_mode,
4110                ht_target_coding_passes,
4111            )?;
4112            let hh_subband = prepare_subband_i64(
4113                &level.hh,
4114                level.high_width,
4115                level.high_height,
4116                step_sizes
4117                    .get(step_base + 2)
4118                    .ok_or("reversible quantization step missing")?,
4119                guard_bits,
4120                cb_width,
4121                cb_height,
4122                SubBandType::HighHigh,
4123                roi_shift,
4124                &roi_plan.regions,
4125                level_roi_scale,
4126                params.block_coding_mode,
4127                ht_target_coding_passes,
4128            )?;
4129
4130            packets.push(PreparedResolutionPacket {
4131                component,
4132                resolution: u32::try_from(level_idx + 1)
4133                    .map_err(|_| "resolution index exceeds u32")?,
4134                precinct: 0,
4135                subbands: vec![hl_subband, lh_subband, hh_subband],
4136            });
4137        }
4138        component_resolution_packets.push(packets);
4139    }
4140
4141    encode_i64_component_resolution_packets(
4142        component_resolution_packets,
4143        width,
4144        height,
4145        num_components,
4146        num_levels,
4147        params,
4148        quant_params,
4149        options,
4150        accelerator,
4151    )
4152}
4153
4154#[allow(clippy::too_many_arguments)]
4155fn encode_i64_component_resolution_packets(
4156    component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
4157    width: u32,
4158    height: u32,
4159    num_components: u16,
4160    num_levels: u8,
4161    params: &EncodeParams,
4162    quant_params: &[(u16, u16)],
4163    options: &EncodeOptions,
4164    accelerator: &mut impl J2kEncodeStageAccelerator,
4165) -> Result<Vec<u8>, &'static str> {
4166    let packetized_tile = packetize_i64_component_resolution_packets(
4167        component_resolution_packets,
4168        width,
4169        height,
4170        num_components,
4171        num_levels,
4172        params,
4173        options,
4174        accelerator,
4175    )?;
4176
4177    write_single_tile_packetized_codestream(
4178        params,
4179        &packetized_tile,
4180        quant_params,
4181        options.tile_part_packet_limit,
4182    )
4183}
4184
4185#[allow(clippy::too_many_arguments)]
4186fn packetize_i64_component_resolution_packets(
4187    component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
4188    width: u32,
4189    height: u32,
4190    num_components: u16,
4191    num_levels: u8,
4192    params: &EncodeParams,
4193    options: &EncodeOptions,
4194    accelerator: &mut impl J2kEncodeStageAccelerator,
4195) -> Result<packet_encode::PacketizedTileData, &'static str> {
4196    let component_resolution_packets = split_component_resolution_packets_by_precinct(
4197        component_resolution_packets,
4198        width,
4199        height,
4200        num_levels,
4201        &params.precinct_exponents,
4202    )?;
4203    let prepared_resolution_packets =
4204        ordered_prepared_resolution_packets(component_resolution_packets, options)?;
4205    let (resolution_packets, packet_descriptors, allow_packetization_accelerator) =
4206        if options.num_layers > 1 {
4207            let (resolution_packets, packet_descriptors) =
4208                encode_prepared_resolution_packets_layered(
4209                    prepared_resolution_packets,
4210                    options.num_layers,
4211                    options.progression_order,
4212                    &options.quality_layer_byte_targets,
4213                    accelerator,
4214                )?;
4215            (resolution_packets, packet_descriptors, false)
4216        } else {
4217            let packet_descriptors = packet_descriptors_for_order(
4218                &prepared_resolution_packets,
4219                1,
4220                options.progression_order,
4221            )?;
4222            let resolution_packets =
4223                encode_prepared_resolution_packets(prepared_resolution_packets, accelerator)?;
4224            (resolution_packets, packet_descriptors, true)
4225        };
4226
4227    let mut resolution_packets = resolution_packets;
4228    let packetized_tile = packetize_resolution_packets_with_options(
4229        &mut resolution_packets,
4230        &packet_descriptors,
4231        options.num_layers,
4232        num_components,
4233        options.progression_order,
4234        packet_encode::PacketMarkerOptions {
4235            write_sop: params.write_sop,
4236            write_eph: params.write_eph,
4237            separate_packet_headers: params.write_ppm || params.write_ppt,
4238        },
4239        allow_packetization_accelerator,
4240        packetization_requires_scalar(params, options.tile_part_packet_limit),
4241        accelerator,
4242    )?;
4243    Ok(packetized_tile)
4244}
4245
4246fn deinterleave_to_i64(
4247    pixels: &[u8],
4248    num_pixels: usize,
4249    num_components: u16,
4250    bit_depth: u8,
4251    signed: bool,
4252) -> Vec<Vec<i64>> {
4253    let nc = num_components as usize;
4254    let mut components = vec![vec![0_i64; num_pixels]; nc];
4255    let unsigned_offset = if signed {
4256        0
4257    } else {
4258        1_i64 << (u32::from(bit_depth) - 1)
4259    };
4260    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth).unwrap_or(5);
4261    for (i, pixel) in pixels
4262        .chunks_exact(nc * bytes_per_sample)
4263        .take(num_pixels)
4264        .enumerate()
4265    {
4266        for (component_idx, component) in components.iter_mut().enumerate().take(nc) {
4267            let offset = component_idx * bytes_per_sample;
4268            let raw = read_le_sample_value(&pixel[offset..offset + bytes_per_sample], bit_depth);
4269            component[i] = if signed {
4270                sign_extend_sample(raw, bit_depth)
4271            } else {
4272                i64::try_from(raw).unwrap_or(i64::MAX) - unsigned_offset
4273            };
4274        }
4275    }
4276    components
4277}
4278
4279fn typed_component_plane_to_i64(
4280    plane: &EncodeTypedComponentPlane<'_>,
4281    width: u32,
4282    height: u32,
4283) -> Result<Vec<i64>, &'static str> {
4284    let bytes_per_sample = raw_pixel_bytes_per_sample(plane.bit_depth)?;
4285    let sample_count = (width as usize)
4286        .checked_mul(height as usize)
4287        .ok_or("image dimensions overflow")?;
4288    let expected_len = sample_count
4289        .checked_mul(bytes_per_sample)
4290        .ok_or("image dimensions overflow")?;
4291    if plane.data.len() != expected_len {
4292        return Err("component plane data length mismatch");
4293    }
4294    let unsigned_offset = if plane.signed {
4295        0
4296    } else {
4297        1_i64 << (u32::from(plane.bit_depth) - 1)
4298    };
4299    Ok(plane
4300        .data
4301        .chunks_exact(bytes_per_sample)
4302        .map(|sample| {
4303            let raw = read_le_sample_value(sample, plane.bit_depth);
4304            if plane.signed {
4305                sign_extend_sample(raw, plane.bit_depth)
4306            } else {
4307                i64::try_from(raw).unwrap_or(i64::MAX) - unsigned_offset
4308            }
4309        })
4310        .collect())
4311}
4312
4313fn forward_rct_i64(components: &mut [Vec<i64>]) {
4314    debug_assert!(components.len() >= 3);
4315    let (r_components, rest) = components.split_at_mut(1);
4316    let (g_components, b_components) = rest.split_at_mut(1);
4317    let r_components = &mut r_components[0];
4318    let g_components = &mut g_components[0];
4319    let b_components = &mut b_components[0];
4320
4321    for ((r, g), b) in r_components
4322        .iter_mut()
4323        .zip(g_components.iter_mut())
4324        .zip(b_components.iter_mut())
4325    {
4326        let r0 = *r;
4327        let g0 = *g;
4328        let b0 = *b;
4329        *r = (r0 + 2 * g0 + b0).div_euclid(4);
4330        *g = b0 - g0;
4331        *b = r0 - g0;
4332    }
4333}
4334
4335struct EncodedTilePart {
4336    tile_index: u16,
4337    tile_part_index: u8,
4338    num_tile_parts: u8,
4339    data: Vec<u8>,
4340    packet_lengths: Vec<u32>,
4341    packet_headers: Vec<Vec<u8>>,
4342}
4343
4344fn split_packetized_tile_into_tile_parts(
4345    tile_index: u16,
4346    data: &[u8],
4347    packet_lengths: &[u32],
4348    packet_headers: &[Vec<u8>],
4349    packet_limit: Option<u16>,
4350) -> Result<Vec<EncodedTilePart>, &'static str> {
4351    if !packet_headers.is_empty() && packet_headers.len() != packet_lengths.len() {
4352        return Err("packet header count does not match packet length count");
4353    }
4354    let Some(packet_limit) = packet_limit else {
4355        return Ok(vec![EncodedTilePart {
4356            tile_index,
4357            tile_part_index: 0,
4358            num_tile_parts: 1,
4359            data: data.to_vec(),
4360            packet_lengths: packet_lengths.to_vec(),
4361            packet_headers: packet_headers.to_vec(),
4362        }]);
4363    };
4364    if packet_limit == 0 {
4365        return Err("tile-part packet limit must be non-zero");
4366    }
4367    if packet_lengths.is_empty() {
4368        return Ok(vec![EncodedTilePart {
4369            tile_index,
4370            tile_part_index: 0,
4371            num_tile_parts: 1,
4372            data: data.to_vec(),
4373            packet_lengths: Vec::new(),
4374            packet_headers: Vec::new(),
4375        }]);
4376    }
4377
4378    let expected_len = packet_lengths.iter().try_fold(0usize, |acc, &len| {
4379        acc.checked_add(usize::try_from(len).map_err(|_| "packet length exceeds usize")?)
4380            .ok_or("packet length sum overflow")
4381    })?;
4382    if expected_len != data.len() {
4383        return Err("packet lengths do not match tile data length");
4384    }
4385
4386    let packet_limit = usize::from(packet_limit);
4387    let num_tile_parts = packet_lengths.len().div_ceil(packet_limit);
4388    if num_tile_parts > usize::from(u8::MAX) {
4389        return Err("tile-part packet limit would emit more than 255 tile-parts");
4390    }
4391    let num_tile_parts = u8::try_from(num_tile_parts).map_err(|_| "tile-part count exceeds u8")?;
4392
4393    let mut parts = Vec::with_capacity(usize::from(num_tile_parts));
4394    let mut data_offset = 0usize;
4395    for (tile_part_index, packet_chunk) in packet_lengths.chunks(packet_limit).enumerate() {
4396        let chunk_len = packet_chunk.iter().try_fold(0usize, |acc, &len| {
4397            acc.checked_add(usize::try_from(len).map_err(|_| "packet length exceeds usize")?)
4398                .ok_or("packet length sum overflow")
4399        })?;
4400        let end = data_offset
4401            .checked_add(chunk_len)
4402            .ok_or("packet length sum overflow")?;
4403        let tile_part_index =
4404            u8::try_from(tile_part_index).map_err(|_| "tile-part index exceeds u8")?;
4405        parts.push(EncodedTilePart {
4406            tile_index,
4407            tile_part_index,
4408            num_tile_parts,
4409            data: data[data_offset..end].to_vec(),
4410            packet_lengths: packet_chunk.to_vec(),
4411            packet_headers: if packet_headers.is_empty() {
4412                Vec::new()
4413            } else {
4414                let packet_start = tile_part_index as usize * packet_limit;
4415                let packet_end = packet_start + packet_chunk.len();
4416                packet_headers[packet_start..packet_end].to_vec()
4417            },
4418        });
4419        data_offset = end;
4420    }
4421    Ok(parts)
4422}
4423
4424fn write_single_tile_packetized_codestream(
4425    params: &EncodeParams,
4426    packetized_tile: &packet_encode::PacketizedTileData,
4427    quant_params: &[(u16, u16)],
4428    tile_part_packet_limit: Option<u16>,
4429) -> Result<Vec<u8>, &'static str> {
4430    validate_packet_header_marker_payload(params, packetized_tile)?;
4431    let tile_parts = split_packetized_tile_into_tile_parts(
4432        0,
4433        &packetized_tile.data,
4434        &packetized_tile.packet_lengths,
4435        &packetized_tile.packet_headers,
4436        tile_part_packet_limit,
4437    )?;
4438    let codestream_tile_parts = tile_parts
4439        .iter()
4440        .map(|part| codestream_write::TilePartData {
4441            tile_index: part.tile_index,
4442            tile_part_index: part.tile_part_index,
4443            num_tile_parts: part.num_tile_parts,
4444            data: &part.data,
4445            packet_lengths: &part.packet_lengths,
4446            packet_headers: &part.packet_headers,
4447        })
4448        .collect::<Vec<_>>();
4449    Ok(codestream_write::write_codestream_tiles(
4450        params,
4451        &codestream_tile_parts,
4452        quant_params,
4453    ))
4454}
4455
4456fn validate_packet_header_marker_payload(
4457    params: &EncodeParams,
4458    packetized_tile: &packet_encode::PacketizedTileData,
4459) -> Result<(), &'static str> {
4460    if !params.write_ppm && !params.write_ppt {
4461        return Ok(());
4462    }
4463    if params.write_ppm && params.write_ppt {
4464        return Err("PPM and PPT packet header markers are mutually exclusive");
4465    }
4466    validate_packet_header_marker_payloads(
4467        params.write_ppm,
4468        params.write_ppt,
4469        &[&packetized_tile.packet_headers],
4470    )?;
4471    Ok(())
4472}
4473
4474fn validate_packet_header_marker_payloads(
4475    write_ppm: bool,
4476    write_ppt: bool,
4477    tile_packet_headers: &[&[Vec<u8>]],
4478) -> Result<(), &'static str> {
4479    const PACKET_HEADER_MARKER_PAYLOAD_LIMIT: usize = u16::MAX as usize - 3;
4480    const PPM_PACKET_HEADER_LIMIT: usize = PACKET_HEADER_MARKER_PAYLOAD_LIMIT - 2;
4481    const MAX_PACKET_HEADER_MARKERS: usize = u8::MAX as usize + 1;
4482
4483    if !write_ppm && !write_ppt {
4484        return Ok(());
4485    }
4486    if write_ppm && write_ppt {
4487        return Err("PPM and PPT packet header markers are mutually exclusive");
4488    }
4489    if tile_packet_headers.iter().any(|headers| headers.is_empty()) {
4490        return Err("PPM/PPT encode requires separated packet headers");
4491    }
4492    if write_ppm {
4493        let mut marker_count = 0usize;
4494        let mut payload_len = 0usize;
4495        for header in tile_packet_headers
4496            .iter()
4497            .flat_map(|headers| headers.iter())
4498        {
4499            if header.len() > PPM_PACKET_HEADER_LIMIT {
4500                return Err("PPM packet header exceeds marker payload limit");
4501            }
4502            let entry_len = 2usize
4503                .checked_add(header.len())
4504                .ok_or("PPM marker payload length overflow")?;
4505            if payload_len == 0 {
4506                marker_count = marker_count
4507                    .checked_add(1)
4508                    .ok_or("PPM marker count overflow")?;
4509            } else if payload_len
4510                .checked_add(entry_len)
4511                .is_none_or(|len| len > PACKET_HEADER_MARKER_PAYLOAD_LIMIT)
4512            {
4513                marker_count = marker_count
4514                    .checked_add(1)
4515                    .ok_or("PPM marker count overflow")?;
4516                payload_len = 0;
4517            }
4518            payload_len = payload_len
4519                .checked_add(entry_len)
4520                .ok_or("PPM marker payload length overflow")?;
4521            if marker_count > MAX_PACKET_HEADER_MARKERS {
4522                return Err("PPM packet headers require more than 256 marker segments");
4523            }
4524        }
4525    }
4526    if write_ppt {
4527        for headers in tile_packet_headers {
4528            let payload_len = headers.iter().try_fold(0usize, |acc, header| {
4529                acc.checked_add(header.len())
4530                    .ok_or("PPT marker payload length overflow")
4531            })?;
4532            let marker_count = payload_len.div_ceil(PACKET_HEADER_MARKER_PAYLOAD_LIMIT);
4533            if marker_count > MAX_PACKET_HEADER_MARKERS {
4534                return Err("PPT packet headers require more than 256 marker segments");
4535            }
4536        }
4537    }
4538    Ok(())
4539}
4540fn encode_multitile_impl(
4541    pixels: &[u8],
4542    width: u32,
4543    height: u32,
4544    num_components: u16,
4545    bit_depth: u8,
4546    signed: bool,
4547    options: &EncodeOptions,
4548    block_coding_mode: BlockCodingMode,
4549    roi_regions: &[EncodeRoiRegion],
4550    component_sample_info: &[EncodeComponentSampleInfo],
4551    accelerator: &mut impl J2kEncodeStageAccelerator,
4552    tile_width: u32,
4553    tile_height: u32,
4554) -> Result<Vec<u8>, &'static str> {
4555    let num_x_tiles = width.div_ceil(tile_width);
4556    let num_y_tiles = height.div_ceil(tile_height);
4557    let num_tiles = num_x_tiles
4558        .checked_mul(num_y_tiles)
4559        .ok_or("tile count overflow")?;
4560    if num_tiles > u32::from(u16::MAX) + 1 {
4561        return Err("multi-tile encode supports at most 65536 tiles");
4562    }
4563
4564    let min_tile_width = if width.is_multiple_of(tile_width) {
4565        tile_width
4566    } else {
4567        width % tile_width
4568    };
4569    let min_tile_height = if height.is_multiple_of(tile_height) {
4570        tile_height
4571    } else {
4572        height % tile_height
4573    };
4574    let num_levels = options
4575        .num_decomposition_levels
4576        .min(max_decomposition_levels(min_tile_width, min_tile_height));
4577    let use_mct = options.use_mct && matches!(num_components, 3 | 4);
4578    let requested_guard_bits = if options.reversible {
4579        if use_mct {
4580            options.guard_bits.max(2)
4581        } else {
4582            options.guard_bits
4583        }
4584    } else {
4585        options.guard_bits.max(2)
4586    };
4587    let high_bit_exact = bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH;
4588    let guard_bits = if high_bit_exact && options.reversible {
4589        reversible_guard_bits_for_marker_limit(bit_depth, num_levels, requested_guard_bits)?
4590    } else {
4591        requested_guard_bits
4592    };
4593    let reversible_guard_delta = guard_bits.saturating_sub(requested_guard_bits);
4594    let mut step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
4595        bit_depth,
4596        num_levels,
4597        options.reversible,
4598        guard_bits,
4599        options.irreversible_quantization_scale,
4600        options.irreversible_quantization_subband_scales,
4601    );
4602    if options.reversible && reversible_guard_delta != 0 {
4603        adjust_reversible_step_sizes_for_guard_delta(&mut step_sizes, reversible_guard_delta)?;
4604    }
4605    let quant_params: Vec<(u16, u16)> = step_sizes
4606        .iter()
4607        .map(|s| (s.exponent, s.mantissa))
4608        .collect();
4609    let mut component_step_sizes = component_step_sizes(
4610        component_sample_info,
4611        num_levels,
4612        options.reversible,
4613        guard_bits,
4614        options.irreversible_quantization_scale,
4615        options.irreversible_quantization_subband_scales,
4616    );
4617    if options.reversible && reversible_guard_delta != 0 {
4618        adjust_component_step_sizes_for_guard_delta(
4619            &mut component_step_sizes,
4620            reversible_guard_delta,
4621        )?;
4622    }
4623    let component_quantization_step_sizes = component_step_sizes
4624        .iter()
4625        .map(|steps| {
4626            steps
4627                .iter()
4628                .map(|step| (step.exponent, step.mantissa))
4629                .collect::<Vec<_>>()
4630        })
4631        .collect::<Vec<_>>();
4632
4633    let mut child_options = options.clone();
4634    child_options.num_decomposition_levels = num_levels;
4635    child_options.tile_size = None;
4636    child_options.write_tlm = false;
4637    child_options.write_plt = options.write_plt
4638        || options.write_plm
4639        || options.write_ppm
4640        || options.write_ppt
4641        || options.tile_part_packet_limit.is_some();
4642    child_options.write_plm = false;
4643    child_options.write_ppm = options.write_ppm || options.write_ppt;
4644    child_options.write_ppt = false;
4645
4646    let mut tile_bodies = Vec::with_capacity(num_tiles as usize);
4647    for tile_y in 0..num_y_tiles {
4648        for tile_x in 0..num_x_tiles {
4649            let tile_index = tile_y
4650                .checked_mul(num_x_tiles)
4651                .and_then(|base| base.checked_add(tile_x))
4652                .ok_or("tile index overflow")?;
4653            let tile_index = u16::try_from(tile_index).map_err(|_| "tile index exceeds u16")?;
4654            let x0 = tile_x * tile_width;
4655            let y0 = tile_y * tile_height;
4656            let actual_width = (width - x0).min(tile_width);
4657            let actual_height = (height - y0).min(tile_height);
4658            let tile_pixels = extract_interleaved_tile(
4659                pixels,
4660                width,
4661                x0,
4662                y0,
4663                actual_width,
4664                actual_height,
4665                num_components,
4666                bit_depth,
4667            )?;
4668            let tile_roi_regions =
4669                roi_regions_for_tile(roi_regions, x0, y0, actual_width, actual_height)?;
4670            let tile_codestream = encode_impl(
4671                &tile_pixels,
4672                actual_width,
4673                actual_height,
4674                num_components,
4675                bit_depth,
4676                signed,
4677                &child_options,
4678                block_coding_mode,
4679                &tile_roi_regions,
4680                component_sample_info,
4681                accelerator,
4682            )?;
4683            let packet_lengths = if options.write_plt
4684                || options.write_plm
4685                || options.write_ppm
4686                || options.write_ppt
4687                || options.tile_part_packet_limit.is_some()
4688            {
4689                extract_single_tile_plt_packet_lengths(&tile_codestream)?
4690            } else {
4691                Vec::new()
4692            };
4693            let packet_headers = if options.write_ppm || options.write_ppt {
4694                extract_single_tile_ppm_packet_headers(&tile_codestream)?
4695            } else {
4696                Vec::new()
4697            };
4698            tile_bodies.extend(split_packetized_tile_into_tile_parts(
4699                tile_index,
4700                extract_single_tile_body(&tile_codestream)?,
4701                &packet_lengths,
4702                &packet_headers,
4703                options.tile_part_packet_limit,
4704            )?);
4705        }
4706    }
4707
4708    let component_sampling = component_sampling_for_options(options, num_components)?;
4709    let roi_plans = roi_encode_plans_for_options(
4710        options,
4711        roi_regions,
4712        num_components,
4713        width,
4714        height,
4715        &component_sampling,
4716        max_total_bitplanes_for_components(&step_sizes, &component_step_sizes, guard_bits)?,
4717        block_coding_mode,
4718    )?;
4719    let precinct_exponents = precinct_exponents_for_options(options, num_levels)?;
4720    let params = EncodeParams {
4721        width,
4722        height,
4723        tile_width,
4724        tile_height,
4725        num_components,
4726        bit_depth,
4727        signed,
4728        component_sample_info: component_sample_info.to_vec(),
4729        component_quantization_step_sizes,
4730        num_decomposition_levels: num_levels,
4731        reversible: options.reversible,
4732        code_block_width_exp: options.code_block_width_exp,
4733        code_block_height_exp: options.code_block_height_exp,
4734        num_layers: options.num_layers,
4735        use_mct,
4736        guard_bits,
4737        block_coding_mode,
4738        progression_order: options.progression_order,
4739        write_tlm: options.write_tlm,
4740        write_plt: options.write_plt,
4741        write_plm: options.write_plm,
4742        write_ppm: options.write_ppm,
4743        write_ppt: options.write_ppt,
4744        write_sop: options.write_sop,
4745        write_eph: options.write_eph,
4746        terminate_coding_passes: block_coding_mode == BlockCodingMode::Classic
4747            && options.num_layers > 1,
4748        component_sampling,
4749        roi_component_shifts: roi_plans.iter().map(|plan| plan.shift).collect(),
4750        precinct_exponents,
4751    };
4752    let tile_packet_headers = tile_bodies
4753        .iter()
4754        .map(|tile| tile.packet_headers.as_slice())
4755        .collect::<Vec<_>>();
4756    validate_packet_header_marker_payloads(
4757        params.write_ppm,
4758        params.write_ppt,
4759        &tile_packet_headers,
4760    )?;
4761    let tile_parts = tile_bodies
4762        .iter()
4763        .map(|tile| codestream_write::TilePartData {
4764            tile_index: tile.tile_index,
4765            tile_part_index: tile.tile_part_index,
4766            num_tile_parts: tile.num_tile_parts,
4767            data: &tile.data,
4768            packet_lengths: &tile.packet_lengths,
4769            packet_headers: &tile.packet_headers,
4770        })
4771        .collect::<Vec<_>>();
4772
4773    Ok(codestream_write::write_codestream_tiles(
4774        &params,
4775        &tile_parts,
4776        &quant_params,
4777    ))
4778}
4779
4780fn extract_interleaved_tile(
4781    pixels: &[u8],
4782    image_width: u32,
4783    x0: u32,
4784    y0: u32,
4785    tile_width: u32,
4786    tile_height: u32,
4787    num_components: u16,
4788    bit_depth: u8,
4789) -> Result<Vec<u8>, &'static str> {
4790    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
4791    let bytes_per_pixel = usize::from(num_components)
4792        .checked_mul(bytes_per_sample)
4793        .ok_or("pixel stride overflow")?;
4794    let row_bytes = usize::try_from(tile_width)
4795        .map_err(|_| "tile width exceeds usize")?
4796        .checked_mul(bytes_per_pixel)
4797        .ok_or("tile row byte count overflow")?;
4798    let out_len = row_bytes
4799        .checked_mul(usize::try_from(tile_height).map_err(|_| "tile height exceeds usize")?)
4800        .ok_or("tile byte count overflow")?;
4801    let mut tile = Vec::with_capacity(out_len);
4802    let image_row_bytes = usize::try_from(image_width)
4803        .map_err(|_| "image width exceeds usize")?
4804        .checked_mul(bytes_per_pixel)
4805        .ok_or("image row byte count overflow")?;
4806    let x_byte_offset = usize::try_from(x0)
4807        .map_err(|_| "tile x offset exceeds usize")?
4808        .checked_mul(bytes_per_pixel)
4809        .ok_or("tile x byte offset overflow")?;
4810
4811    for y in y0..y0 + tile_height {
4812        let row_start = usize::try_from(y)
4813            .map_err(|_| "tile y offset exceeds usize")?
4814            .checked_mul(image_row_bytes)
4815            .and_then(|offset| offset.checked_add(x_byte_offset))
4816            .ok_or("tile row offset overflow")?;
4817        let row_end = row_start
4818            .checked_add(row_bytes)
4819            .ok_or("tile row range overflow")?;
4820        tile.extend_from_slice(
4821            pixels
4822                .get(row_start..row_end)
4823                .ok_or("tile row range outside source pixels")?,
4824        );
4825    }
4826
4827    Ok(tile)
4828}
4829
4830fn extract_component_plane_tile(
4831    data: &[u8],
4832    image_width: u32,
4833    x0: u32,
4834    y0: u32,
4835    tile_width: u32,
4836    tile_height: u32,
4837    bit_depth: u8,
4838) -> Result<Vec<u8>, &'static str> {
4839    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
4840    let row_bytes = usize::try_from(tile_width)
4841        .map_err(|_| "tile width exceeds usize")?
4842        .checked_mul(bytes_per_sample)
4843        .ok_or("tile row byte count overflow")?;
4844    let out_len = row_bytes
4845        .checked_mul(usize::try_from(tile_height).map_err(|_| "tile height exceeds usize")?)
4846        .ok_or("tile byte count overflow")?;
4847    let mut tile = Vec::with_capacity(out_len);
4848    let image_row_bytes = usize::try_from(image_width)
4849        .map_err(|_| "image width exceeds usize")?
4850        .checked_mul(bytes_per_sample)
4851        .ok_or("image row byte count overflow")?;
4852    let x_byte_offset = usize::try_from(x0)
4853        .map_err(|_| "tile x offset exceeds usize")?
4854        .checked_mul(bytes_per_sample)
4855        .ok_or("tile x byte offset overflow")?;
4856
4857    for y in y0..y0 + tile_height {
4858        let row_start = usize::try_from(y)
4859            .map_err(|_| "tile y offset exceeds usize")?
4860            .checked_mul(image_row_bytes)
4861            .and_then(|offset| offset.checked_add(x_byte_offset))
4862            .ok_or("tile row offset overflow")?;
4863        let row_end = row_start
4864            .checked_add(row_bytes)
4865            .ok_or("tile row range overflow")?;
4866        tile.extend_from_slice(
4867            data.get(row_start..row_end)
4868                .ok_or("component plane tile row range outside source data")?,
4869        );
4870    }
4871
4872    Ok(tile)
4873}
4874
4875fn roi_regions_for_tile(
4876    roi_regions: &[EncodeRoiRegion],
4877    tile_x: u32,
4878    tile_y: u32,
4879    tile_width: u32,
4880    tile_height: u32,
4881) -> Result<Vec<EncodeRoiRegion>, &'static str> {
4882    let tile_x1 = tile_x
4883        .checked_add(tile_width)
4884        .ok_or("tile ROI bounds overflow")?;
4885    let tile_y1 = tile_y
4886        .checked_add(tile_height)
4887        .ok_or("tile ROI bounds overflow")?;
4888    let mut clipped = Vec::new();
4889
4890    for region in roi_regions {
4891        let region_x1 = region
4892            .x
4893            .checked_add(region.width)
4894            .ok_or("ROI region bounds overflow")?;
4895        let region_y1 = region
4896            .y
4897            .checked_add(region.height)
4898            .ok_or("ROI region bounds overflow")?;
4899        let x0 = region.x.max(tile_x);
4900        let y0 = region.y.max(tile_y);
4901        let x1 = region_x1.min(tile_x1);
4902        let y1 = region_y1.min(tile_y1);
4903        if x0 >= x1 || y0 >= y1 {
4904            continue;
4905        }
4906        clipped.push(EncodeRoiRegion {
4907            component: region.component,
4908            x: x0 - tile_x,
4909            y: y0 - tile_y,
4910            width: x1 - x0,
4911            height: y1 - y0,
4912            shift: region.shift,
4913        });
4914    }
4915
4916    Ok(clipped)
4917}
4918
4919fn extract_single_tile_body(codestream: &[u8]) -> Result<&[u8], &'static str> {
4920    let sod = codestream
4921        .windows(2)
4922        .position(|marker| marker == [0xFF, super::codestream::markers::SOD])
4923        .ok_or("encoded tile codestream missing SOD")?;
4924    let eoc = codestream
4925        .windows(2)
4926        .rposition(|marker| marker == [0xFF, super::codestream::markers::EOC])
4927        .ok_or("encoded tile codestream missing EOC")?;
4928    if eoc < sod + 2 {
4929        return Err("encoded tile codestream marker order invalid");
4930    }
4931    Ok(&codestream[sod + 2..eoc])
4932}
4933
4934fn extract_single_tile_plt_packet_lengths(codestream: &[u8]) -> Result<Vec<u32>, &'static str> {
4935    let sod = codestream
4936        .windows(2)
4937        .position(|marker| marker == [0xFF, super::codestream::markers::SOD])
4938        .ok_or("encoded tile codestream missing SOD")?;
4939    let mut packet_lengths = Vec::new();
4940    let mut offset = 0usize;
4941
4942    while offset + 4 <= sod {
4943        if codestream[offset] == 0xFF && codestream[offset + 1] == super::codestream::markers::PLT {
4944            let marker_len =
4945                u16::from_be_bytes([codestream[offset + 2], codestream[offset + 3]]) as usize;
4946            if marker_len < 3 {
4947                return Err("encoded tile codestream has invalid PLT length");
4948            }
4949            let marker_end = offset
4950                .checked_add(2)
4951                .and_then(|value| value.checked_add(marker_len))
4952                .ok_or("encoded tile codestream PLT length overflow")?;
4953            if marker_end > sod {
4954                return Err("encoded tile codestream PLT extends past SOD");
4955            }
4956            let length_bytes = codestream
4957                .get(offset + 5..marker_end)
4958                .ok_or("encoded tile codestream PLT payload out of range")?;
4959            packet_lengths.extend(
4960                super::codestream::decode_packet_lengths(length_bytes)
4961                    .ok_or("encoded tile codestream has invalid PLT packet lengths")?,
4962            );
4963            offset = marker_end;
4964        } else {
4965            offset += 1;
4966        }
4967    }
4968
4969    if packet_lengths.is_empty() {
4970        return Err("encoded tile codestream missing PLT packet lengths");
4971    }
4972
4973    Ok(packet_lengths)
4974}
4975
4976fn extract_single_tile_ppm_packet_headers(codestream: &[u8]) -> Result<Vec<Vec<u8>>, &'static str> {
4977    let sot = codestream
4978        .windows(2)
4979        .position(|marker| marker == [0xFF, super::codestream::markers::SOT])
4980        .ok_or("encoded tile codestream missing SOT")?;
4981    let mut packet_headers = Vec::new();
4982    let mut offset = 0usize;
4983
4984    while offset + 4 <= sot {
4985        if codestream[offset] == 0xFF && codestream[offset + 1] == super::codestream::markers::PPM {
4986            let marker_len =
4987                u16::from_be_bytes([codestream[offset + 2], codestream[offset + 3]]) as usize;
4988            if marker_len < 3 {
4989                return Err("encoded tile codestream has invalid PPM length");
4990            }
4991            let marker_end = offset
4992                .checked_add(2)
4993                .and_then(|value| value.checked_add(marker_len))
4994                .ok_or("encoded tile codestream PPM length overflow")?;
4995            if marker_end > sot {
4996                return Err("encoded tile codestream PPM extends past SOT");
4997            }
4998            let mut payload_offset = offset + 5;
4999            while payload_offset < marker_end {
5000                let header_len_end = payload_offset
5001                    .checked_add(2)
5002                    .ok_or("encoded tile codestream PPM payload overflow")?;
5003                let len_bytes = codestream
5004                    .get(payload_offset..header_len_end)
5005                    .ok_or("encoded tile codestream PPM packet length truncated")?;
5006                let header_len = u16::from_be_bytes([len_bytes[0], len_bytes[1]]) as usize;
5007                let header_start = header_len_end;
5008                let header_end = header_start
5009                    .checked_add(header_len)
5010                    .ok_or("encoded tile codestream PPM packet header overflow")?;
5011                let header = codestream
5012                    .get(header_start..header_end)
5013                    .ok_or("encoded tile codestream PPM packet header truncated")?;
5014                packet_headers.push(header.to_vec());
5015                payload_offset = header_end;
5016            }
5017            offset = marker_end;
5018        } else {
5019            offset += 1;
5020        }
5021    }
5022
5023    if packet_headers.is_empty() {
5024        return Err("encoded tile codestream missing PPM packet headers");
5025    }
5026
5027    Ok(packet_headers)
5028}
5029
5030fn try_encode_forward_rct(
5031    components: &mut [Vec<f32>],
5032    accelerator: &mut impl J2kEncodeStageAccelerator,
5033) -> Result<bool, &'static str> {
5034    debug_assert!(components.len() >= 3);
5035    let (plane0, rest) = components.split_at_mut(1);
5036    let (plane1, plane2) = rest.split_at_mut(1);
5037    accelerator.encode_forward_rct(J2kForwardRctJob {
5038        plane0: &mut plane0[0],
5039        plane1: &mut plane1[0],
5040        plane2: &mut plane2[0],
5041    })
5042}
5043
5044fn try_encode_forward_ict(
5045    components: &mut [Vec<f32>],
5046    accelerator: &mut impl J2kEncodeStageAccelerator,
5047) -> Result<bool, &'static str> {
5048    debug_assert!(components.len() >= 3);
5049    let (plane0, rest) = components.split_at_mut(1);
5050    let (plane1, plane2) = rest.split_at_mut(1);
5051    accelerator.encode_forward_ict(J2kForwardIctJob {
5052        plane0: &mut plane0[0],
5053        plane1: &mut plane1[0],
5054        plane2: &mut plane2[0],
5055    })
5056}
5057
5058fn encode_forward_dwt(
5059    component: &[f32],
5060    width: u32,
5061    height: u32,
5062    num_levels: u8,
5063    reversible: bool,
5064    accelerator: &mut impl J2kEncodeStageAccelerator,
5065) -> Result<DwtDecomposition, &'static str> {
5066    if reversible {
5067        if let Some(output) = accelerator.encode_forward_dwt53(J2kForwardDwt53Job {
5068            samples: component,
5069            width,
5070            height,
5071            num_levels,
5072        })? {
5073            return convert_forward_dwt53_output(output);
5074        }
5075    } else if let Some(output) = accelerator.encode_forward_dwt97(J2kForwardDwt97Job {
5076        samples: component,
5077        width,
5078        height,
5079        num_levels,
5080    })? {
5081        return convert_forward_dwt97_output(output);
5082    }
5083
5084    Ok(fdwt::forward_dwt(
5085        component, width, height, num_levels, reversible,
5086    ))
5087}
5088
5089fn convert_forward_dwt53_output(
5090    output: J2kForwardDwt53Output,
5091) -> Result<DwtDecomposition, &'static str> {
5092    validate_band_len(output.ll.len(), output.ll_width, output.ll_height)?;
5093    let mut levels = Vec::with_capacity(output.levels.len());
5094    for level in output.levels {
5095        validate_dwt53_level(&level)?;
5096        levels.push(fdwt::DwtLevel {
5097            hl: level.hl,
5098            lh: level.lh,
5099            hh: level.hh,
5100            low_width: level.low_width,
5101            low_height: level.low_height,
5102            high_width: level.high_width,
5103            high_height: level.high_height,
5104        });
5105    }
5106    Ok(DwtDecomposition {
5107        ll: output.ll,
5108        ll_width: output.ll_width,
5109        ll_height: output.ll_height,
5110        levels,
5111    })
5112}
5113
5114fn convert_forward_dwt97_output(
5115    output: J2kForwardDwt97Output,
5116) -> Result<DwtDecomposition, &'static str> {
5117    validate_band_len(output.ll.len(), output.ll_width, output.ll_height)?;
5118    let mut levels = Vec::with_capacity(output.levels.len());
5119    for level in output.levels {
5120        validate_dwt97_level(&level)?;
5121        levels.push(fdwt::DwtLevel {
5122            hl: level.hl,
5123            lh: level.lh,
5124            hh: level.hh,
5125            low_width: level.low_width,
5126            low_height: level.low_height,
5127            high_width: level.high_width,
5128            high_height: level.high_height,
5129        });
5130    }
5131    Ok(DwtDecomposition {
5132        ll: output.ll,
5133        ll_width: output.ll_width,
5134        ll_height: output.ll_height,
5135        levels,
5136    })
5137}
5138
5139fn validate_dwt53_level(level: &J2kForwardDwt53Level) -> Result<(), &'static str> {
5140    validate_band_len(level.hl.len(), level.high_width, level.low_height)?;
5141    validate_band_len(level.lh.len(), level.low_width, level.high_height)?;
5142    validate_band_len(level.hh.len(), level.high_width, level.high_height)?;
5143    Ok(())
5144}
5145
5146fn validate_dwt97_level(level: &J2kForwardDwt97Level) -> Result<(), &'static str> {
5147    validate_band_len(level.hl.len(), level.high_width, level.low_height)?;
5148    validate_band_len(level.lh.len(), level.low_width, level.high_height)?;
5149    validate_band_len(level.hh.len(), level.high_width, level.high_height)?;
5150    Ok(())
5151}
5152
5153fn validate_band_len(actual: usize, width: u32, height: u32) -> Result<(), &'static str> {
5154    let expected = (width as usize)
5155        .checked_mul(height as usize)
5156        .ok_or("accelerated DWT output dimensions overflow")?;
5157    if actual != expected {
5158        return Err("accelerated DWT output length mismatch");
5159    }
5160    Ok(())
5161}
5162
5163fn validate_deinterleaved_components(
5164    components: Vec<Vec<f32>>,
5165    num_components: u16,
5166    num_pixels: usize,
5167) -> Result<Vec<Vec<f32>>, &'static str> {
5168    if components.len() != usize::from(num_components) {
5169        return Err("accelerated deinterleave component count mismatch");
5170    }
5171    if components
5172        .iter()
5173        .any(|component| component.len() != num_pixels)
5174    {
5175        return Err("accelerated deinterleave component length mismatch");
5176    }
5177    Ok(components)
5178}
5179
5180fn component_plane_to_f32(
5181    data: &[u8],
5182    width: u32,
5183    height: u32,
5184    bit_depth: u8,
5185    signed: bool,
5186) -> Result<Vec<f32>, &'static str> {
5187    let sample_count = (width as usize)
5188        .checked_mul(height as usize)
5189        .ok_or("image dimensions overflow")?;
5190    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth)?;
5191    let expected_len = sample_count
5192        .checked_mul(bytes_per_sample)
5193        .ok_or("image dimensions overflow")?;
5194    if data.len() != expected_len {
5195        return Err("component plane data length mismatch");
5196    }
5197
5198    let unsigned_offset = if signed {
5199        0.0
5200    } else {
5201        (1_u64 << (u32::from(bit_depth) - 1)) as f32
5202    };
5203    Ok(data
5204        .chunks_exact(bytes_per_sample)
5205        .map(|sample| {
5206            let raw = read_le_sample_value(sample, bit_depth);
5207            if signed {
5208                sign_extend_sample(raw, bit_depth) as f32
5209            } else {
5210                raw as f32 - unsigned_offset
5211            }
5212        })
5213        .collect())
5214}
5215
5216fn forward_dwt53_output_from_decomposition(
5217    decomposition: DwtDecomposition,
5218) -> J2kForwardDwt53Output {
5219    J2kForwardDwt53Output {
5220        ll: decomposition.ll,
5221        ll_width: decomposition.ll_width,
5222        ll_height: decomposition.ll_height,
5223        levels: decomposition
5224            .levels
5225            .into_iter()
5226            .map(|level| {
5227                let width = level.low_width + level.high_width;
5228                let height = level.low_height + level.high_height;
5229                J2kForwardDwt53Level {
5230                    hl: level.hl,
5231                    lh: level.lh,
5232                    hh: level.hh,
5233                    width,
5234                    height,
5235                    low_width: level.low_width,
5236                    low_height: level.low_height,
5237                    high_width: level.high_width,
5238                    high_height: level.high_height,
5239                }
5240            })
5241            .collect(),
5242    }
5243}
5244
5245fn validate_component_sampling_dwt_geometry(
5246    decompositions: &[DwtDecomposition],
5247    reference_width: u32,
5248    reference_height: u32,
5249    component_sampling: &[(u8, u8)],
5250) -> Result<(), &'static str> {
5251    if decompositions.len() != component_sampling.len() {
5252        return Err("component sampling count does not match component count");
5253    }
5254    for (decomposition, &(x_rsiz, y_rsiz)) in decompositions.iter().zip(component_sampling) {
5255        let expected_width = reference_width.div_ceil(u32::from(x_rsiz.max(1)));
5256        let expected_height = reference_height.div_ceil(u32::from(y_rsiz.max(1)));
5257        if dwt_decomposition_dimensions(decomposition) != (expected_width, expected_height) {
5258            return Err("component sampling requires component-sized DWT geometry");
5259        }
5260    }
5261    Ok(())
5262}
5263
5264fn dwt_decomposition_dimensions(decomposition: &DwtDecomposition) -> (u32, u32) {
5265    decomposition
5266        .levels
5267        .last()
5268        .map_or((decomposition.ll_width, decomposition.ll_height), |level| {
5269            (
5270                level.low_width + level.high_width,
5271                level.low_height + level.high_height,
5272            )
5273        })
5274}
5275
5276#[derive(Clone, Debug, Default)]
5277struct ComponentRoiEncodePlan {
5278    shift: u8,
5279    regions: Vec<ComponentRoiEncodeRegion>,
5280}
5281
5282#[derive(Clone, Copy, Debug)]
5283struct ComponentRoiEncodeRegion {
5284    x: u32,
5285    y: u32,
5286    width: u32,
5287    height: u32,
5288}
5289
5290fn component_sampling_for_options(
5291    options: &EncodeOptions,
5292    num_components: u16,
5293) -> Result<Vec<(u8, u8)>, &'static str> {
5294    match &options.component_sampling {
5295        Some(component_sampling) => {
5296            if component_sampling.len() != usize::from(num_components) {
5297                return Err("component sampling count does not match component count");
5298            }
5299            if component_sampling
5300                .iter()
5301                .any(|&(x_rsiz, y_rsiz)| x_rsiz == 0 || y_rsiz == 0)
5302            {
5303                return Err("component sampling factors must be non-zero");
5304            }
5305            Ok(component_sampling.clone())
5306        }
5307        None => Ok(vec![(1, 1); usize::from(num_components)]),
5308    }
5309}
5310
5311fn roi_encode_plans_for_options(
5312    options: &EncodeOptions,
5313    roi_regions: &[EncodeRoiRegion],
5314    num_components: u16,
5315    width: u32,
5316    height: u32,
5317    component_sampling: &[(u8, u8)],
5318    base_bitplanes: u8,
5319    block_coding_mode: BlockCodingMode,
5320) -> Result<Vec<ComponentRoiEncodePlan>, &'static str> {
5321    let whole_component_shifts = roi_component_shifts_for_options(
5322        options,
5323        num_components,
5324        base_bitplanes,
5325        block_coding_mode,
5326    )?;
5327    let mut plans = whole_component_shifts
5328        .iter()
5329        .map(|&shift| ComponentRoiEncodePlan {
5330            shift,
5331            regions: Vec::new(),
5332        })
5333        .collect::<Vec<_>>();
5334
5335    for region in roi_regions {
5336        if region.component >= num_components {
5337            return Err("ROI region component index out of range");
5338        }
5339        if region.width == 0 || region.height == 0 {
5340            return Err("ROI region dimensions must be non-zero");
5341        }
5342        if region.shift == 0 {
5343            return Err("ROI region maxshift must be non-zero");
5344        }
5345
5346        let x1 = region
5347            .x
5348            .checked_add(region.width)
5349            .ok_or("ROI region bounds overflow")?;
5350        let y1 = region
5351            .y
5352            .checked_add(region.height)
5353            .ok_or("ROI region bounds overflow")?;
5354        if region.x >= width || region.y >= height || x1 > width || y1 > height {
5355            return Err("ROI region must be inside image bounds");
5356        }
5357
5358        let component_idx = usize::from(region.component);
5359        if whole_component_shifts[component_idx] != 0 {
5360            return Err("ROI region cannot be combined with whole-component ROI shift");
5361        }
5362        if region.shift < base_bitplanes {
5363            return Err("ROI region maxshift must cover background bitplanes");
5364        }
5365        validate_roi_shift(region.shift, base_bitplanes, block_coding_mode)?;
5366
5367        let plan = &mut plans[component_idx];
5368        if plan.shift == 0 {
5369            plan.shift = region.shift;
5370        } else if plan.shift != region.shift {
5371            return Err("ROI regions for one component must use one maxshift");
5372        }
5373
5374        let &(x_rsiz, y_rsiz) = component_sampling
5375            .get(component_idx)
5376            .ok_or("component sampling count does not match component count")?;
5377        let component_width = width.div_ceil(u32::from(x_rsiz));
5378        let component_height = height.div_ceil(u32::from(y_rsiz));
5379        let component_x0 = region.x / u32::from(x_rsiz);
5380        let component_y0 = region.y / u32::from(y_rsiz);
5381        let component_x1 = x1.div_ceil(u32::from(x_rsiz)).min(component_width);
5382        let component_y1 = y1.div_ceil(u32::from(y_rsiz)).min(component_height);
5383        if component_x0 >= component_x1 || component_y0 >= component_y1 {
5384            return Err("ROI region does not intersect component grid");
5385        }
5386        plan.regions.push(ComponentRoiEncodeRegion {
5387            x: component_x0,
5388            y: component_y0,
5389            width: component_x1 - component_x0,
5390            height: component_y1 - component_y0,
5391        });
5392    }
5393
5394    Ok(plans)
5395}
5396
5397fn roi_component_shifts_for_options(
5398    options: &EncodeOptions,
5399    num_components: u16,
5400    base_bitplanes: u8,
5401    block_coding_mode: BlockCodingMode,
5402) -> Result<Vec<u8>, &'static str> {
5403    if options.roi_component_shifts.is_empty() {
5404        return Ok(vec![0; usize::from(num_components)]);
5405    }
5406    if options.roi_component_shifts.len() != usize::from(num_components) {
5407        return Err("ROI component shift count does not match component count");
5408    }
5409    let max_bitplanes = max_roi_coded_bitplanes(block_coding_mode);
5410    for &shift in &options.roi_component_shifts {
5411        validate_roi_shift_for_max(shift, base_bitplanes, max_bitplanes)?;
5412    }
5413    Ok(options.roi_component_shifts.clone())
5414}
5415
5416fn validate_roi_shift(
5417    shift: u8,
5418    base_bitplanes: u8,
5419    block_coding_mode: BlockCodingMode,
5420) -> Result<(), &'static str> {
5421    let max_bitplanes = max_roi_coded_bitplanes(block_coding_mode);
5422    validate_roi_shift_for_max(shift, base_bitplanes, max_bitplanes)
5423}
5424
5425fn max_roi_coded_bitplanes(block_coding_mode: BlockCodingMode) -> u8 {
5426    match block_coding_mode {
5427        BlockCodingMode::Classic => MAX_CLASSIC_ROI_CODED_BITPLANES,
5428        BlockCodingMode::HighThroughput => MAX_HT_ROI_CODED_BITPLANES,
5429    }
5430}
5431
5432fn validate_roi_shift_for_max(
5433    shift: u8,
5434    base_bitplanes: u8,
5435    max_bitplanes: u8,
5436) -> Result<(), &'static str> {
5437    if base_bitplanes
5438        .checked_add(shift)
5439        .is_none_or(|bitplanes| bitplanes > max_bitplanes)
5440    {
5441        return Err("ROI maxshift exceeds supported coded bitplane count");
5442    }
5443    Ok(())
5444}
5445
5446fn roi_subband_scale(num_levels: u8, level_idx: Option<usize>) -> Result<u32, &'static str> {
5447    let shift = match level_idx {
5448        Some(level_idx) => usize::from(num_levels)
5449            .checked_sub(level_idx)
5450            .ok_or("ROI subband level exceeds decomposition level count")?,
5451        None => usize::from(num_levels),
5452    };
5453    if shift >= u32::BITS as usize {
5454        return Err("ROI subband scale exceeds supported coordinate range");
5455    }
5456    Ok(1_u32 << shift)
5457}
5458
5459fn max_total_bitplanes(step_sizes: &[QuantStepSize], guard_bits: u8) -> Result<u8, &'static str> {
5460    step_sizes
5461        .iter()
5462        .map(|step_size| {
5463            debug_assert!(step_size.exponent <= u16::from(u8::MAX));
5464            guard_bits
5465                .checked_add(
5466                    u8::try_from(step_size.exponent)
5467                        .map_err(|_| "quantization exponent exceeds supported bitplane count")?,
5468                )
5469                .and_then(|value| value.checked_sub(1))
5470                .ok_or("quantization bitplane count underflows")
5471        })
5472        .max()
5473        .unwrap_or(Ok(0))
5474}
5475
5476fn max_total_bitplanes_for_components(
5477    default_step_sizes: &[QuantStepSize],
5478    component_step_sizes: &[Vec<QuantStepSize>],
5479    guard_bits: u8,
5480) -> Result<u8, &'static str> {
5481    let default = max_total_bitplanes(default_step_sizes, guard_bits)?;
5482    component_step_sizes
5483        .iter()
5484        .try_fold(default, |max_bitplanes, step_sizes| {
5485            Ok(max_bitplanes.max(max_total_bitplanes(step_sizes, guard_bits)?))
5486        })
5487}
5488
5489fn validate_component_sample_info(
5490    component_sample_info: &[EncodeComponentSampleInfo],
5491    num_components: usize,
5492) -> Result<(), &'static str> {
5493    if component_sample_info.is_empty() {
5494        return Ok(());
5495    }
5496    if component_sample_info.len() != num_components {
5497        return Err("component sample metadata count does not match component count");
5498    }
5499    if component_sample_info
5500        .iter()
5501        .any(|info| info.bit_depth == 0 || info.bit_depth > MAX_RAW_PIXEL_ENCODE_BIT_DEPTH)
5502    {
5503        return Err("unsupported bit depth");
5504    }
5505    Ok(())
5506}
5507
5508fn component_step_sizes(
5509    component_sample_info: &[EncodeComponentSampleInfo],
5510    num_levels: u8,
5511    reversible: bool,
5512    guard_bits: u8,
5513    quantization_scale: f32,
5514    subband_scales: IrreversibleQuantizationSubbandScales,
5515) -> Vec<Vec<QuantStepSize>> {
5516    component_sample_info
5517        .iter()
5518        .map(|info| {
5519            quantize::compute_step_sizes_with_irreversible_profile(
5520                info.bit_depth,
5521                num_levels,
5522                reversible,
5523                guard_bits,
5524                quantization_scale,
5525                subband_scales,
5526            )
5527        })
5528        .collect()
5529}
5530
5531fn reversible_guard_bits_for_marker_limit(
5532    bit_depth: u8,
5533    num_levels: u8,
5534    requested_guard_bits: u8,
5535) -> Result<u8, &'static str> {
5536    if requested_guard_bits > MAX_REVERSIBLE_NO_QUANT_GUARD_BITS {
5537        return Err("reversible guard bits exceed the Part 1 marker field");
5538    }
5539    let max_reversible_gain = if num_levels == 0 { 0 } else { 2 };
5540    let requested_bitplanes = u16::from(requested_guard_bits)
5541        .checked_add(u16::from(bit_depth))
5542        .and_then(|value| value.checked_add(max_reversible_gain))
5543        .and_then(|value| value.checked_sub(1))
5544        .ok_or("reversible no-quantization bitplane count underflows")?;
5545    if requested_bitplanes > MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES {
5546        return Err("25-38 bit reversible encode exceeds the current no-quantization guard/exponent signaling limit");
5547    }
5548    let min_guard_bits = requested_bitplanes.saturating_sub(MAX_REVERSIBLE_NO_QUANT_EXPONENT - 1);
5549    let guard_bits = requested_guard_bits
5550        .max(u8::try_from(min_guard_bits).map_err(|_| "reversible guard bits exceed u8")?);
5551    if guard_bits > MAX_REVERSIBLE_NO_QUANT_GUARD_BITS {
5552        return Err("reversible guard bits exceed the Part 1 marker field");
5553    }
5554    Ok(guard_bits)
5555}
5556
5557fn adjust_component_step_sizes_for_guard_delta(
5558    component_step_sizes: &mut [Vec<QuantStepSize>],
5559    guard_delta: u8,
5560) -> Result<(), &'static str> {
5561    for step_sizes in component_step_sizes {
5562        adjust_reversible_step_sizes_for_guard_delta(step_sizes, guard_delta)?;
5563    }
5564    Ok(())
5565}
5566
5567fn adjust_reversible_step_sizes_for_guard_delta(
5568    step_sizes: &mut [QuantStepSize],
5569    guard_delta: u8,
5570) -> Result<(), &'static str> {
5571    let guard_delta = u16::from(guard_delta);
5572    for step in step_sizes {
5573        step.exponent = step
5574            .exponent
5575            .checked_sub(guard_delta)
5576            .ok_or("reversible no-quantization exponent underflows guard-bit adjustment")?;
5577        if step.exponent > MAX_REVERSIBLE_NO_QUANT_EXPONENT {
5578            return Err("reversible no-quantization exponent exceeds the Part 1 marker field");
5579        }
5580    }
5581    Ok(())
5582}
5583
5584fn precinct_exponents_for_options(
5585    options: &EncodeOptions,
5586    num_decomposition_levels: u8,
5587) -> Result<Vec<(u8, u8)>, &'static str> {
5588    if options.precinct_exponents.is_empty() {
5589        return Ok(Vec::new());
5590    }
5591
5592    let expected = usize::from(num_decomposition_levels) + 1;
5593    if options.precinct_exponents.len() != expected {
5594        return Err("precinct exponent count must match resolution level count");
5595    }
5596    if options
5597        .precinct_exponents
5598        .iter()
5599        .any(|&(ppx, ppy)| ppx > 15 || ppy > 15)
5600    {
5601        return Err("precinct exponents must fit in COD marker nybbles");
5602    }
5603    let code_block_width_exp = options.code_block_width_exp + 2;
5604    let code_block_height_exp = options.code_block_height_exp + 2;
5605    for (resolution, &(ppx, ppy)) in options.precinct_exponents.iter().enumerate() {
5606        let min_ppx = if resolution == 0 {
5607            code_block_width_exp
5608        } else {
5609            code_block_width_exp + 1
5610        };
5611        let min_ppy = if resolution == 0 {
5612            code_block_height_exp
5613        } else {
5614            code_block_height_exp + 1
5615        };
5616        if ppx < min_ppx || ppy < min_ppy {
5617            return Err("precinct exponents must not reduce encoder code-block dimensions");
5618        }
5619    }
5620    Ok(options.precinct_exponents.clone())
5621}
5622
5623fn count_code_blocks(resolution_packets: &[ResolutionPacket]) -> Result<u32, &'static str> {
5624    let count = resolution_packets
5625        .iter()
5626        .flat_map(|resolution| resolution.subbands.iter())
5627        .try_fold(0usize, |acc, subband| {
5628            acc.checked_add(subband.code_blocks.len())
5629                .ok_or("packetization code-block count overflow")
5630        })?;
5631    u32::try_from(count).map_err(|_| "packetization code-block count exceeds u32")
5632}
5633
5634fn count_compact_code_blocks(
5635    resolution_packets: &[PreparedCompactResolutionPacket<'_>],
5636) -> Result<u32, &'static str> {
5637    let count = resolution_packets
5638        .iter()
5639        .flat_map(|resolution| resolution.subbands.iter())
5640        .try_fold(0usize, |acc, subband| {
5641            acc.checked_add(subband.code_blocks.len())
5642                .ok_or("packetization code-block count overflow")
5643        })?;
5644    u32::try_from(count).map_err(|_| "packetization code-block count exceeds u32")
5645}
5646
5647fn split_component_resolution_packets_by_precinct(
5648    component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
5649    width: u32,
5650    height: u32,
5651    num_decomposition_levels: u8,
5652    precinct_exponents: &[(u8, u8)],
5653) -> Result<Vec<Vec<PreparedResolutionPacket>>, &'static str> {
5654    if precinct_exponents.is_empty() {
5655        return Ok(component_resolution_packets);
5656    }
5657
5658    component_resolution_packets
5659        .into_iter()
5660        .map(|component_packets| {
5661            let mut split_packets = Vec::new();
5662            for packet in component_packets {
5663                split_packets.extend(split_prepared_resolution_packet_by_precinct(
5664                    packet,
5665                    width,
5666                    height,
5667                    num_decomposition_levels,
5668                    precinct_exponents,
5669                )?);
5670            }
5671            Ok(split_packets)
5672        })
5673        .collect()
5674}
5675
5676fn split_prepared_resolution_packet_by_precinct(
5677    packet: PreparedResolutionPacket,
5678    width: u32,
5679    height: u32,
5680    num_decomposition_levels: u8,
5681    precinct_exponents: &[(u8, u8)],
5682) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
5683    let resolution =
5684        usize::try_from(packet.resolution).map_err(|_| "resolution index exceeds usize")?;
5685    let &(ppx, ppy) = precinct_exponents
5686        .get(resolution)
5687        .ok_or("missing precinct exponents for resolution")?;
5688    let (precincts_x, precincts_y) = resolution_precinct_grid(
5689        width,
5690        height,
5691        num_decomposition_levels,
5692        packet.resolution,
5693        ppx,
5694        ppy,
5695    )?;
5696    let packet_count = (precincts_x as usize)
5697        .checked_mul(precincts_y as usize)
5698        .ok_or("precinct packet count overflow")?;
5699    let component = packet.component;
5700    let resolution = packet.resolution;
5701    let subbands = packet.subbands;
5702    let mut packets = Vec::with_capacity(packet_count);
5703
5704    for precinct_y in 0..precincts_y {
5705        for precinct_x in 0..precincts_x {
5706            let precinct = u64::from(precinct_y)
5707                .checked_mul(u64::from(precincts_x))
5708                .and_then(|value| value.checked_add(u64::from(precinct_x)))
5709                .ok_or("precinct index overflow")?;
5710            let split_subbands = subbands
5711                .iter()
5712                .map(|subband| {
5713                    split_prepared_subband_by_precinct(
5714                        subband, resolution, ppx, ppy, precinct_x, precinct_y,
5715                    )
5716                })
5717                .collect::<Result<Vec<_>, &'static str>>()?;
5718            packets.push(PreparedResolutionPacket {
5719                component,
5720                resolution,
5721                precinct,
5722                subbands: split_subbands,
5723            });
5724        }
5725    }
5726
5727    Ok(packets)
5728}
5729
5730fn resolution_precinct_grid(
5731    width: u32,
5732    height: u32,
5733    num_decomposition_levels: u8,
5734    resolution: u32,
5735    ppx: u8,
5736    ppy: u8,
5737) -> Result<(u32, u32), &'static str> {
5738    let resolution_shift = u32::from(num_decomposition_levels)
5739        .checked_sub(resolution)
5740        .ok_or("resolution exceeds decomposition level count")?;
5741    let resolution_scale = pow2_u32(resolution_shift)?;
5742    let resolution_width = width.div_ceil(resolution_scale);
5743    let resolution_height = height.div_ceil(resolution_scale);
5744    let precinct_width = pow2_u32(u32::from(ppx))?;
5745    let precinct_height = pow2_u32(u32::from(ppy))?;
5746
5747    Ok((
5748        if resolution_width == 0 {
5749            0
5750        } else {
5751            resolution_width.div_ceil(precinct_width)
5752        },
5753        if resolution_height == 0 {
5754            0
5755        } else {
5756            resolution_height.div_ceil(precinct_height)
5757        },
5758    ))
5759}
5760
5761fn split_prepared_subband_by_precinct(
5762    subband: &PreparedEncodeSubband,
5763    resolution: u32,
5764    ppx: u8,
5765    ppy: u8,
5766    precinct_x: u32,
5767    precinct_y: u32,
5768) -> Result<PreparedEncodeSubband, &'static str> {
5769    if subband.code_blocks.is_empty() || subband.width == 0 || subband.height == 0 {
5770        return Ok(empty_prepared_subband_precinct(subband));
5771    }
5772
5773    let subband_ppx = if resolution > 0 {
5774        ppx.checked_sub(1)
5775            .ok_or("nonzero resolution precinct exponent underflow")?
5776    } else {
5777        ppx
5778    };
5779    let subband_ppy = if resolution > 0 {
5780        ppy.checked_sub(1)
5781            .ok_or("nonzero resolution precinct exponent underflow")?
5782    } else {
5783        ppy
5784    };
5785    let precinct_width = pow2_u32(u32::from(subband_ppx))?;
5786    let precinct_height = pow2_u32(u32::from(subband_ppy))?;
5787    let precinct_x0 = precinct_x
5788        .checked_mul(precinct_width)
5789        .ok_or("precinct x coordinate overflow")?;
5790    let precinct_y0 = precinct_y
5791        .checked_mul(precinct_height)
5792        .ok_or("precinct y coordinate overflow")?;
5793    let x0 = precinct_x0.min(subband.width);
5794    let y0 = precinct_y0.min(subband.height);
5795    let x1 = precinct_x0
5796        .checked_add(precinct_width)
5797        .ok_or("precinct x extent overflow")?
5798        .min(subband.width);
5799    let y1 = precinct_y0
5800        .checked_add(precinct_height)
5801        .ok_or("precinct y extent overflow")?
5802        .min(subband.height);
5803
5804    if x0 >= x1 || y0 >= y1 {
5805        return Ok(empty_prepared_subband_precinct(subband));
5806    }
5807
5808    let cb_width = subband.code_block_width;
5809    let cb_height = subband.code_block_height;
5810    if cb_width == 0 || cb_height == 0 {
5811        return Ok(empty_prepared_subband_precinct(subband));
5812    }
5813
5814    let cb_x0 = (x0 / cb_width) * cb_width;
5815    let cb_y0 = (y0 / cb_height) * cb_height;
5816    let cb_x1 = x1.div_ceil(cb_width) * cb_width;
5817    let cb_y1 = y1.div_ceil(cb_height) * cb_height;
5818    let cbx_start = cb_x0 / cb_width;
5819    let cby_start = cb_y0 / cb_height;
5820    let cbx_end = cb_x1 / cb_width;
5821    let cby_end = cb_y1 / cb_height;
5822    let num_cbs_x = cbx_end.saturating_sub(cbx_start);
5823    let num_cbs_y = cby_end.saturating_sub(cby_start);
5824    let mut indices = Vec::with_capacity((num_cbs_x as usize).saturating_mul(num_cbs_y as usize));
5825
5826    for cby in cby_start..cby_end {
5827        for cbx in cbx_start..cbx_end {
5828            let index = cby
5829                .checked_mul(subband.num_cbs_x)
5830                .and_then(|value| value.checked_add(cbx))
5831                .ok_or("precinct code-block index overflow")?;
5832            indices.push(usize::try_from(index).map_err(|_| "code-block index exceeds usize")?);
5833        }
5834    }
5835
5836    let code_blocks = indices
5837        .iter()
5838        .map(|&idx| {
5839            subband
5840                .code_blocks
5841                .get(idx)
5842                .cloned()
5843                .ok_or("precinct code-block index out of range")
5844        })
5845        .collect::<Result<Vec<_>, &'static str>>()?;
5846    let preencoded_ht_code_blocks = subband
5847        .preencoded_ht_code_blocks
5848        .as_ref()
5849        .map(|blocks| {
5850            indices
5851                .iter()
5852                .map(|&idx| {
5853                    blocks
5854                        .get(idx)
5855                        .cloned()
5856                        .ok_or("precinct preencoded code-block index out of range")
5857                })
5858                .collect::<Result<Vec<_>, &'static str>>()
5859        })
5860        .transpose()?;
5861
5862    Ok(PreparedEncodeSubband {
5863        code_blocks,
5864        preencoded_ht_code_blocks,
5865        num_cbs_x,
5866        num_cbs_y,
5867        code_block_width: cb_width,
5868        code_block_height: cb_height,
5869        width: x1 - x0,
5870        height: y1 - y0,
5871        sub_band_type: subband.sub_band_type,
5872        total_bitplanes: subband.total_bitplanes,
5873        block_coding_mode: subband.block_coding_mode,
5874        ht_target_coding_passes: subband.ht_target_coding_passes,
5875    })
5876}
5877
5878fn empty_prepared_subband_precinct(subband: &PreparedEncodeSubband) -> PreparedEncodeSubband {
5879    PreparedEncodeSubband {
5880        code_blocks: Vec::new(),
5881        preencoded_ht_code_blocks: subband
5882            .preencoded_ht_code_blocks
5883            .as_ref()
5884            .map(|_| Vec::new()),
5885        num_cbs_x: 0,
5886        num_cbs_y: 0,
5887        code_block_width: subband.code_block_width,
5888        code_block_height: subband.code_block_height,
5889        width: 0,
5890        height: 0,
5891        sub_band_type: subband.sub_band_type,
5892        total_bitplanes: subband.total_bitplanes,
5893        block_coding_mode: subband.block_coding_mode,
5894        ht_target_coding_passes: subband.ht_target_coding_passes,
5895    }
5896}
5897
5898fn pow2_u32(exponent: u32) -> Result<u32, &'static str> {
5899    1_u32
5900        .checked_shl(exponent)
5901        .ok_or("precinct exponent exceeds u32 shift width")
5902}
5903
5904fn packet_descriptors_for_order(
5905    packets: &[PreparedResolutionPacket],
5906    num_layers: u8,
5907    progression_order: EncodeProgressionOrder,
5908) -> Result<Vec<J2kPacketizationPacketDescriptor>, &'static str> {
5909    if num_layers != 1 {
5910        return Err("encode currently prepares one packet contribution layer");
5911    }
5912    let mut descriptors = packets
5913        .iter()
5914        .enumerate()
5915        .map(|(packet_index, packet)| {
5916            Ok(J2kPacketizationPacketDescriptor {
5917                packet_index: u32::try_from(packet_index)
5918                    .map_err(|_| "packet descriptor index exceeds u32")?,
5919                state_index: u32::try_from(packet_index)
5920                    .map_err(|_| "packet descriptor state index exceeds u32")?,
5921                layer: 0,
5922                resolution: packet.resolution,
5923                component: packet.component,
5924                precinct: packet.precinct,
5925            })
5926        })
5927        .collect::<Result<Vec<_>, &'static str>>()?;
5928    sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
5929    Ok(descriptors)
5930}
5931
5932fn packet_descriptors_for_compact_order(
5933    packets: &[PreparedCompactResolutionPacket<'_>],
5934    num_layers: u8,
5935    progression_order: EncodeProgressionOrder,
5936) -> Result<Vec<J2kPacketizationPacketDescriptor>, &'static str> {
5937    if num_layers != 1 {
5938        return Err("encode currently prepares one packet contribution layer");
5939    }
5940    let mut descriptors = packets
5941        .iter()
5942        .enumerate()
5943        .map(|(packet_index, packet)| {
5944            Ok(J2kPacketizationPacketDescriptor {
5945                packet_index: u32::try_from(packet_index)
5946                    .map_err(|_| "packet descriptor index exceeds u32")?,
5947                state_index: u32::try_from(packet_index)
5948                    .map_err(|_| "packet descriptor state index exceeds u32")?,
5949                layer: 0,
5950                resolution: packet.resolution,
5951                component: packet.component,
5952                precinct: packet.precinct,
5953            })
5954        })
5955        .collect::<Result<Vec<_>, &'static str>>()?;
5956    sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
5957    Ok(descriptors)
5958}
5959
5960fn ordered_prepared_resolution_packets(
5961    component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
5962    options: &EncodeOptions,
5963) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
5964    match options.progression_order {
5965        EncodeProgressionOrder::Lrcp
5966        | EncodeProgressionOrder::Rlcp
5967        | EncodeProgressionOrder::Rpcl => {
5968            lrcp_ordered_prepared_resolution_packets(component_resolution_packets)
5969        }
5970        EncodeProgressionOrder::Pcrl | EncodeProgressionOrder::Cprl => {
5971            component_ordered_prepared_resolution_packets(component_resolution_packets)
5972        }
5973    }
5974}
5975
5976fn ordered_prepared_compact_resolution_packets<'a>(
5977    component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
5978    options: &EncodeOptions,
5979) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
5980    match options.progression_order {
5981        EncodeProgressionOrder::Lrcp
5982        | EncodeProgressionOrder::Rlcp
5983        | EncodeProgressionOrder::Rpcl => {
5984            lrcp_ordered_prepared_compact_resolution_packets(component_resolution_packets)
5985        }
5986        EncodeProgressionOrder::Pcrl | EncodeProgressionOrder::Cprl => {
5987            component_ordered_prepared_compact_resolution_packets(component_resolution_packets)
5988        }
5989    }
5990}
5991
5992fn lrcp_ordered_prepared_resolution_packets(
5993    component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
5994) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
5995    let resolution_count = component_resolution_packets
5996        .first()
5997        .map_or(0usize, alloc::vec::Vec::len);
5998    let mut component_iters: Vec<_> = component_resolution_packets
5999        .into_iter()
6000        .map(alloc::vec::Vec::into_iter)
6001        .collect();
6002    let mut resolution_packets =
6003        Vec::with_capacity(resolution_count.saturating_mul(component_iters.len()));
6004
6005    for _resolution in 0..resolution_count {
6006        for component in &mut component_iters {
6007            resolution_packets.push(
6008                component
6009                    .next()
6010                    .ok_or("component packet resolution count mismatch")?,
6011            );
6012        }
6013    }
6014
6015    if component_iters
6016        .iter_mut()
6017        .any(|component| component.next().is_some())
6018    {
6019        return Err("component packet resolution count mismatch");
6020    }
6021
6022    Ok(resolution_packets)
6023}
6024
6025fn lrcp_ordered_prepared_compact_resolution_packets<'a>(
6026    component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
6027) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
6028    let resolution_count = component_resolution_packets
6029        .first()
6030        .map_or(0usize, alloc::vec::Vec::len);
6031    let mut component_iters: Vec<_> = component_resolution_packets
6032        .into_iter()
6033        .map(alloc::vec::Vec::into_iter)
6034        .collect();
6035    let mut resolution_packets =
6036        Vec::with_capacity(resolution_count.saturating_mul(component_iters.len()));
6037
6038    for _resolution in 0..resolution_count {
6039        for component in &mut component_iters {
6040            resolution_packets.push(
6041                component
6042                    .next()
6043                    .ok_or("component packet resolution count mismatch")?,
6044            );
6045        }
6046    }
6047
6048    if component_iters
6049        .iter_mut()
6050        .any(|component| component.next().is_some())
6051    {
6052        return Err("component packet resolution count mismatch");
6053    }
6054
6055    Ok(resolution_packets)
6056}
6057
6058fn component_ordered_prepared_resolution_packets(
6059    component_resolution_packets: Vec<Vec<PreparedResolutionPacket>>,
6060) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
6061    let resolution_count = component_resolution_packets
6062        .first()
6063        .map_or(0usize, alloc::vec::Vec::len);
6064    let mut resolution_packets =
6065        Vec::with_capacity(resolution_count.saturating_mul(component_resolution_packets.len()));
6066
6067    for component in component_resolution_packets {
6068        if component.len() != resolution_count {
6069            return Err("component packet resolution count mismatch");
6070        }
6071        resolution_packets.extend(component);
6072    }
6073
6074    Ok(resolution_packets)
6075}
6076
6077fn component_ordered_prepared_compact_resolution_packets<'a>(
6078    component_resolution_packets: Vec<Vec<PreparedCompactResolutionPacket<'a>>>,
6079) -> Result<Vec<PreparedCompactResolutionPacket<'a>>, &'static str> {
6080    let resolution_count = component_resolution_packets
6081        .first()
6082        .map_or(0usize, alloc::vec::Vec::len);
6083    let mut resolution_packets =
6084        Vec::with_capacity(resolution_count.saturating_mul(component_resolution_packets.len()));
6085
6086    for component in component_resolution_packets {
6087        if component.len() != resolution_count {
6088            return Err("component packet resolution count mismatch");
6089        }
6090        resolution_packets.extend(component);
6091    }
6092
6093    Ok(resolution_packets)
6094}
6095
6096fn public_packetization_progression_order(
6097    progression_order: EncodeProgressionOrder,
6098) -> crate::J2kPacketizationProgressionOrder {
6099    match progression_order {
6100        EncodeProgressionOrder::Lrcp => crate::J2kPacketizationProgressionOrder::Lrcp,
6101        EncodeProgressionOrder::Rlcp => crate::J2kPacketizationProgressionOrder::Rlcp,
6102        EncodeProgressionOrder::Rpcl => crate::J2kPacketizationProgressionOrder::Rpcl,
6103        EncodeProgressionOrder::Pcrl => crate::J2kPacketizationProgressionOrder::Pcrl,
6104        EncodeProgressionOrder::Cprl => crate::J2kPacketizationProgressionOrder::Cprl,
6105    }
6106}
6107
6108fn scalar_packet_descriptors(
6109    descriptors: &[J2kPacketizationPacketDescriptor],
6110) -> Vec<packet_encode::PacketDescriptor> {
6111    descriptors
6112        .iter()
6113        .map(|descriptor| packet_encode::PacketDescriptor {
6114            packet_index: descriptor.packet_index,
6115            state_index: descriptor.state_index,
6116            layer: descriptor.layer,
6117            resolution: descriptor.resolution,
6118            component: descriptor.component,
6119            precinct: descriptor.precinct,
6120        })
6121        .collect()
6122}
6123
6124fn public_packetization_resolutions(
6125    resolution_packets: &[ResolutionPacket],
6126) -> Vec<J2kPacketizationResolution<'_>> {
6127    resolution_packets
6128        .iter()
6129        .map(|resolution| J2kPacketizationResolution {
6130            subbands: resolution
6131                .subbands
6132                .iter()
6133                .map(|subband| J2kPacketizationSubband {
6134                    code_blocks: subband
6135                        .code_blocks
6136                        .iter()
6137                        .map(|code_block| J2kPacketizationCodeBlock {
6138                            data: &code_block.data,
6139                            ht_cleanup_length: code_block.ht_cleanup_length,
6140                            ht_refinement_length: code_block.ht_refinement_length,
6141                            num_coding_passes: code_block.num_coding_passes,
6142                            num_zero_bitplanes: code_block.num_zero_bitplanes,
6143                            previously_included: code_block.previously_included,
6144                            l_block: code_block.l_block,
6145                            block_coding_mode: public_packetization_block_coding_mode(
6146                                code_block.block_coding_mode,
6147                            ),
6148                        })
6149                        .collect(),
6150                    num_cbs_x: subband.num_cbs_x,
6151                    num_cbs_y: subband.num_cbs_y,
6152                })
6153                .collect(),
6154        })
6155        .collect()
6156}
6157
6158fn packetize_resolution_packets_with_options(
6159    resolution_packets: &mut [ResolutionPacket],
6160    packet_descriptors: &[J2kPacketizationPacketDescriptor],
6161    num_layers: u8,
6162    num_components: u16,
6163    progression_order: EncodeProgressionOrder,
6164    marker_options: packet_encode::PacketMarkerOptions,
6165    allow_packetization_accelerator: bool,
6166    force_scalar_packetization: bool,
6167    accelerator: &mut impl J2kEncodeStageAccelerator,
6168) -> Result<packet_encode::PacketizedTileData, &'static str> {
6169    let packetization_resolutions = public_packetization_resolutions(resolution_packets);
6170    let packetization_job = J2kPacketizationEncodeJob {
6171        resolution_count: resolution_packets.len() as u32,
6172        num_layers,
6173        num_components,
6174        code_block_count: count_code_blocks(resolution_packets)?,
6175        progression_order: public_packetization_progression_order(progression_order),
6176        packet_descriptors,
6177        resolutions: &packetization_resolutions,
6178    };
6179    if allow_packetization_accelerator && !force_scalar_packetization {
6180        if let Some(data) = accelerator.encode_packetization(packetization_job)? {
6181            return Ok(packet_encode::PacketizedTileData {
6182                data,
6183                packet_lengths: Vec::new(),
6184                packet_headers: Vec::new(),
6185            });
6186        }
6187    }
6188
6189    let scalar_packet_descriptors = scalar_packet_descriptors(packet_descriptors);
6190    packet_encode::form_tile_bitstream_with_descriptors_lengths_and_markers(
6191        resolution_packets,
6192        &scalar_packet_descriptors,
6193        marker_options,
6194    )
6195}
6196
6197fn packetization_requires_scalar(
6198    params: &EncodeParams,
6199    tile_part_packet_limit: Option<u16>,
6200) -> bool {
6201    params.write_plt
6202        || params.write_plm
6203        || params.write_ppm
6204        || params.write_ppt
6205        || params.write_sop
6206        || params.write_eph
6207        || tile_part_packet_limit.is_some()
6208}
6209
6210fn public_packetization_resolutions_from_compact<'a>(
6211    resolution_packets: &'a [PreparedCompactResolutionPacket<'a>],
6212) -> Vec<J2kPacketizationResolution<'a>> {
6213    resolution_packets
6214        .iter()
6215        .map(|resolution| J2kPacketizationResolution {
6216            subbands: resolution
6217                .subbands
6218                .iter()
6219                .map(|subband| J2kPacketizationSubband {
6220                    code_blocks: subband
6221                        .code_blocks
6222                        .iter()
6223                        .map(|code_block| J2kPacketizationCodeBlock {
6224                            data: code_block.data,
6225                            ht_cleanup_length: code_block.cleanup_length,
6226                            ht_refinement_length: code_block.refinement_length,
6227                            num_coding_passes: code_block.num_coding_passes,
6228                            num_zero_bitplanes: code_block.num_zero_bitplanes,
6229                            previously_included: false,
6230                            l_block: 3,
6231                            block_coding_mode: J2kPacketizationBlockCodingMode::HighThroughput,
6232                        })
6233                        .collect(),
6234                    num_cbs_x: subband.num_cbs_x,
6235                    num_cbs_y: subband.num_cbs_y,
6236                })
6237                .collect(),
6238        })
6239        .collect()
6240}
6241
6242fn public_packetization_block_coding_mode(
6243    block_coding_mode: BlockCodingMode,
6244) -> J2kPacketizationBlockCodingMode {
6245    match block_coding_mode {
6246        BlockCodingMode::Classic => J2kPacketizationBlockCodingMode::Classic,
6247        BlockCodingMode::HighThroughput => J2kPacketizationBlockCodingMode::HighThroughput,
6248    }
6249}
6250
6251#[derive(Clone)]
6252struct PreparedEncodeCodeBlock {
6253    coefficients: Vec<i64>,
6254    width: u32,
6255    height: u32,
6256}
6257
6258#[derive(Clone)]
6259struct PreparedEncodeSubband {
6260    code_blocks: Vec<PreparedEncodeCodeBlock>,
6261    preencoded_ht_code_blocks: Option<Vec<EncodedHtJ2kCodeBlock>>,
6262    num_cbs_x: u32,
6263    num_cbs_y: u32,
6264    code_block_width: u32,
6265    code_block_height: u32,
6266    width: u32,
6267    height: u32,
6268    sub_band_type: SubBandType,
6269    total_bitplanes: u8,
6270    block_coding_mode: BlockCodingMode,
6271    ht_target_coding_passes: u8,
6272}
6273
6274struct PreparedResolutionPacket {
6275    component: u16,
6276    resolution: u32,
6277    precinct: u64,
6278    subbands: Vec<PreparedEncodeSubband>,
6279}
6280
6281struct PreparedCompactCodeBlock<'a> {
6282    data: &'a [u8],
6283    cleanup_length: u32,
6284    refinement_length: u32,
6285    num_coding_passes: u8,
6286    num_zero_bitplanes: u8,
6287}
6288
6289struct PreparedCompactSubband<'a> {
6290    code_blocks: Vec<PreparedCompactCodeBlock<'a>>,
6291    num_cbs_x: u32,
6292    num_cbs_y: u32,
6293}
6294
6295struct PreparedCompactResolutionPacket<'a> {
6296    component: u16,
6297    resolution: u32,
6298    precinct: u64,
6299    subbands: Vec<PreparedCompactSubband<'a>>,
6300}
6301
6302struct PreparedPrecomputedHtj2k97Image {
6303    params: EncodeParams,
6304    quant_params: Vec<(u16, u16)>,
6305    packet_descriptors: Vec<J2kPacketizationPacketDescriptor>,
6306    packet_count: usize,
6307    prepared_packets: Vec<PreparedResolutionPacket>,
6308}
6309
6310fn prepare_precomputed_htj2k97_image_for_batch(
6311    image: &PrecomputedHtj2k97Image,
6312    options: &EncodeOptions,
6313) -> Result<PreparedPrecomputedHtj2k97Image, &'static str> {
6314    if image.width == 0 || image.height == 0 {
6315        return Err("invalid dimensions");
6316    }
6317    if image.components.is_empty() || image.components.len() > usize::from(MAX_J2K_SPEC_COMPONENTS)
6318    {
6319        return Err("unsupported component count");
6320    }
6321    if image.bit_depth == 0 || image.bit_depth > 16 {
6322        return Err("unsupported bit depth");
6323    }
6324    validate_irreversible_quantization_profile(options)?;
6325    if image
6326        .components
6327        .iter()
6328        .any(|component| component.x_rsiz == 0 || component.y_rsiz == 0)
6329    {
6330        return Err("component sampling factors must be non-zero");
6331    }
6332    validate_precomputed_dwt97_geometry(image)?;
6333
6334    let num_components =
6335        u16::try_from(image.components.len()).map_err(|_| "unsupported component count")?;
6336    let num_levels = precomputed_97_level_count(&image.components)?;
6337    let guard_bits = options.guard_bits.max(2);
6338    let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
6339        image.bit_depth,
6340        num_levels,
6341        false,
6342        guard_bits,
6343        options.irreversible_quantization_scale,
6344        options.irreversible_quantization_subband_scales,
6345    );
6346    let quant_params: Vec<(u16, u16)> = step_sizes
6347        .iter()
6348        .map(|s| (s.exponent, s.mantissa))
6349        .collect();
6350    let cb_width = 1u32 << (options.code_block_width_exp + 2);
6351    let cb_height = 1u32 << (options.code_block_height_exp + 2);
6352    let component_sampling = image
6353        .components
6354        .iter()
6355        .map(|component| (component.x_rsiz, component.y_rsiz))
6356        .collect::<Vec<_>>();
6357    let mut precomputed_options = options.clone();
6358    precomputed_options.num_decomposition_levels = num_levels;
6359    precomputed_options.reversible = false;
6360    precomputed_options.use_ht_block_coding = true;
6361    precomputed_options.use_mct = false;
6362    precomputed_options.validate_high_throughput_codestream = false;
6363    precomputed_options.component_sampling = Some(component_sampling.clone());
6364    let precinct_exponents = precinct_exponents_for_options(&precomputed_options, num_levels)?;
6365    let params = EncodeParams {
6366        width: image.width,
6367        height: image.height,
6368        tile_width: image.width,
6369        tile_height: image.height,
6370        num_components,
6371        bit_depth: image.bit_depth,
6372        signed: image.signed,
6373        component_sample_info: Vec::new(),
6374        component_quantization_step_sizes: Vec::new(),
6375        num_decomposition_levels: num_levels,
6376        reversible: false,
6377        code_block_width_exp: precomputed_options.code_block_width_exp,
6378        code_block_height_exp: precomputed_options.code_block_height_exp,
6379        num_layers: 1,
6380        use_mct: false,
6381        guard_bits,
6382        block_coding_mode: BlockCodingMode::HighThroughput,
6383        progression_order: precomputed_options.progression_order,
6384        write_tlm: precomputed_options.write_tlm,
6385        write_plt: precomputed_options.write_plt,
6386        write_plm: precomputed_options.write_plm,
6387        write_ppm: precomputed_options.write_ppm,
6388        write_ppt: precomputed_options.write_ppt,
6389        write_sop: precomputed_options.write_sop,
6390        write_eph: precomputed_options.write_eph,
6391        terminate_coding_passes: false,
6392        component_sampling,
6393        roi_component_shifts: vec![0; usize::from(num_components)],
6394        precinct_exponents,
6395    };
6396
6397    let component_resolution_packets = image
6398        .components
6399        .iter()
6400        .enumerate()
6401        .map(|(component_idx, component)| {
6402            prepared_resolution_packets_from_precomputed_97_component(
6403                component_idx,
6404                component,
6405                &step_sizes,
6406                image.bit_depth,
6407                guard_bits,
6408                cb_width,
6409                cb_height,
6410            )
6411        })
6412        .collect::<Result<Vec<_>, _>>()?;
6413    let component_resolution_packets = split_component_resolution_packets_by_precinct(
6414        component_resolution_packets,
6415        image.width,
6416        image.height,
6417        num_levels,
6418        &params.precinct_exponents,
6419    )?;
6420    let prepared_packets =
6421        ordered_prepared_resolution_packets(component_resolution_packets, &precomputed_options)?;
6422    let packet_descriptors =
6423        packet_descriptors_for_order(&prepared_packets, 1, precomputed_options.progression_order)?;
6424
6425    Ok(PreparedPrecomputedHtj2k97Image {
6426        params,
6427        quant_params,
6428        packet_descriptors,
6429        packet_count: 0,
6430        prepared_packets,
6431    })
6432}
6433
6434fn prepared_resolution_packets_from_precomputed_97_component(
6435    component_idx: usize,
6436    component: &PrecomputedHtj2k97Component,
6437    step_sizes: &[QuantStepSize],
6438    bit_depth: u8,
6439    guard_bits: u8,
6440    cb_width: u32,
6441    cb_height: u32,
6442) -> Result<Vec<PreparedResolutionPacket>, &'static str> {
6443    let component_idx = u16::try_from(component_idx).map_err(|_| "component index exceeds u16")?;
6444    let mut packets = Vec::with_capacity(component.dwt.levels.len() + 1);
6445    packets.push(PreparedResolutionPacket {
6446        component: component_idx,
6447        resolution: 0,
6448        precinct: 0,
6449        subbands: vec![prepare_subband_cpu_quantized(
6450            &component.dwt.ll,
6451            component.dwt.ll_width,
6452            component.dwt.ll_height,
6453            step_sizes
6454                .first()
6455                .ok_or("irreversible quantization step missing")?,
6456            bit_depth,
6457            guard_bits,
6458            false,
6459            BlockCodingMode::HighThroughput,
6460            cb_width,
6461            cb_height,
6462            SubBandType::LowLow,
6463        )?],
6464    });
6465
6466    for (level_idx, level) in component.dwt.levels.iter().enumerate() {
6467        let step_base = 1 + level_idx * 3;
6468        packets.push(PreparedResolutionPacket {
6469            component: component_idx,
6470            resolution: u32::try_from(level_idx + 1).map_err(|_| "resolution index exceeds u32")?,
6471            precinct: 0,
6472            subbands: vec![
6473                prepare_subband_cpu_quantized(
6474                    &level.hl,
6475                    level.high_width,
6476                    level.low_height,
6477                    step_sizes
6478                        .get(step_base)
6479                        .ok_or("irreversible quantization step missing")?,
6480                    bit_depth,
6481                    guard_bits,
6482                    false,
6483                    BlockCodingMode::HighThroughput,
6484                    cb_width,
6485                    cb_height,
6486                    SubBandType::HighLow,
6487                )?,
6488                prepare_subband_cpu_quantized(
6489                    &level.lh,
6490                    level.low_width,
6491                    level.high_height,
6492                    step_sizes
6493                        .get(step_base + 1)
6494                        .ok_or("irreversible quantization step missing")?,
6495                    bit_depth,
6496                    guard_bits,
6497                    false,
6498                    BlockCodingMode::HighThroughput,
6499                    cb_width,
6500                    cb_height,
6501                    SubBandType::LowHigh,
6502                )?,
6503                prepare_subband_cpu_quantized(
6504                    &level.hh,
6505                    level.high_width,
6506                    level.high_height,
6507                    step_sizes
6508                        .get(step_base + 2)
6509                        .ok_or("irreversible quantization step missing")?,
6510                    bit_depth,
6511                    guard_bits,
6512                    false,
6513                    BlockCodingMode::HighThroughput,
6514                    cb_width,
6515                    cb_height,
6516                    SubBandType::HighHigh,
6517                )?,
6518            ],
6519        });
6520    }
6521
6522    Ok(packets)
6523}
6524
6525fn copy_code_block_coefficients(
6526    quantized: &[i32],
6527    width: usize,
6528    x0: usize,
6529    y0: usize,
6530    cbw: usize,
6531    cbh: usize,
6532) -> Vec<i32> {
6533    let len = cbw * cbh;
6534    let start = y0 * width + x0;
6535    if cbw == width {
6536        return quantized[start..start + len].to_vec();
6537    }
6538
6539    let mut coefficients = Vec::with_capacity(len);
6540    for y in 0..cbh {
6541        let row_start = (y0 + y) * width + x0;
6542        coefficients.extend_from_slice(&quantized[row_start..row_start + cbw]);
6543    }
6544    coefficients
6545}
6546
6547fn copy_code_block_coefficients_i64(
6548    quantized: &[i64],
6549    width: usize,
6550    x0: usize,
6551    y0: usize,
6552    cbw: usize,
6553    cbh: usize,
6554) -> Vec<i64> {
6555    let len = cbw * cbh;
6556    let start = y0 * width + x0;
6557    if cbw == width {
6558        return quantized[start..start + len].to_vec();
6559    }
6560
6561    let mut coefficients = Vec::with_capacity(len);
6562    for y in 0..cbh {
6563        let row_start = (y0 + y) * width + x0;
6564        coefficients.extend_from_slice(&quantized[row_start..row_start + cbw]);
6565    }
6566    coefficients
6567}
6568
6569fn coefficients_fit_i32(coefficients: &[i64]) -> bool {
6570    coefficients
6571        .iter()
6572        .all(|&coefficient| i32::try_from(coefficient).is_ok())
6573}
6574
6575fn downcast_i64_coefficients_to_i32(coefficients: &[i64]) -> Result<Vec<i32>, &'static str> {
6576    coefficients
6577        .iter()
6578        .map(|&coefficient| {
6579            i32::try_from(coefficient).map_err(|_| {
6580                "HTJ2K/accelerated code-block encode does not support i64 coefficients"
6581            })
6582        })
6583        .collect()
6584}
6585
6586fn apply_roi_maxshift_encode(
6587    coefficients: &mut [i32],
6588    width: u32,
6589    height: u32,
6590    roi_shift: u8,
6591    roi_regions: &[ComponentRoiEncodeRegion],
6592    roi_scale: u32,
6593) -> Result<(), &'static str> {
6594    if roi_shift == 0 {
6595        return Ok(());
6596    }
6597    let expected_len = (width as usize)
6598        .checked_mul(height as usize)
6599        .ok_or("ROI subband dimensions overflow")?;
6600    if coefficients.len() != expected_len {
6601        return Err("ROI subband coefficient length mismatch");
6602    }
6603    if roi_regions.is_empty() {
6604        for coefficient in coefficients {
6605            shift_roi_coefficient(coefficient, roi_shift)?;
6606        }
6607        return Ok(());
6608    }
6609
6610    let mut selected = vec![false; coefficients.len()];
6611    for region in roi_regions {
6612        let Some((x0, y0, x1, y1)) = roi_region_subband_window(*region, width, height, roi_scale)
6613        else {
6614            continue;
6615        };
6616        for y in y0..y1 {
6617            for x in x0..x1 {
6618                let idx = (y as usize)
6619                    .checked_mul(width as usize)
6620                    .and_then(|row| row.checked_add(x as usize))
6621                    .ok_or("ROI subband index overflow")?;
6622                if selected[idx] {
6623                    continue;
6624                }
6625                selected[idx] = true;
6626                shift_roi_coefficient(&mut coefficients[idx], roi_shift)?;
6627            }
6628        }
6629    }
6630    Ok(())
6631}
6632
6633fn apply_roi_maxshift_encode_i64(
6634    coefficients: &mut [i64],
6635    width: u32,
6636    height: u32,
6637    roi_shift: u8,
6638    roi_regions: &[ComponentRoiEncodeRegion],
6639    roi_scale: u32,
6640) -> Result<(), &'static str> {
6641    if roi_shift == 0 {
6642        return Ok(());
6643    }
6644    let expected_len = (width as usize)
6645        .checked_mul(height as usize)
6646        .ok_or("ROI subband dimensions overflow")?;
6647    if coefficients.len() != expected_len {
6648        return Err("ROI subband coefficient length mismatch");
6649    }
6650    if roi_regions.is_empty() {
6651        for coefficient in coefficients {
6652            shift_roi_coefficient_i64(coefficient, roi_shift)?;
6653        }
6654        return Ok(());
6655    }
6656
6657    let mut selected = vec![false; coefficients.len()];
6658    for region in roi_regions {
6659        let Some((x0, y0, x1, y1)) = roi_region_subband_window(*region, width, height, roi_scale)
6660        else {
6661            continue;
6662        };
6663        for y in y0..y1 {
6664            for x in x0..x1 {
6665                let idx = (y as usize)
6666                    .checked_mul(width as usize)
6667                    .and_then(|row| row.checked_add(x as usize))
6668                    .ok_or("ROI subband index overflow")?;
6669                if selected[idx] {
6670                    continue;
6671                }
6672                selected[idx] = true;
6673                shift_roi_coefficient_i64(&mut coefficients[idx], roi_shift)?;
6674            }
6675        }
6676    }
6677    Ok(())
6678}
6679
6680fn shift_roi_coefficient(coefficient: &mut i32, roi_shift: u8) -> Result<(), &'static str> {
6681    *coefficient = coefficient
6682        .checked_shl(u32::from(roi_shift))
6683        .ok_or("ROI maxshift coefficient overflow")?;
6684    Ok(())
6685}
6686
6687fn shift_roi_coefficient_i64(coefficient: &mut i64, roi_shift: u8) -> Result<(), &'static str> {
6688    let factor = 1_i64
6689        .checked_shl(u32::from(roi_shift))
6690        .ok_or("ROI maxshift coefficient overflow")?;
6691    *coefficient = coefficient
6692        .checked_mul(factor)
6693        .ok_or("ROI maxshift coefficient overflow")?;
6694    Ok(())
6695}
6696
6697fn roi_region_subband_window(
6698    region: ComponentRoiEncodeRegion,
6699    width: u32,
6700    height: u32,
6701    roi_scale: u32,
6702) -> Option<(u32, u32, u32, u32)> {
6703    if width == 0 || height == 0 || roi_scale == 0 {
6704        return None;
6705    }
6706    let x1 = region.x.saturating_add(region.width);
6707    let y1 = region.y.saturating_add(region.height);
6708    let x0 = (region.x / roi_scale).min(width);
6709    let y0 = (region.y / roi_scale).min(height);
6710    let x1 = x1.div_ceil(roi_scale).min(width);
6711    let y1 = y1.div_ceil(roi_scale).min(height);
6712    if x0 >= x1 || y0 >= y1 {
6713        None
6714    } else {
6715        Some((x0, y0, x1, y1))
6716    }
6717}
6718
6719fn prepare_subband(
6720    coefficients: &[f32],
6721    width: u32,
6722    height: u32,
6723    step_size: &QuantStepSize,
6724    bit_depth: u8,
6725    guard_bits: u8,
6726    reversible: bool,
6727    block_coding_mode: BlockCodingMode,
6728    cb_width: u32,
6729    cb_height: u32,
6730    sub_band_type: SubBandType,
6731    roi_shift: u8,
6732    roi_regions: &[ComponentRoiEncodeRegion],
6733    roi_scale: u32,
6734    ht_target_coding_passes: u8,
6735    accelerator: &mut impl J2kEncodeStageAccelerator,
6736) -> Result<PreparedEncodeSubband, &'static str> {
6737    if width == 0 || height == 0 {
6738        return Ok(PreparedEncodeSubband {
6739            code_blocks: Vec::new(),
6740            preencoded_ht_code_blocks: None,
6741            num_cbs_x: 0,
6742            num_cbs_y: 0,
6743            code_block_width: cb_width,
6744            code_block_height: cb_height,
6745            width,
6746            height,
6747            sub_band_type,
6748            total_bitplanes: 0,
6749            block_coding_mode,
6750            ht_target_coding_passes,
6751        });
6752    }
6753
6754    let range_bits = subband_range_bits(bit_depth, sub_band_type);
6755    debug_assert!(step_size.exponent <= u16::from(u8::MAX));
6756    let base_total_bitplanes = guard_bits
6757        .saturating_add(step_size.exponent as u8)
6758        .saturating_sub(1);
6759    let total_bitplanes = base_total_bitplanes
6760        .checked_add(roi_shift)
6761        .ok_or("ROI maxshift exceeds supported coded bitplane count")?;
6762    let num_cbs_x = width.div_ceil(cb_width);
6763    let num_cbs_y = height.div_ceil(cb_height);
6764
6765    if block_coding_mode == BlockCodingMode::HighThroughput
6766        && roi_shift == 0
6767        && ht_target_coding_passes == 1
6768    {
6769        if let Some(encoded) = accelerator.encode_ht_subband(J2kHtSubbandEncodeJob {
6770            coefficients,
6771            width,
6772            height,
6773            step_exponent: step_size.exponent,
6774            step_mantissa: step_size.mantissa,
6775            range_bits,
6776            reversible,
6777            code_block_width: cb_width,
6778            code_block_height: cb_height,
6779            total_bitplanes,
6780        })? {
6781            let expected_code_blocks = (num_cbs_x as usize)
6782                .checked_mul(num_cbs_y as usize)
6783                .ok_or("code-block count overflow")?;
6784            if encoded.len() != expected_code_blocks {
6785                return Err("accelerated HT subband code-block count mismatch");
6786            }
6787            return Ok(PreparedEncodeSubband {
6788                code_blocks: code_block_shapes(width, height, cb_width, cb_height)?,
6789                preencoded_ht_code_blocks: Some(encoded),
6790                num_cbs_x,
6791                num_cbs_y,
6792                code_block_width: cb_width,
6793                code_block_height: cb_height,
6794                width,
6795                height,
6796                sub_band_type,
6797                total_bitplanes,
6798                block_coding_mode,
6799                ht_target_coding_passes,
6800            });
6801        }
6802    }
6803
6804    let mut quantized = match accelerator.encode_quantize_subband(J2kQuantizeSubbandJob {
6805        coefficients,
6806        step_exponent: step_size.exponent,
6807        step_mantissa: step_size.mantissa,
6808        range_bits,
6809        reversible,
6810    })? {
6811        Some(quantized) => {
6812            if quantized.len() != coefficients.len() {
6813                return Err("accelerated quantized subband length mismatch");
6814            }
6815            quantized
6816        }
6817        None => quantize::quantize_subband(coefficients, step_size, range_bits, reversible),
6818    };
6819    apply_roi_maxshift_encode(
6820        &mut quantized,
6821        width,
6822        height,
6823        roi_shift,
6824        roi_regions,
6825        roi_scale,
6826    )?;
6827
6828    // Split into code-blocks
6829    let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
6830
6831    for cby in 0..num_cbs_y {
6832        for cbx in 0..num_cbs_x {
6833            let x0 = cbx * cb_width;
6834            let y0 = cby * cb_height;
6835            let x1 = (x0 + cb_width).min(width);
6836            let y1 = (y0 + cb_height).min(height);
6837            let cbw = x1 - x0;
6838            let cbh = y1 - y0;
6839
6840            let cb_coeffs = copy_code_block_coefficients(
6841                &quantized,
6842                width as usize,
6843                x0 as usize,
6844                y0 as usize,
6845                cbw as usize,
6846                cbh as usize,
6847            )
6848            .into_iter()
6849            .map(i64::from)
6850            .collect();
6851
6852            code_blocks.push(PreparedEncodeCodeBlock {
6853                coefficients: cb_coeffs,
6854                width: cbw,
6855                height: cbh,
6856            });
6857        }
6858    }
6859
6860    Ok(PreparedEncodeSubband {
6861        code_blocks,
6862        preencoded_ht_code_blocks: None,
6863        num_cbs_x,
6864        num_cbs_y,
6865        code_block_width: cb_width,
6866        code_block_height: cb_height,
6867        width,
6868        height,
6869        sub_band_type,
6870        total_bitplanes,
6871        block_coding_mode,
6872        ht_target_coding_passes,
6873    })
6874}
6875
6876#[allow(clippy::too_many_arguments)]
6877fn prepare_subband_i64(
6878    coefficients: &[i64],
6879    width: u32,
6880    height: u32,
6881    step_size: &QuantStepSize,
6882    guard_bits: u8,
6883    cb_width: u32,
6884    cb_height: u32,
6885    sub_band_type: SubBandType,
6886    roi_shift: u8,
6887    roi_regions: &[ComponentRoiEncodeRegion],
6888    roi_scale: u32,
6889    block_coding_mode: BlockCodingMode,
6890    ht_target_coding_passes: u8,
6891) -> Result<PreparedEncodeSubband, &'static str> {
6892    if width == 0 || height == 0 {
6893        return Ok(PreparedEncodeSubband {
6894            code_blocks: Vec::new(),
6895            preencoded_ht_code_blocks: None,
6896            num_cbs_x: 0,
6897            num_cbs_y: 0,
6898            code_block_width: cb_width,
6899            code_block_height: cb_height,
6900            width,
6901            height,
6902            sub_band_type,
6903            total_bitplanes: 0,
6904            block_coding_mode,
6905            ht_target_coding_passes,
6906        });
6907    }
6908
6909    debug_assert!(step_size.exponent <= u16::from(u8::MAX));
6910    let base_total_bitplanes = guard_bits
6911        .saturating_add(step_size.exponent as u8)
6912        .saturating_sub(1);
6913    let total_bitplanes = base_total_bitplanes
6914        .checked_add(roi_shift)
6915        .ok_or("ROI maxshift exceeds supported coded bitplane count")?;
6916    let num_cbs_x = width.div_ceil(cb_width);
6917    let num_cbs_y = height.div_ceil(cb_height);
6918    let mut quantized = coefficients.to_vec();
6919    apply_roi_maxshift_encode_i64(
6920        &mut quantized,
6921        width,
6922        height,
6923        roi_shift,
6924        roi_regions,
6925        roi_scale,
6926    )?;
6927
6928    let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
6929    for cby in 0..num_cbs_y {
6930        for cbx in 0..num_cbs_x {
6931            let x0 = cbx * cb_width;
6932            let y0 = cby * cb_height;
6933            let x1 = (x0 + cb_width).min(width);
6934            let y1 = (y0 + cb_height).min(height);
6935            let cbw = x1 - x0;
6936            let cbh = y1 - y0;
6937
6938            let cb_coeffs = copy_code_block_coefficients_i64(
6939                &quantized,
6940                width as usize,
6941                x0 as usize,
6942                y0 as usize,
6943                cbw as usize,
6944                cbh as usize,
6945            );
6946
6947            code_blocks.push(PreparedEncodeCodeBlock {
6948                coefficients: cb_coeffs,
6949                width: cbw,
6950                height: cbh,
6951            });
6952        }
6953    }
6954
6955    Ok(PreparedEncodeSubband {
6956        code_blocks,
6957        preencoded_ht_code_blocks: None,
6958        num_cbs_x,
6959        num_cbs_y,
6960        code_block_width: cb_width,
6961        code_block_height: cb_height,
6962        width,
6963        height,
6964        sub_band_type,
6965        total_bitplanes,
6966        block_coding_mode,
6967        ht_target_coding_passes,
6968    })
6969}
6970
6971fn prepare_subband_cpu_quantized(
6972    coefficients: &[f32],
6973    width: u32,
6974    height: u32,
6975    step_size: &QuantStepSize,
6976    bit_depth: u8,
6977    guard_bits: u8,
6978    reversible: bool,
6979    block_coding_mode: BlockCodingMode,
6980    cb_width: u32,
6981    cb_height: u32,
6982    sub_band_type: SubBandType,
6983) -> Result<PreparedEncodeSubband, &'static str> {
6984    if width == 0 || height == 0 {
6985        return Ok(PreparedEncodeSubband {
6986            code_blocks: Vec::new(),
6987            preencoded_ht_code_blocks: None,
6988            num_cbs_x: 0,
6989            num_cbs_y: 0,
6990            code_block_width: cb_width,
6991            code_block_height: cb_height,
6992            width,
6993            height,
6994            sub_band_type,
6995            total_bitplanes: 0,
6996            block_coding_mode,
6997            ht_target_coding_passes: 1,
6998        });
6999    }
7000
7001    let range_bits = subband_range_bits(bit_depth, sub_band_type);
7002    debug_assert!(step_size.exponent <= u16::from(u8::MAX));
7003    let total_bitplanes = guard_bits
7004        .saturating_add(step_size.exponent as u8)
7005        .saturating_sub(1);
7006    let num_cbs_x = width.div_ceil(cb_width);
7007    let num_cbs_y = height.div_ceil(cb_height);
7008    let quantized = quantize::quantize_subband(coefficients, step_size, range_bits, reversible);
7009    let mut code_blocks = Vec::with_capacity((num_cbs_x * num_cbs_y) as usize);
7010
7011    for cby in 0..num_cbs_y {
7012        for cbx in 0..num_cbs_x {
7013            let x0 = cbx * cb_width;
7014            let y0 = cby * cb_height;
7015            let x1 = (x0 + cb_width).min(width);
7016            let y1 = (y0 + cb_height).min(height);
7017            let cbw = x1 - x0;
7018            let cbh = y1 - y0;
7019            let cb_coeffs = copy_code_block_coefficients(
7020                &quantized,
7021                width as usize,
7022                x0 as usize,
7023                y0 as usize,
7024                cbw as usize,
7025                cbh as usize,
7026            )
7027            .into_iter()
7028            .map(i64::from)
7029            .collect();
7030
7031            code_blocks.push(PreparedEncodeCodeBlock {
7032                coefficients: cb_coeffs,
7033                width: cbw,
7034                height: cbh,
7035            });
7036        }
7037    }
7038
7039    Ok(PreparedEncodeSubband {
7040        code_blocks,
7041        preencoded_ht_code_blocks: None,
7042        num_cbs_x,
7043        num_cbs_y,
7044        code_block_width: cb_width,
7045        code_block_height: cb_height,
7046        width,
7047        height,
7048        sub_band_type,
7049        total_bitplanes,
7050        block_coding_mode,
7051        ht_target_coding_passes: 1,
7052    })
7053}
7054
7055fn code_block_shapes(
7056    width: u32,
7057    height: u32,
7058    cb_width: u32,
7059    cb_height: u32,
7060) -> Result<Vec<PreparedEncodeCodeBlock>, &'static str> {
7061    let num_cbs_x = width.div_ceil(cb_width);
7062    let num_cbs_y = height.div_ceil(cb_height);
7063    let count = (num_cbs_x as usize)
7064        .checked_mul(num_cbs_y as usize)
7065        .ok_or("code-block count overflow")?;
7066    let mut code_blocks = Vec::with_capacity(count);
7067    for cby in 0..num_cbs_y {
7068        for cbx in 0..num_cbs_x {
7069            let x0 = cbx * cb_width;
7070            let y0 = cby * cb_height;
7071            let x1 = (x0 + cb_width).min(width);
7072            let y1 = (y0 + cb_height).min(height);
7073            code_blocks.push(PreparedEncodeCodeBlock {
7074                coefficients: Vec::new(),
7075                width: x1 - x0,
7076                height: y1 - y0,
7077            });
7078        }
7079    }
7080    Ok(code_blocks)
7081}
7082
7083fn subband_range_bits(bit_depth: u8, sub_band_type: SubBandType) -> u8 {
7084    let log_gain = match sub_band_type {
7085        SubBandType::LowLow => 0,
7086        SubBandType::LowHigh | SubBandType::HighLow => 1,
7087        SubBandType::HighHigh => 2,
7088    };
7089
7090    bit_depth.saturating_add(log_gain)
7091}
7092
7093fn encode_prepared_resolution_packets(
7094    prepared_packets: Vec<PreparedResolutionPacket>,
7095    accelerator: &mut impl J2kEncodeStageAccelerator,
7096) -> Result<Vec<ResolutionPacket>, &'static str> {
7097    let subband_counts: Vec<_> = prepared_packets
7098        .iter()
7099        .map(|packet| packet.subbands.len())
7100        .collect();
7101    let prepared_subbands: Vec<_> = prepared_packets
7102        .into_iter()
7103        .flat_map(|packet| packet.subbands)
7104        .collect();
7105    let mut encoded_subbands =
7106        encode_prepared_subbands(prepared_subbands, accelerator)?.into_iter();
7107
7108    subband_counts
7109        .into_iter()
7110        .map(|subband_count| {
7111            let mut subbands = Vec::with_capacity(subband_count);
7112            for _ in 0..subband_count {
7113                subbands.push(
7114                    encoded_subbands
7115                        .next()
7116                        .ok_or("encoded subband count mismatch")?,
7117                );
7118            }
7119            Ok(ResolutionPacket { subbands })
7120        })
7121        .collect()
7122}
7123
7124fn encode_prepared_resolution_packets_layered(
7125    prepared_packets: Vec<PreparedResolutionPacket>,
7126    num_layers: u8,
7127    progression_order: EncodeProgressionOrder,
7128    quality_layer_byte_targets: &[u64],
7129    accelerator: &mut impl J2kEncodeStageAccelerator,
7130) -> Result<(Vec<ResolutionPacket>, Vec<J2kPacketizationPacketDescriptor>), &'static str> {
7131    let layer_count = usize::from(num_layers);
7132    let mut layered_packets = Vec::with_capacity(prepared_packets.len());
7133    let mut classic_candidates = Vec::new();
7134    let mut classic_locations = Vec::new();
7135    let mut classic_block_index = 0usize;
7136    let mut ht_candidates = Vec::new();
7137    let mut ht_locations = Vec::new();
7138    let mut ht_block_index = 0usize;
7139
7140    for prepared_packet in prepared_packets {
7141        let packet_idx = layered_packets.len();
7142        let mut layered_packet = LayeredPreparedPacket {
7143            component: prepared_packet.component,
7144            resolution: prepared_packet.resolution,
7145            precinct: prepared_packet.precinct,
7146            subbands: Vec::with_capacity(prepared_packet.subbands.len()),
7147        };
7148
7149        for subband in prepared_packet.subbands {
7150            let subband_idx = layered_packet.subbands.len();
7151            let mut layered_subband = LayeredPreparedSubband {
7152                num_cbs_x: subband.num_cbs_x,
7153                num_cbs_y: subband.num_cbs_y,
7154                blocks: Vec::with_capacity(subband.code_blocks.len()),
7155            };
7156
7157            match subband.block_coding_mode {
7158                BlockCodingMode::Classic => {
7159                    for block in subband.code_blocks {
7160                        let block_idx = layered_subband.blocks.len();
7161                        let encoded = bitplane_encode::encode_code_block_segments_with_style_i64(
7162                            &block.coefficients,
7163                            block.width,
7164                            block.height,
7165                            subband.sub_band_type,
7166                            subband.total_bitplanes,
7167                            &classic_multilayer_code_block_style(),
7168                        );
7169                        let segment_layers = if quality_layer_byte_targets.is_empty() {
7170                            classic_unbudgeted_segment_layers(&encoded, num_layers)?
7171                        } else {
7172                            for (segment_idx, segment) in encoded.segments.iter().enumerate() {
7173                                classic_candidates.push(ClassicSegmentAssignmentCandidate {
7174                                    block_index: classic_block_index,
7175                                    segment_index: segment_idx,
7176                                    rate: u64::from(segment.data_length),
7177                                    distortion_delta: segment.distortion_delta,
7178                                });
7179                                classic_locations.push(ClassicSegmentLocation {
7180                                    packet_idx,
7181                                    subband_idx,
7182                                    block_idx,
7183                                    segment_idx,
7184                                });
7185                            }
7186                            vec![layer_count.saturating_sub(1); encoded.segments.len()]
7187                        };
7188                        layered_subband.blocks.push(LayeredPreparedBlock::Classic {
7189                            encoded,
7190                            segment_layers,
7191                        });
7192                        classic_block_index = classic_block_index
7193                            .checked_add(1)
7194                            .ok_or("classic PCRD block index overflow")?;
7195                    }
7196                }
7197                BlockCodingMode::HighThroughput => {
7198                    let encoded_blocks =
7199                        encode_all_ht_code_blocks(core::slice::from_ref(&subband), accelerator)?;
7200                    let block_count = encoded_blocks.len();
7201                    for (block_idx, encoded) in encoded_blocks.into_iter().enumerate() {
7202                        let segment_layers = if quality_layer_byte_targets.is_empty() {
7203                            ht_unbudgeted_segment_layers(
7204                                &encoded,
7205                                num_layers,
7206                                block_idx,
7207                                block_count,
7208                            )?
7209                        } else {
7210                            let segment_count = ht_segment_count(&encoded);
7211                            let mut segment_layers = Vec::with_capacity(segment_count);
7212                            for segment_idx in 0..segment_count {
7213                                ht_candidates.push(HtSegmentAssignmentCandidate {
7214                                    block_index: ht_block_index,
7215                                    segment_index: segment_idx,
7216                                    rate: ht_segment_rate(&encoded, segment_idx)?,
7217                                });
7218                                ht_locations.push(HtSegmentLocation {
7219                                    packet_idx,
7220                                    subband_idx,
7221                                    block_idx: layered_subband.blocks.len(),
7222                                    segment_idx,
7223                                });
7224                                segment_layers.push(layer_count.saturating_sub(1));
7225                            }
7226                            segment_layers
7227                        };
7228                        layered_subband
7229                            .blocks
7230                            .push(LayeredPreparedBlock::HighThroughput {
7231                                encoded,
7232                                segment_layers,
7233                            });
7234                        ht_block_index = ht_block_index
7235                            .checked_add(1)
7236                            .ok_or("HTJ2K segment block index overflow")?;
7237                    }
7238                }
7239            }
7240
7241            layered_packet.subbands.push(layered_subband);
7242        }
7243
7244        layered_packets.push(layered_packet);
7245    }
7246
7247    if !quality_layer_byte_targets.is_empty() {
7248        let assignments = assign_classic_segment_layers_by_slope(
7249            &classic_candidates,
7250            layer_count,
7251            quality_layer_byte_targets,
7252        )?;
7253        for (assignment_idx, layer) in assignments.into_iter().enumerate() {
7254            let location = classic_locations
7255                .get(assignment_idx)
7256                .ok_or("classic PCRD assignment location mismatch")?;
7257            let block = layered_packets
7258                .get_mut(location.packet_idx)
7259                .ok_or("classic PCRD packet index mismatch")?
7260                .subbands
7261                .get_mut(location.subband_idx)
7262                .ok_or("classic PCRD subband index mismatch")?
7263                .blocks
7264                .get_mut(location.block_idx)
7265                .ok_or("classic PCRD block index mismatch")?;
7266            let LayeredPreparedBlock::Classic { segment_layers, .. } = block else {
7267                return Err("classic PCRD assignment referenced HT block");
7268            };
7269            let segment_layer = segment_layers
7270                .get_mut(location.segment_idx)
7271                .ok_or("classic PCRD segment index mismatch")?;
7272            *segment_layer = layer;
7273        }
7274        enforce_classic_segment_layer_monotonicity(&mut layered_packets);
7275    }
7276    if !quality_layer_byte_targets.is_empty() {
7277        let assignments = assign_ht_segment_layers_by_budget(
7278            &ht_candidates,
7279            layer_count,
7280            quality_layer_byte_targets,
7281        )?;
7282        for (assignment_idx, layer) in assignments.into_iter().enumerate() {
7283            let location = ht_locations
7284                .get(assignment_idx)
7285                .ok_or("HTJ2K segment assignment location mismatch")?;
7286            let block = layered_packets
7287                .get_mut(location.packet_idx)
7288                .ok_or("HTJ2K packet index mismatch")?
7289                .subbands
7290                .get_mut(location.subband_idx)
7291                .ok_or("HTJ2K subband index mismatch")?
7292                .blocks
7293                .get_mut(location.block_idx)
7294                .ok_or("HTJ2K block index mismatch")?;
7295            let LayeredPreparedBlock::HighThroughput { segment_layers, .. } = block else {
7296                return Err("HTJ2K segment assignment referenced classic block");
7297            };
7298            let segment_layer = segment_layers
7299                .get_mut(location.segment_idx)
7300                .ok_or("HTJ2K segment index mismatch")?;
7301            *segment_layer = layer;
7302        }
7303        enforce_ht_segment_layer_monotonicity(&mut layered_packets);
7304    }
7305
7306    let mut resolution_packets = Vec::with_capacity(layered_packets.len() * layer_count);
7307    let mut descriptors = Vec::with_capacity(layered_packets.len() * layer_count);
7308    for (state_index, layered_packet) in layered_packets.into_iter().enumerate() {
7309        let mut layer_packets: Vec<_> = (0..layer_count)
7310            .map(|_| ResolutionPacket {
7311                subbands: Vec::with_capacity(layered_packet.subbands.len()),
7312            })
7313            .collect();
7314
7315        for subband in layered_packet.subbands {
7316            let mut layer_subbands: Vec<_> = (0..layer_count)
7317                .map(|_| SubbandPrecinct {
7318                    code_blocks: Vec::with_capacity(subband.blocks.len()),
7319                    num_cbs_x: subband.num_cbs_x,
7320                    num_cbs_y: subband.num_cbs_y,
7321                })
7322                .collect();
7323
7324            for block in subband.blocks {
7325                let contributions = match block {
7326                    LayeredPreparedBlock::Classic {
7327                        encoded,
7328                        segment_layers,
7329                    } => classic_layer_contributions(encoded, num_layers, &segment_layers)?,
7330                    LayeredPreparedBlock::HighThroughput {
7331                        encoded,
7332                        segment_layers,
7333                    } => ht_layer_contributions(encoded, num_layers, &segment_layers)?,
7334                };
7335                for (layer_idx, contribution) in contributions.into_iter().enumerate() {
7336                    layer_subbands[layer_idx].code_blocks.push(contribution);
7337                }
7338            }
7339
7340            for (layer_packet, layer_subband) in layer_packets.iter_mut().zip(layer_subbands) {
7341                layer_packet.subbands.push(layer_subband);
7342            }
7343        }
7344
7345        let state_index =
7346            u32::try_from(state_index).map_err(|_| "packet descriptor state index exceeds u32")?;
7347        for (layer_idx, layer_packet) in layer_packets.into_iter().enumerate() {
7348            let packet_index = u32::try_from(resolution_packets.len())
7349                .map_err(|_| "packet descriptor index exceeds u32")?;
7350            resolution_packets.push(layer_packet);
7351            descriptors.push(J2kPacketizationPacketDescriptor {
7352                packet_index,
7353                state_index,
7354                layer: u8::try_from(layer_idx).map_err(|_| "quality layer index exceeds u8")?,
7355                resolution: layered_packet.resolution,
7356                component: layered_packet.component,
7357                precinct: layered_packet.precinct,
7358            });
7359        }
7360    }
7361
7362    sort_packet_descriptors_for_progression(&mut descriptors, progression_order);
7363
7364    Ok((resolution_packets, descriptors))
7365}
7366
7367fn sort_packet_descriptors_for_progression(
7368    descriptors: &mut [J2kPacketizationPacketDescriptor],
7369    progression_order: EncodeProgressionOrder,
7370) {
7371    match progression_order {
7372        EncodeProgressionOrder::Lrcp => descriptors.sort_by_key(|descriptor| {
7373            (
7374                descriptor.layer,
7375                descriptor.resolution,
7376                descriptor.component,
7377                descriptor.precinct,
7378            )
7379        }),
7380        EncodeProgressionOrder::Rlcp => descriptors.sort_by_key(|descriptor| {
7381            (
7382                descriptor.resolution,
7383                descriptor.layer,
7384                descriptor.component,
7385                descriptor.precinct,
7386            )
7387        }),
7388        EncodeProgressionOrder::Rpcl => descriptors.sort_by_key(|descriptor| {
7389            (
7390                descriptor.resolution,
7391                descriptor.precinct,
7392                descriptor.component,
7393                descriptor.layer,
7394            )
7395        }),
7396        EncodeProgressionOrder::Pcrl => descriptors.sort_by_key(|descriptor| {
7397            (
7398                descriptor.precinct,
7399                descriptor.component,
7400                descriptor.resolution,
7401                descriptor.layer,
7402            )
7403        }),
7404        EncodeProgressionOrder::Cprl => descriptors.sort_by_key(|descriptor| {
7405            (
7406                descriptor.component,
7407                descriptor.precinct,
7408                descriptor.resolution,
7409                descriptor.layer,
7410            )
7411        }),
7412    }
7413}
7414
7415fn classic_multilayer_code_block_style() -> CodeBlockStyle {
7416    CodeBlockStyle {
7417        termination_on_each_pass: true,
7418        ..CodeBlockStyle::default()
7419    }
7420}
7421
7422struct LayeredPreparedPacket {
7423    component: u16,
7424    resolution: u32,
7425    precinct: u64,
7426    subbands: Vec<LayeredPreparedSubband>,
7427}
7428
7429struct LayeredPreparedSubband {
7430    num_cbs_x: u32,
7431    num_cbs_y: u32,
7432    blocks: Vec<LayeredPreparedBlock>,
7433}
7434
7435enum LayeredPreparedBlock {
7436    Classic {
7437        encoded: bitplane_encode::EncodedCodeBlockWithSegments,
7438        segment_layers: Vec<usize>,
7439    },
7440    HighThroughput {
7441        encoded: bitplane_encode::EncodedCodeBlock,
7442        segment_layers: Vec<usize>,
7443    },
7444}
7445
7446#[derive(Debug, Clone, Copy, PartialEq)]
7447struct ClassicSegmentAssignmentCandidate {
7448    block_index: usize,
7449    segment_index: usize,
7450    rate: u64,
7451    distortion_delta: f64,
7452}
7453
7454#[derive(Debug, Clone, Copy)]
7455struct ClassicSegmentLocation {
7456    packet_idx: usize,
7457    subband_idx: usize,
7458    block_idx: usize,
7459    segment_idx: usize,
7460}
7461
7462#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7463struct HtSegmentAssignmentCandidate {
7464    block_index: usize,
7465    segment_index: usize,
7466    rate: u64,
7467}
7468
7469#[derive(Debug, Clone, Copy)]
7470struct HtSegmentLocation {
7471    packet_idx: usize,
7472    subband_idx: usize,
7473    block_idx: usize,
7474    segment_idx: usize,
7475}
7476
7477struct ClassicLayerBudgetAllocator {
7478    cumulative_targets: Vec<u64>,
7479    cumulative_used: Vec<u64>,
7480}
7481
7482impl ClassicLayerBudgetAllocator {
7483    fn new(cumulative_targets: &[u64], layer_count: usize) -> Result<Self, &'static str> {
7484        if cumulative_targets.is_empty() {
7485            return Ok(Self {
7486                cumulative_targets: Vec::new(),
7487                cumulative_used: Vec::new(),
7488            });
7489        }
7490        if cumulative_targets.len() != layer_count {
7491            return Err("quality layer byte target count must match quality layer count");
7492        }
7493        if cumulative_targets.windows(2).any(|pair| pair[0] > pair[1]) {
7494            return Err("quality layer byte targets must be cumulative and monotonic");
7495        }
7496        Ok(Self {
7497            cumulative_targets: cumulative_targets
7498                .iter()
7499                .map(|&target| target.saturating_add(classic_rate_target_tolerance(target)))
7500                .collect(),
7501            cumulative_used: vec![0; layer_count],
7502        })
7503    }
7504
7505    fn is_budgeted(&self) -> bool {
7506        !self.cumulative_targets.is_empty()
7507    }
7508
7509    fn assign_segment(
7510        &mut self,
7511        min_layer: usize,
7512        data_length: u64,
7513    ) -> Result<usize, &'static str> {
7514        if !self.is_budgeted() {
7515            return Ok(min_layer);
7516        }
7517
7518        let rate = data_length;
7519        let last_layer = self
7520            .cumulative_targets
7521            .len()
7522            .checked_sub(1)
7523            .ok_or("quality layer target count underflow")?;
7524        for layer_idx in min_layer..last_layer {
7525            if self.layer_can_accept(layer_idx, rate)? {
7526                self.record_segment(layer_idx, rate)?;
7527                return Ok(layer_idx);
7528            }
7529        }
7530        self.record_segment(last_layer, rate)?;
7531        Ok(last_layer)
7532    }
7533
7534    fn layer_can_accept(&self, layer_idx: usize, rate: u64) -> Result<bool, &'static str> {
7535        for cumulative_idx in layer_idx..self.cumulative_targets.len() {
7536            let used = self.cumulative_used[cumulative_idx]
7537                .checked_add(rate)
7538                .ok_or("quality layer byte budget overflow")?;
7539            if used > self.cumulative_targets[cumulative_idx] {
7540                return Ok(false);
7541            }
7542        }
7543        Ok(true)
7544    }
7545
7546    fn record_segment(&mut self, layer_idx: usize, rate: u64) -> Result<(), &'static str> {
7547        for used in &mut self.cumulative_used[layer_idx..] {
7548            *used = used
7549                .checked_add(rate)
7550                .ok_or("quality layer byte budget overflow")?;
7551        }
7552        Ok(())
7553    }
7554}
7555
7556fn classic_rate_target_tolerance(target: u64) -> u64 {
7557    (target / 100).max(512)
7558}
7559
7560fn assign_classic_segment_layers_by_slope(
7561    candidates: &[ClassicSegmentAssignmentCandidate],
7562    layer_count: usize,
7563    cumulative_targets: &[u64],
7564) -> Result<Vec<usize>, &'static str> {
7565    let mut allocator = ClassicLayerBudgetAllocator::new(cumulative_targets, layer_count)?;
7566    if candidates.is_empty() {
7567        return Ok(Vec::new());
7568    }
7569
7570    let block_count = candidates
7571        .iter()
7572        .map(|candidate| candidate.block_index)
7573        .max()
7574        .and_then(|max| max.checked_add(1))
7575        .ok_or("classic PCRD block count overflow")?;
7576    let mut block_candidates = vec![Vec::new(); block_count];
7577    for (candidate_idx, candidate) in candidates.iter().enumerate() {
7578        block_candidates
7579            .get_mut(candidate.block_index)
7580            .ok_or("classic PCRD block index mismatch")?
7581            .push(candidate_idx);
7582    }
7583    for block in &mut block_candidates {
7584        block.sort_by_key(|&idx| candidates[idx].segment_index);
7585    }
7586
7587    let mut block_min_layers = vec![0usize; block_count];
7588    let mut assignments = vec![layer_count.saturating_sub(1); candidates.len()];
7589    let mut next_block_segment = vec![0usize; block_count];
7590    let mut remaining = candidates.len();
7591    while remaining > 0 {
7592        let candidate_idx = block_candidates
7593            .iter()
7594            .enumerate()
7595            .filter_map(|(block_idx, block)| block.get(next_block_segment[block_idx]).copied())
7596            .min_by(|&left, &right| compare_classic_segment_candidates(candidates, left, right))
7597            .ok_or("classic PCRD candidate queue underflow")?;
7598        let candidate = candidates[candidate_idx];
7599        let min_layer = *block_min_layers
7600            .get(candidate.block_index)
7601            .ok_or("classic PCRD block index mismatch")?;
7602        let layer = allocator.assign_segment(min_layer, candidate.rate)?;
7603        assignments[candidate_idx] = layer;
7604        if let Some(block_layer) = block_min_layers.get_mut(candidate.block_index) {
7605            *block_layer = layer;
7606        }
7607        if let Some(next) = next_block_segment.get_mut(candidate.block_index) {
7608            *next = next
7609                .checked_add(1)
7610                .ok_or("classic PCRD segment index overflow")?;
7611        }
7612        remaining -= 1;
7613    }
7614
7615    enforce_classic_assignment_monotonicity(candidates, &mut assignments);
7616    Ok(assignments)
7617}
7618
7619fn compare_classic_segment_candidates(
7620    candidates: &[ClassicSegmentAssignmentCandidate],
7621    left: usize,
7622    right: usize,
7623) -> Ordering {
7624    let left_candidate = candidates[left];
7625    let right_candidate = candidates[right];
7626    pcrd_slope(right_candidate)
7627        .partial_cmp(&pcrd_slope(left_candidate))
7628        .unwrap_or(Ordering::Equal)
7629        .then_with(|| left_candidate.block_index.cmp(&right_candidate.block_index))
7630        .then_with(|| {
7631            left_candidate
7632                .segment_index
7633                .cmp(&right_candidate.segment_index)
7634        })
7635}
7636
7637fn pcrd_slope(candidate: ClassicSegmentAssignmentCandidate) -> f64 {
7638    if candidate.rate == 0 {
7639        return f64::INFINITY;
7640    }
7641    candidate.distortion_delta / candidate.rate as f64
7642}
7643
7644fn enforce_classic_assignment_monotonicity(
7645    candidates: &[ClassicSegmentAssignmentCandidate],
7646    assignments: &mut [usize],
7647) {
7648    let mut order: Vec<_> = (0..candidates.len()).collect();
7649    order.sort_by_key(|&idx| (candidates[idx].block_index, candidates[idx].segment_index));
7650    let mut current_block = None;
7651    let mut min_layer = 0usize;
7652    for idx in order {
7653        if current_block != Some(candidates[idx].block_index) {
7654            current_block = Some(candidates[idx].block_index);
7655            min_layer = 0;
7656        }
7657        if assignments[idx] < min_layer {
7658            assignments[idx] = min_layer;
7659        }
7660        min_layer = assignments[idx];
7661    }
7662}
7663
7664fn enforce_classic_segment_layer_monotonicity(layered_packets: &mut [LayeredPreparedPacket]) {
7665    for packet in layered_packets {
7666        for subband in &mut packet.subbands {
7667            for block in &mut subband.blocks {
7668                if let LayeredPreparedBlock::Classic { segment_layers, .. } = block {
7669                    let mut min_layer = 0usize;
7670                    for layer in segment_layers {
7671                        if *layer < min_layer {
7672                            *layer = min_layer;
7673                        }
7674                        min_layer = *layer;
7675                    }
7676                }
7677            }
7678        }
7679    }
7680}
7681
7682fn enforce_ht_segment_layer_monotonicity(layered_packets: &mut [LayeredPreparedPacket]) {
7683    for packet in layered_packets {
7684        for subband in &mut packet.subbands {
7685            for block in &mut subband.blocks {
7686                if let LayeredPreparedBlock::HighThroughput { segment_layers, .. } = block {
7687                    let mut min_layer = 0usize;
7688                    for layer in segment_layers {
7689                        if *layer < min_layer {
7690                            *layer = min_layer;
7691                        }
7692                        min_layer = *layer;
7693                    }
7694                }
7695            }
7696        }
7697    }
7698}
7699
7700fn assign_ht_segment_layers_by_budget(
7701    candidates: &[HtSegmentAssignmentCandidate],
7702    layer_count: usize,
7703    cumulative_targets: &[u64],
7704) -> Result<Vec<usize>, &'static str> {
7705    let mut allocator = ClassicLayerBudgetAllocator::new(cumulative_targets, layer_count)?;
7706    let mut assignments = vec![layer_count.saturating_sub(1); candidates.len()];
7707    let mut candidate_order: Vec<_> = (0..candidates.len()).collect();
7708    candidate_order
7709        .sort_by_key(|&idx| (candidates[idx].block_index, candidates[idx].segment_index));
7710    let mut block_min_layers = vec![
7711        0usize;
7712        candidates
7713            .iter()
7714            .map(|c| c.block_index)
7715            .max()
7716            .map_or(0, |idx| idx + 1)
7717    ];
7718
7719    for candidate_idx in candidate_order {
7720        let candidate = candidates
7721            .get(candidate_idx)
7722            .ok_or("HTJ2K segment candidate index mismatch")?;
7723        let min_layer = *block_min_layers
7724            .get(candidate.block_index)
7725            .ok_or("HTJ2K segment candidate block index mismatch")?;
7726        let layer = allocator.assign_segment(min_layer, candidate.rate)?;
7727        assignments[candidate_idx] = layer;
7728        if let Some(block_layer) = block_min_layers.get_mut(candidate.block_index) {
7729            *block_layer = layer;
7730        }
7731    }
7732
7733    Ok(assignments)
7734}
7735
7736fn ht_segment_count(encoded: &bitplane_encode::EncodedCodeBlock) -> usize {
7737    match encoded.num_coding_passes {
7738        0 => 0,
7739        1 => 1,
7740        _ => 2,
7741    }
7742}
7743
7744fn ht_segment_rate(
7745    encoded: &bitplane_encode::EncodedCodeBlock,
7746    segment_idx: usize,
7747) -> Result<u64, &'static str> {
7748    match segment_idx {
7749        0 if encoded.num_coding_passes > 0 => Ok(u64::from(encoded.ht_cleanup_length)),
7750        1 if encoded.num_coding_passes > 1 => Ok(u64::from(encoded.ht_refinement_length)),
7751        _ => Err("HTJ2K segment index out of range"),
7752    }
7753}
7754
7755fn ht_unbudgeted_segment_layers(
7756    encoded: &bitplane_encode::EncodedCodeBlock,
7757    num_layers: u8,
7758    block_idx: usize,
7759    block_count: usize,
7760) -> Result<Vec<usize>, &'static str> {
7761    let segment_count = ht_segment_count(encoded);
7762    if segment_count == 0 {
7763        return Ok(Vec::new());
7764    }
7765    let layer_count = usize::from(num_layers);
7766    if layer_count == 0 {
7767        return Err("HTJ2K layer allocation requires non-empty inputs");
7768    }
7769    if encoded.num_coding_passes == 1 {
7770        return Ok(vec![ht_target_layer(block_idx, block_count, layer_count)?]);
7771    }
7772
7773    let mut segment_layers = Vec::with_capacity(segment_count);
7774    let mut min_layer = 0usize;
7775    for (_, end_pass) in [(0, 1), (1, encoded.num_coding_passes)] {
7776        let mut assigned = None;
7777        for layer_idx in min_layer..layer_count {
7778            let cumulative_passes = if layer_idx + 1 == layer_count {
7779                encoded.num_coding_passes
7780            } else {
7781                layer_pass_count(encoded.num_coding_passes, layer_idx + 1, num_layers)?
7782            };
7783            if end_pass <= cumulative_passes {
7784                assigned = Some(layer_idx);
7785                break;
7786            }
7787        }
7788        let assigned =
7789            assigned.ok_or("HTJ2K quality layer split must align to segment boundaries")?;
7790        segment_layers.push(assigned);
7791        min_layer = assigned;
7792    }
7793    Ok(segment_layers)
7794}
7795
7796fn classic_unbudgeted_segment_layers(
7797    encoded: &bitplane_encode::EncodedCodeBlockWithSegments,
7798    num_layers: u8,
7799) -> Result<Vec<usize>, &'static str> {
7800    let mut segment_layers = Vec::with_capacity(encoded.segments.len());
7801    for segment in &encoded.segments {
7802        let mut assigned = None;
7803        for layer_idx in 0..usize::from(num_layers) {
7804            let previous_pass =
7805                previous_layer_pass_count(encoded.num_coding_passes, layer_idx, num_layers)?;
7806            let cumulative_passes = if layer_idx + 1 == usize::from(num_layers) {
7807                encoded.num_coding_passes
7808            } else {
7809                layer_pass_count(encoded.num_coding_passes, layer_idx + 1, num_layers)?
7810            };
7811            if segment.start_coding_pass >= previous_pass
7812                && segment.end_coding_pass <= cumulative_passes
7813            {
7814                assigned = Some(layer_idx);
7815                break;
7816            }
7817        }
7818        segment_layers.push(
7819            assigned.ok_or("classic quality layer split must align to terminated coding passes")?,
7820        );
7821    }
7822    Ok(segment_layers)
7823}
7824
7825fn classic_layer_contributions(
7826    encoded: bitplane_encode::EncodedCodeBlockWithSegments,
7827    num_layers: u8,
7828    segment_layers: &[usize],
7829) -> Result<Vec<CodeBlockPacketData>, &'static str> {
7830    let layer_count = usize::from(num_layers);
7831    if segment_layers.len() != encoded.segments.len() {
7832        return Err("classic PCRD segment assignment count mismatch");
7833    }
7834    if segment_layers.iter().any(|&layer| layer >= layer_count) {
7835        return Err("classic PCRD segment layer exceeds layer count");
7836    }
7837    let mut contributions = Vec::with_capacity(layer_count);
7838
7839    for layer_idx in 0..layer_count {
7840        let mut data = Vec::new();
7841        let mut classic_segment_lengths = Vec::new();
7842        let mut contribution_passes = 0u8;
7843
7844        for (segment_idx, segment) in encoded.segments.iter().enumerate() {
7845            if segment_layers[segment_idx] != layer_idx {
7846                continue;
7847            }
7848            let start = usize::try_from(segment.data_offset)
7849                .map_err(|_| "classic code-block segment offset overflow")?;
7850            let len = usize::try_from(segment.data_length)
7851                .map_err(|_| "classic code-block segment length overflow")?;
7852            let end = start
7853                .checked_add(len)
7854                .ok_or("classic code-block segment range overflow")?;
7855            data.extend_from_slice(
7856                encoded
7857                    .data
7858                    .get(start..end)
7859                    .ok_or("classic code-block segment range invalid")?,
7860            );
7861            classic_segment_lengths.push(segment.data_length);
7862            contribution_passes = contribution_passes
7863                .checked_add(segment.end_coding_pass - segment.start_coding_pass)
7864                .ok_or("classic code-block contribution pass count overflow")?;
7865        }
7866
7867        contributions.push(CodeBlockPacketData {
7868            data,
7869            ht_cleanup_length: 0,
7870            ht_refinement_length: 0,
7871            num_coding_passes: contribution_passes,
7872            classic_segment_lengths,
7873            num_zero_bitplanes: encoded.num_zero_bitplanes,
7874            previously_included: false,
7875            l_block: 3,
7876            block_coding_mode: BlockCodingMode::Classic,
7877        });
7878    }
7879
7880    Ok(contributions)
7881}
7882
7883fn layer_pass_count(
7884    num_coding_passes: u8,
7885    layer_count: usize,
7886    num_layers: u8,
7887) -> Result<u8, &'static str> {
7888    let numerator = u32::from(num_coding_passes)
7889        .checked_mul(u32::try_from(layer_count).map_err(|_| "layer index overflow")?)
7890        .ok_or("quality layer pass allocation overflow")?;
7891    numerator
7892        .div_ceil(u32::from(num_layers))
7893        .try_into()
7894        .map_err(|_| "quality layer pass allocation overflow")
7895}
7896
7897fn previous_layer_pass_count(
7898    num_coding_passes: u8,
7899    layer_idx: usize,
7900    num_layers: u8,
7901) -> Result<u8, &'static str> {
7902    if layer_idx == 0 {
7903        Ok(0)
7904    } else {
7905        layer_pass_count(num_coding_passes, layer_idx, num_layers)
7906    }
7907}
7908
7909fn ht_target_layer(
7910    block_idx: usize,
7911    block_count: usize,
7912    layer_count: usize,
7913) -> Result<usize, &'static str> {
7914    if block_count == 0 || layer_count == 0 {
7915        return Err("HTJ2K layer allocation requires non-empty inputs");
7916    }
7917    Ok(block_idx
7918        .checked_mul(layer_count)
7919        .ok_or("HTJ2K layer allocation overflow")?
7920        / block_count)
7921}
7922
7923fn ht_layer_contributions(
7924    encoded: bitplane_encode::EncodedCodeBlock,
7925    num_layers: u8,
7926    segment_layers: &[usize],
7927) -> Result<Vec<CodeBlockPacketData>, &'static str> {
7928    let layer_count = usize::from(num_layers);
7929    if segment_layers.len() != ht_segment_count(&encoded) {
7930        return Err("HTJ2K segment assignment count mismatch");
7931    }
7932    if segment_layers.iter().any(|&layer| layer >= layer_count) {
7933        return Err("HTJ2K segment layer exceeds layer count");
7934    }
7935    if segment_layers
7936        .windows(2)
7937        .any(|layers| layers[1] < layers[0])
7938    {
7939        return Err("HTJ2K segment layers must be monotonic");
7940    }
7941
7942    let cleanup_len = usize::try_from(encoded.ht_cleanup_length)
7943        .map_err(|_| "HTJ2K cleanup segment length overflow")?;
7944    let refinement_len = usize::try_from(encoded.ht_refinement_length)
7945        .map_err(|_| "HTJ2K refinement segment length overflow")?;
7946    let refinement_start = cleanup_len;
7947    let refinement_end = refinement_start
7948        .checked_add(refinement_len)
7949        .ok_or("HTJ2K refinement segment range overflow")?;
7950    if encoded.num_coding_passes > 0 && cleanup_len == 0 {
7951        return Err("HTJ2K cleanup segment is missing");
7952    }
7953    if encoded.num_coding_passes > 1 && refinement_len == 0 {
7954        return Err("HTJ2K refinement segment is missing");
7955    }
7956    if refinement_end > encoded.data.len() {
7957        return Err("HTJ2K segment range invalid");
7958    }
7959
7960    let mut contributions = Vec::with_capacity(layer_count);
7961    for layer_idx in 0..layer_count {
7962        let mut data = Vec::new();
7963        let mut ht_cleanup_length = 0u32;
7964        let mut ht_refinement_length = 0u32;
7965        let mut num_coding_passes = 0u8;
7966
7967        if segment_layers.first() == Some(&layer_idx) {
7968            data.extend_from_slice(
7969                encoded
7970                    .data
7971                    .get(..cleanup_len)
7972                    .ok_or("HTJ2K cleanup segment range invalid")?,
7973            );
7974            ht_cleanup_length = encoded.ht_cleanup_length;
7975            num_coding_passes = num_coding_passes
7976                .checked_add(1)
7977                .ok_or("HTJ2K packet contribution pass count overflow")?;
7978        }
7979
7980        if encoded.num_coding_passes > 1 && segment_layers.get(1) == Some(&layer_idx) {
7981            data.extend_from_slice(
7982                encoded
7983                    .data
7984                    .get(refinement_start..refinement_end)
7985                    .ok_or("HTJ2K refinement segment range invalid")?,
7986            );
7987            ht_refinement_length = encoded.ht_refinement_length;
7988            num_coding_passes = num_coding_passes
7989                .checked_add(encoded.num_coding_passes - 1)
7990                .ok_or("HTJ2K packet contribution pass count overflow")?;
7991        }
7992
7993        contributions.push(CodeBlockPacketData {
7994            data,
7995            ht_cleanup_length,
7996            ht_refinement_length,
7997            num_coding_passes,
7998            classic_segment_lengths: Vec::new(),
7999            num_zero_bitplanes: encoded.num_zero_bitplanes,
8000            previously_included: false,
8001            l_block: 3,
8002            block_coding_mode: BlockCodingMode::HighThroughput,
8003        });
8004    }
8005
8006    Ok(contributions)
8007}
8008
8009fn encode_prepared_subbands(
8010    prepared_subbands: Vec<PreparedEncodeSubband>,
8011    accelerator: &mut impl J2kEncodeStageAccelerator,
8012) -> Result<Vec<SubbandPrecinct>, &'static str> {
8013    let block_coding_mode = prepared_subbands
8014        .iter()
8015        .find(|subband| !subband.code_blocks.is_empty())
8016        .map(|subband| subband.block_coding_mode);
8017    let encoded_blocks = match block_coding_mode {
8018        Some(BlockCodingMode::HighThroughput) => {
8019            encode_all_ht_code_blocks(&prepared_subbands, accelerator)?
8020        }
8021        Some(BlockCodingMode::Classic) => {
8022            encode_all_tier1_code_blocks(&prepared_subbands, accelerator)?
8023        }
8024        None => Vec::new(),
8025    };
8026
8027    let mut encoded_iter = encoded_blocks.into_iter();
8028    let mut precincts = Vec::with_capacity(prepared_subbands.len());
8029    for subband in prepared_subbands {
8030        let mut code_blocks = Vec::with_capacity(subband.code_blocks.len());
8031        for _ in 0..subband.code_blocks.len() {
8032            let encoded = encoded_iter
8033                .next()
8034                .ok_or("encoded code-block count mismatch")?;
8035            code_blocks.push(CodeBlockPacketData {
8036                data: encoded.data,
8037                ht_cleanup_length: if subband.block_coding_mode == BlockCodingMode::HighThroughput {
8038                    encoded.ht_cleanup_length
8039                } else {
8040                    0
8041                },
8042                ht_refinement_length: if subband.block_coding_mode
8043                    == BlockCodingMode::HighThroughput
8044                {
8045                    encoded.ht_refinement_length
8046                } else {
8047                    0
8048                },
8049                num_coding_passes: encoded.num_coding_passes,
8050                classic_segment_lengths: Vec::new(),
8051                num_zero_bitplanes: encoded.num_zero_bitplanes,
8052                previously_included: false,
8053                l_block: 3,
8054                block_coding_mode: subband.block_coding_mode,
8055            });
8056        }
8057        precincts.push(SubbandPrecinct {
8058            code_blocks,
8059            num_cbs_x: subband.num_cbs_x,
8060            num_cbs_y: subband.num_cbs_y,
8061        });
8062    }
8063    if encoded_iter.next().is_some() {
8064        return Err("encoded code-block count mismatch");
8065    }
8066
8067    Ok(precincts)
8068}
8069
8070fn encode_all_ht_code_blocks(
8071    prepared_subbands: &[PreparedEncodeSubband],
8072    accelerator: &mut impl J2kEncodeStageAccelerator,
8073) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8074    if prepared_subbands.iter().all(|subband| {
8075        subband.code_blocks.is_empty() || subband.preencoded_ht_code_blocks.is_some()
8076    }) {
8077        let total_blocks = prepared_subbands
8078            .iter()
8079            .map(|subband| subband.code_blocks.len())
8080            .sum();
8081        let mut encoded = Vec::with_capacity(total_blocks);
8082        for subband in prepared_subbands {
8083            if let Some(blocks) = &subband.preencoded_ht_code_blocks {
8084                if blocks.len() != subband.code_blocks.len() {
8085                    return Err("preencoded HT subband code-block count mismatch");
8086                }
8087                encoded.extend(
8088                    blocks
8089                        .iter()
8090                        .cloned()
8091                        .map(ht_encoded_code_block_from_accelerator),
8092                );
8093            }
8094        }
8095        return Ok(encoded);
8096    }
8097    if prepared_subbands
8098        .iter()
8099        .any(|subband| subband.preencoded_ht_code_blocks.is_some())
8100    {
8101        return Err("mixed preencoded and quantized HT subbands are unsupported");
8102    }
8103
8104    let job_coefficients = prepared_subbands
8105        .iter()
8106        .flat_map(|subband| subband.code_blocks.iter())
8107        .map(|block| downcast_i64_coefficients_to_i32(&block.coefficients))
8108        .collect::<Result<Vec<_>, _>>()?;
8109    let mut jobs = Vec::with_capacity(job_coefficients.len());
8110    let mut coefficient_idx = 0usize;
8111    for subband in prepared_subbands {
8112        for block in &subband.code_blocks {
8113            let coefficients = job_coefficients
8114                .get(coefficient_idx)
8115                .ok_or("HT coefficient storage count mismatch")?;
8116            jobs.push(crate::J2kHtCodeBlockEncodeJob {
8117                coefficients,
8118                width: block.width,
8119                height: block.height,
8120                total_bitplanes: subband.total_bitplanes,
8121                target_coding_passes: subband.ht_target_coding_passes,
8122            });
8123            coefficient_idx = coefficient_idx
8124                .checked_add(1)
8125                .ok_or("HT coefficient storage count overflow")?;
8126        }
8127    }
8128
8129    if let Some(encoded) = accelerator.encode_ht_code_blocks(&jobs)? {
8130        if encoded.len() != jobs.len() {
8131            return Err("accelerated HT code-block batch length mismatch");
8132        }
8133        return Ok(encoded
8134            .into_iter()
8135            .map(ht_encoded_code_block_from_accelerator)
8136            .collect());
8137    }
8138
8139    if accelerator.prefer_parallel_cpu_code_block_fallback() {
8140        if jobs.len() < HT_CPU_PARALLEL_FALLBACK_MIN_JOBS {
8141            return encode_all_ht_code_blocks_serial_cpu(&jobs);
8142        }
8143        return encode_all_ht_code_blocks_parallel(&jobs);
8144    }
8145
8146    jobs.iter()
8147        .map(|job| {
8148            encode_ht_code_block(
8149                job.coefficients,
8150                job.width,
8151                job.height,
8152                job.total_bitplanes,
8153                job.target_coding_passes,
8154                accelerator,
8155            )
8156        })
8157        .collect()
8158}
8159
8160fn encode_all_tier1_code_blocks(
8161    prepared_subbands: &[PreparedEncodeSubband],
8162    accelerator: &mut impl J2kEncodeStageAccelerator,
8163) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8164    let style = default_public_code_block_style();
8165    let can_use_i32_jobs = prepared_subbands
8166        .iter()
8167        .flat_map(|subband| &subband.code_blocks)
8168        .all(|block| coefficients_fit_i32(&block.coefficients));
8169    if !can_use_i32_jobs {
8170        let mut encoded = Vec::new();
8171        for subband in prepared_subbands {
8172            encoded.reserve(subband.code_blocks.len());
8173            for block in &subband.code_blocks {
8174                encoded.push(bitplane_encode::encode_code_block_i64(
8175                    &block.coefficients,
8176                    block.width,
8177                    block.height,
8178                    subband.sub_band_type,
8179                    subband.total_bitplanes,
8180                ));
8181            }
8182        }
8183        return Ok(encoded);
8184    }
8185
8186    let job_coefficients = prepared_subbands
8187        .iter()
8188        .flat_map(|subband| subband.code_blocks.iter())
8189        .map(|block| downcast_i64_coefficients_to_i32(&block.coefficients))
8190        .collect::<Result<Vec<_>, _>>()?;
8191    let mut jobs = Vec::with_capacity(job_coefficients.len());
8192    let mut coefficient_idx = 0usize;
8193    for subband in prepared_subbands {
8194        let public_sub_band_type = public_sub_band_type(subband.sub_band_type);
8195        for block in &subband.code_blocks {
8196            let coefficients = job_coefficients
8197                .get(coefficient_idx)
8198                .ok_or("classic coefficient storage count mismatch")?;
8199            jobs.push(J2kTier1CodeBlockEncodeJob {
8200                coefficients,
8201                width: block.width,
8202                height: block.height,
8203                sub_band_type: public_sub_band_type,
8204                total_bitplanes: subband.total_bitplanes,
8205                style,
8206            });
8207            coefficient_idx = coefficient_idx
8208                .checked_add(1)
8209                .ok_or("classic coefficient storage count overflow")?;
8210        }
8211    }
8212
8213    if let Some(encoded) = accelerator.encode_tier1_code_blocks(&jobs)? {
8214        if encoded.len() != jobs.len() {
8215            return Err("accelerated classic code-block batch length mismatch");
8216        }
8217        return Ok(encoded
8218            .into_iter()
8219            .map(encoded_code_block_from_accelerator)
8220            .collect());
8221    }
8222
8223    if accelerator.prefer_parallel_cpu_code_block_fallback() {
8224        return encode_all_tier1_code_blocks_parallel(&jobs);
8225    }
8226
8227    let mut encoded = Vec::with_capacity(jobs.len());
8228    for job in &jobs {
8229        encoded.push(encode_tier1_code_block(
8230            job.coefficients,
8231            job.width,
8232            job.height,
8233            internal_sub_band_type(job.sub_band_type),
8234            job.total_bitplanes,
8235            accelerator,
8236        )?);
8237    }
8238    Ok(encoded)
8239}
8240
8241fn encode_all_ht_code_blocks_serial_cpu(
8242    jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
8243) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8244    if jobs
8245        .iter()
8246        .any(|job| !(1..=3).contains(&job.target_coding_passes))
8247    {
8248        return Err("CPU HTJ2K code-block fallback supports at most three HT coding passes");
8249    }
8250    jobs.iter()
8251        .map(|job| {
8252            ht_block_encode::encode_code_block_with_passes(
8253                job.coefficients,
8254                job.width,
8255                job.height,
8256                job.total_bitplanes,
8257                job.target_coding_passes,
8258            )
8259        })
8260        .collect()
8261}
8262
8263#[cfg(feature = "parallel")]
8264fn encode_all_ht_code_blocks_parallel(
8265    jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
8266) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8267    if jobs
8268        .iter()
8269        .any(|job| !(1..=3).contains(&job.target_coding_passes))
8270    {
8271        return Err("CPU HTJ2K code-block fallback supports at most three HT coding passes");
8272    }
8273    jobs.par_iter()
8274        .map(|job| {
8275            ht_block_encode::encode_code_block_with_passes(
8276                job.coefficients,
8277                job.width,
8278                job.height,
8279                job.total_bitplanes,
8280                job.target_coding_passes,
8281            )
8282        })
8283        .collect()
8284}
8285
8286#[cfg(not(feature = "parallel"))]
8287fn encode_all_ht_code_blocks_parallel(
8288    jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
8289) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8290    if jobs
8291        .iter()
8292        .any(|job| !(1..=3).contains(&job.target_coding_passes))
8293    {
8294        return Err("CPU HTJ2K code-block fallback supports at most three HT coding passes");
8295    }
8296    jobs.iter()
8297        .map(|job| {
8298            ht_block_encode::encode_code_block_with_passes(
8299                job.coefficients,
8300                job.width,
8301                job.height,
8302                job.total_bitplanes,
8303                job.target_coding_passes,
8304            )
8305        })
8306        .collect()
8307}
8308
8309#[cfg(feature = "parallel")]
8310fn encode_all_tier1_code_blocks_parallel(
8311    jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
8312) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8313    jobs.par_iter()
8314        .map(|job| {
8315            Ok(bitplane_encode::encode_code_block(
8316                job.coefficients,
8317                job.width,
8318                job.height,
8319                internal_sub_band_type(job.sub_band_type),
8320                job.total_bitplanes,
8321            ))
8322        })
8323        .collect()
8324}
8325
8326#[cfg(not(feature = "parallel"))]
8327fn encode_all_tier1_code_blocks_parallel(
8328    jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
8329) -> Result<Vec<bitplane_encode::EncodedCodeBlock>, &'static str> {
8330    jobs.iter()
8331        .map(|job| {
8332            Ok(bitplane_encode::encode_code_block(
8333                job.coefficients,
8334                job.width,
8335                job.height,
8336                internal_sub_band_type(job.sub_band_type),
8337                job.total_bitplanes,
8338            ))
8339        })
8340        .collect()
8341}
8342
8343fn encode_ht_code_block(
8344    coefficients: &[i32],
8345    width: u32,
8346    height: u32,
8347    total_bitplanes: u8,
8348    target_coding_passes: u8,
8349    accelerator: &mut impl J2kEncodeStageAccelerator,
8350) -> Result<bitplane_encode::EncodedCodeBlock, &'static str> {
8351    if let Some(encoded) = accelerator.encode_ht_code_block(crate::J2kHtCodeBlockEncodeJob {
8352        coefficients,
8353        width,
8354        height,
8355        total_bitplanes,
8356        target_coding_passes,
8357    })? {
8358        return Ok(ht_encoded_code_block_from_accelerator(encoded));
8359    }
8360
8361    ht_block_encode::encode_code_block_with_passes(
8362        coefficients,
8363        width,
8364        height,
8365        total_bitplanes,
8366        target_coding_passes,
8367    )
8368}
8369
8370fn ht_encoded_code_block_from_accelerator(
8371    encoded: crate::EncodedHtJ2kCodeBlock,
8372) -> bitplane_encode::EncodedCodeBlock {
8373    bitplane_encode::EncodedCodeBlock {
8374        data: encoded.data,
8375        num_coding_passes: encoded.num_coding_passes,
8376        num_zero_bitplanes: encoded.num_zero_bitplanes,
8377        ht_cleanup_length: encoded.cleanup_length,
8378        ht_refinement_length: encoded.refinement_length,
8379    }
8380}
8381
8382fn encode_tier1_code_block(
8383    coefficients: &[i32],
8384    width: u32,
8385    height: u32,
8386    sub_band_type: SubBandType,
8387    total_bitplanes: u8,
8388    accelerator: &mut impl J2kEncodeStageAccelerator,
8389) -> Result<bitplane_encode::EncodedCodeBlock, &'static str> {
8390    if let Some(encoded) = accelerator.encode_tier1_code_block(J2kTier1CodeBlockEncodeJob {
8391        coefficients,
8392        width,
8393        height,
8394        sub_band_type: public_sub_band_type(sub_band_type),
8395        total_bitplanes,
8396        style: default_public_code_block_style(),
8397    })? {
8398        return Ok(encoded_code_block_from_accelerator(encoded));
8399    }
8400
8401    Ok(bitplane_encode::encode_code_block(
8402        coefficients,
8403        width,
8404        height,
8405        sub_band_type,
8406        total_bitplanes,
8407    ))
8408}
8409
8410fn encoded_code_block_from_accelerator(
8411    encoded: EncodedJ2kCodeBlock,
8412) -> bitplane_encode::EncodedCodeBlock {
8413    bitplane_encode::EncodedCodeBlock {
8414        data: encoded.data,
8415        num_coding_passes: encoded.number_of_coding_passes,
8416        num_zero_bitplanes: encoded.missing_bit_planes,
8417        ht_cleanup_length: 0,
8418        ht_refinement_length: 0,
8419    }
8420}
8421
8422fn public_sub_band_type(sub_band_type: SubBandType) -> J2kSubBandType {
8423    match sub_band_type {
8424        SubBandType::LowLow => J2kSubBandType::LowLow,
8425        SubBandType::HighLow => J2kSubBandType::HighLow,
8426        SubBandType::LowHigh => J2kSubBandType::LowHigh,
8427        SubBandType::HighHigh => J2kSubBandType::HighHigh,
8428    }
8429}
8430
8431fn internal_sub_band_type(sub_band_type: J2kSubBandType) -> SubBandType {
8432    match sub_band_type {
8433        J2kSubBandType::LowLow => SubBandType::LowLow,
8434        J2kSubBandType::HighLow => SubBandType::HighLow,
8435        J2kSubBandType::LowHigh => SubBandType::LowHigh,
8436        J2kSubBandType::HighHigh => SubBandType::HighHigh,
8437    }
8438}
8439
8440fn default_public_code_block_style() -> crate::J2kCodeBlockStyle {
8441    crate::J2kCodeBlockStyle {
8442        selective_arithmetic_coding_bypass: false,
8443        reset_context_probabilities: false,
8444        termination_on_each_pass: false,
8445        vertically_causal_context: false,
8446        segmentation_symbols: false,
8447    }
8448}
8449
8450/// Convert interleaved pixel bytes to per-component f32 arrays.
8451pub(crate) fn deinterleave_to_f32(
8452    pixels: &[u8],
8453    num_pixels: usize,
8454    num_components: u16,
8455    bit_depth: u8,
8456    signed: bool,
8457) -> Vec<Vec<f32>> {
8458    if num_components == 3 && bit_depth == 8 && !signed {
8459        return deinterleave_rgb8_unsigned_to_f32(pixels, num_pixels);
8460    }
8461
8462    let nc = num_components as usize;
8463    let mut components = vec![vec![0.0f32; num_pixels]; nc];
8464    let unsigned_offset = if signed {
8465        0.0
8466    } else {
8467        (1_u64 << (u32::from(bit_depth) - 1)) as f32
8468    };
8469
8470    let bytes_per_sample = raw_pixel_bytes_per_sample(bit_depth).unwrap_or(2);
8471    for (i, pixel) in pixels
8472        .chunks_exact(nc * bytes_per_sample)
8473        .take(num_pixels)
8474        .enumerate()
8475    {
8476        for (c, component) in components.iter_mut().enumerate().take(nc) {
8477            let offset = c * bytes_per_sample;
8478            let raw = read_le_sample_value(&pixel[offset..offset + bytes_per_sample], bit_depth);
8479            component[i] = if signed {
8480                sign_extend_sample(raw, bit_depth) as f32
8481            } else {
8482                raw as f32 - unsigned_offset
8483            };
8484        }
8485    }
8486
8487    components
8488}
8489
8490fn deinterleave_rgb8_unsigned_to_f32(pixels: &[u8], num_pixels: usize) -> Vec<Vec<f32>> {
8491    let mut r = Vec::with_capacity(num_pixels);
8492    let mut g = Vec::with_capacity(num_pixels);
8493    let mut b = Vec::with_capacity(num_pixels);
8494
8495    for pixel in pixels.chunks_exact(3).take(num_pixels) {
8496        r.push(f32::from(pixel[0]) - 128.0);
8497        g.push(f32::from(pixel[1]) - 128.0);
8498        b.push(f32::from(pixel[2]) - 128.0);
8499    }
8500
8501    vec![r, g, b]
8502}
8503
8504/// Calculate the maximum number of decomposition levels for given dimensions.
8505fn max_decomposition_levels(width: u32, height: u32) -> u8 {
8506    let min_dim = width.min(height);
8507    if min_dim <= 1 {
8508        return 0;
8509    }
8510    floor_f32(log2_f32(min_dim as f32)) as u8
8511}
8512
8513#[cfg(test)]
8514mod tests {
8515    use super::*;
8516    use crate::PrequantizedHtj2k97CodeBlock;
8517
8518    fn test_preencoded_subband_payload(marker: u8) -> PreencodedHtj2k97Subband {
8519        PreencodedHtj2k97Subband {
8520            sub_band_type: J2kSubBandType::LowLow,
8521            num_cbs_x: 1,
8522            num_cbs_y: 1,
8523            total_bitplanes: 8,
8524            code_blocks: vec![PreencodedHtj2k97CodeBlock {
8525                width: 1,
8526                height: 1,
8527                encoded: crate::EncodedHtJ2kCodeBlock {
8528                    data: vec![marker; 8],
8529                    cleanup_length: 8,
8530                    refinement_length: 0,
8531                    num_coding_passes: 1,
8532                    num_zero_bitplanes: 0,
8533                },
8534            }],
8535        }
8536    }
8537
8538    #[test]
8539    fn prepared_subband_from_owned_preencoded_moves_payload_without_clone() {
8540        let subband = test_preencoded_subband_payload(7);
8541        let original_ptr = subband.code_blocks[0].encoded.data.as_ptr() as usize;
8542
8543        let prepared =
8544            prepared_subband_from_preencoded_owned(subband).expect("owned preencoded subband");
8545        let prepared_blocks = prepared
8546            .preencoded_ht_code_blocks
8547            .expect("preencoded payloads");
8548
8549        assert_eq!(prepared_blocks[0].data.as_ptr() as usize, original_ptr);
8550        assert!(prepared.code_blocks[0].coefficients.is_empty());
8551    }
8552
8553    #[test]
8554    fn compact_preencoded_packetization_borrows_payload_ranges() {
8555        #[derive(Default)]
8556        struct RecordingPacketizationAccelerator {
8557            payload_base: usize,
8558            observed_offsets: Vec<usize>,
8559            observed_lengths: Vec<usize>,
8560        }
8561
8562        impl crate::J2kEncodeStageAccelerator for RecordingPacketizationAccelerator {
8563            fn encode_packetization(
8564                &mut self,
8565                job: crate::J2kPacketizationEncodeJob<'_>,
8566            ) -> core::result::Result<Option<Vec<u8>>, &'static str> {
8567                for code_block in job
8568                    .resolutions
8569                    .iter()
8570                    .flat_map(|resolution| resolution.subbands.iter())
8571                    .flat_map(|subband| subband.code_blocks.iter())
8572                    .filter(|code_block| !code_block.data.is_empty())
8573                {
8574                    self.observed_offsets
8575                        .push((code_block.data.as_ptr() as usize) - self.payload_base);
8576                    self.observed_lengths.push(code_block.data.len());
8577                }
8578                Ok(Some(crate::encode_j2k_packetization_scalar(job)?))
8579            }
8580        }
8581
8582        let (preencoded, options) = sample_preencoded_htj2k97_for_test();
8583        let expected = encode_preencoded_htj2k_97(&preencoded, &options).expect("owned preencoded");
8584        let mut payload = Vec::new();
8585        let mut expected_offsets = Vec::new();
8586        let mut expected_lengths = Vec::new();
8587        let components = preencoded
8588            .components
8589            .iter()
8590            .map(|component| PreencodedHtj2k97CompactComponent {
8591                x_rsiz: component.x_rsiz,
8592                y_rsiz: component.y_rsiz,
8593                resolutions: component
8594                    .resolutions
8595                    .iter()
8596                    .map(|resolution| PreencodedHtj2k97CompactResolution {
8597                        subbands: resolution
8598                            .subbands
8599                            .iter()
8600                            .map(|subband| PreencodedHtj2k97CompactSubband {
8601                                sub_band_type: subband.sub_band_type,
8602                                num_cbs_x: subband.num_cbs_x,
8603                                num_cbs_y: subband.num_cbs_y,
8604                                total_bitplanes: subband.total_bitplanes,
8605                                code_blocks: subband
8606                                    .code_blocks
8607                                    .iter()
8608                                    .map(|block| {
8609                                        let start = payload.len();
8610                                        payload.extend_from_slice(&block.encoded.data);
8611                                        let end = payload.len();
8612                                        if start != end {
8613                                            expected_offsets.push(start);
8614                                            expected_lengths.push(end - start);
8615                                        }
8616                                        PreencodedHtj2k97CompactCodeBlock {
8617                                            width: block.width,
8618                                            height: block.height,
8619                                            payload_range: start..end,
8620                                            cleanup_length: block.encoded.cleanup_length,
8621                                            refinement_length: block.encoded.refinement_length,
8622                                            num_coding_passes: block.encoded.num_coding_passes,
8623                                            num_zero_bitplanes: block.encoded.num_zero_bitplanes,
8624                                        }
8625                                    })
8626                                    .collect(),
8627                            })
8628                            .collect(),
8629                    })
8630                    .collect(),
8631            })
8632            .collect();
8633        let compact = PreencodedHtj2k97CompactImage {
8634            width: preencoded.width,
8635            height: preencoded.height,
8636            bit_depth: preencoded.bit_depth,
8637            signed: preencoded.signed,
8638            payload,
8639            components,
8640        };
8641        let mut accelerator = RecordingPacketizationAccelerator {
8642            payload_base: compact.payload.as_ptr() as usize,
8643            ..Default::default()
8644        };
8645
8646        let actual = encode_preencoded_htj2k_97_compact_owned_with_accelerator(
8647            compact,
8648            &options,
8649            &mut accelerator,
8650        )
8651        .expect("compact preencoded");
8652
8653        assert_eq!(actual, expected);
8654        assert_eq!(accelerator.observed_offsets, expected_offsets);
8655        assert_eq!(accelerator.observed_lengths, expected_lengths);
8656    }
8657
8658    #[test]
8659    fn test_encode_8bit_gray() {
8660        let width = 8u32;
8661        let height = 8u32;
8662        let pixels: Vec<u8> = (0..64).collect();
8663
8664        let result = encode(
8665            &pixels,
8666            width,
8667            height,
8668            1,
8669            8,
8670            false,
8671            &EncodeOptions {
8672                num_decomposition_levels: 2,
8673                ..Default::default()
8674            },
8675        );
8676
8677        assert!(result.is_ok());
8678        let codestream = result.unwrap();
8679        // Verify SOC marker
8680        assert_eq!(codestream[0], 0xFF);
8681        assert_eq!(codestream[1], 0x4F);
8682        // Verify EOC marker
8683        let len = codestream.len();
8684        assert_eq!(codestream[len - 2], 0xFF);
8685        assert_eq!(codestream[len - 1], 0xD9);
8686    }
8687
8688    #[test]
8689    fn test_encode_16bit_gray() {
8690        let width = 8u32;
8691        let height = 8u32;
8692        let mut pixels = Vec::with_capacity(128);
8693        for i in 0..64u16 {
8694            let val = i * 100;
8695            pixels.extend_from_slice(&val.to_le_bytes());
8696        }
8697
8698        let result = encode(
8699            &pixels,
8700            width,
8701            height,
8702            1,
8703            16,
8704            false,
8705            &EncodeOptions {
8706                num_decomposition_levels: 2,
8707                ..Default::default()
8708            },
8709        );
8710
8711        assert!(result.is_ok());
8712    }
8713
8714    #[test]
8715    fn test_encode_rgb() {
8716        let width = 16u32;
8717        let height = 16u32;
8718        let pixels: Vec<u8> = (0..width * height * 3).map(|i| (i & 0xFF) as u8).collect();
8719
8720        let result = encode(
8721            &pixels,
8722            width,
8723            height,
8724            3,
8725            8,
8726            false,
8727            &EncodeOptions {
8728                num_decomposition_levels: 3,
8729                ..Default::default()
8730            },
8731        );
8732
8733        assert!(result.is_ok(), "RGB encode failed: {:?}", result.err());
8734    }
8735
8736    #[test]
8737    fn encode_with_accelerator_calls_lossless_stage_hooks() {
8738        #[derive(Default)]
8739        struct CountingAccelerator {
8740            forward_rct: usize,
8741            forward_dwt53: usize,
8742            tier1_code_blocks: usize,
8743            tier1_code_block_batches: usize,
8744            tier1_batched_jobs: usize,
8745            packetization: usize,
8746            packetization_resolution_count: u32,
8747            packetization_code_block_count: u32,
8748            packetization_saw_payload: bool,
8749        }
8750
8751        impl crate::J2kEncodeStageAccelerator for CountingAccelerator {
8752            fn encode_forward_rct(
8753                &mut self,
8754                _job: crate::J2kForwardRctJob<'_>,
8755            ) -> core::result::Result<bool, &'static str> {
8756                self.forward_rct += 1;
8757                Ok(false)
8758            }
8759
8760            fn encode_forward_dwt53(
8761                &mut self,
8762                _job: crate::J2kForwardDwt53Job<'_>,
8763            ) -> core::result::Result<Option<crate::J2kForwardDwt53Output>, &'static str>
8764            {
8765                self.forward_dwt53 += 1;
8766                Ok(None)
8767            }
8768
8769            fn encode_tier1_code_block(
8770                &mut self,
8771                _job: crate::J2kTier1CodeBlockEncodeJob<'_>,
8772            ) -> core::result::Result<Option<crate::EncodedJ2kCodeBlock>, &'static str>
8773            {
8774                self.tier1_code_blocks += 1;
8775                Ok(None)
8776            }
8777
8778            fn encode_tier1_code_blocks(
8779                &mut self,
8780                jobs: &[crate::J2kTier1CodeBlockEncodeJob<'_>],
8781            ) -> core::result::Result<Option<Vec<crate::EncodedJ2kCodeBlock>>, &'static str>
8782            {
8783                self.tier1_code_block_batches += 1;
8784                self.tier1_batched_jobs += jobs.len();
8785                Ok(None)
8786            }
8787
8788            fn encode_packetization(
8789                &mut self,
8790                job: crate::J2kPacketizationEncodeJob<'_>,
8791            ) -> core::result::Result<Option<Vec<u8>>, &'static str> {
8792                self.packetization += 1;
8793                self.packetization_resolution_count = job.resolution_count;
8794                self.packetization_code_block_count = job.code_block_count;
8795                self.packetization_saw_payload = job
8796                    .resolutions
8797                    .iter()
8798                    .flat_map(|resolution| resolution.subbands.iter())
8799                    .flat_map(|subband| subband.code_blocks.iter())
8800                    .any(|code_block| !code_block.data.is_empty());
8801                Ok(None)
8802            }
8803        }
8804
8805        let pixels: Vec<u8> = (0..8 * 8 * 3).map(|i| (i & 0xFF) as u8).collect();
8806        let options = EncodeOptions {
8807            num_decomposition_levels: 1,
8808            reversible: true,
8809            ..EncodeOptions::default()
8810        };
8811        let mut accelerator = CountingAccelerator::default();
8812
8813        let codestream =
8814            encode_with_accelerator(&pixels, 8, 8, 3, 8, false, &options, &mut accelerator)
8815                .expect("encode with accelerator hooks");
8816
8817        assert!(codestream.starts_with(&[0xFF, 0x4F]));
8818        assert_eq!(accelerator.forward_rct, 1);
8819        assert_eq!(accelerator.forward_dwt53, 3);
8820        assert!(accelerator.tier1_code_block_batches > 0);
8821        assert_eq!(
8822            accelerator.tier1_code_blocks,
8823            accelerator.tier1_batched_jobs
8824        );
8825        assert_eq!(accelerator.packetization, 1);
8826        assert_eq!(accelerator.packetization_resolution_count, 6);
8827        assert_eq!(
8828            accelerator.packetization_code_block_count,
8829            u32::try_from(accelerator.tier1_code_blocks).expect("test code-block count fits u32")
8830        );
8831        assert!(accelerator.packetization_saw_payload);
8832    }
8833
8834    #[test]
8835    fn cpu_only_accelerator_opts_into_parallel_block_fallback_only_for_native_cpu() {
8836        #[derive(Default)]
8837        struct ExternalAccelerator;
8838
8839        impl crate::J2kEncodeStageAccelerator for ExternalAccelerator {}
8840
8841        let cpu = crate::CpuOnlyJ2kEncodeStageAccelerator;
8842        let external = ExternalAccelerator;
8843
8844        assert!(cpu.prefer_parallel_cpu_code_block_fallback());
8845        assert!(!external.prefer_parallel_cpu_code_block_fallback());
8846    }
8847
8848    #[test]
8849    fn cpu_parallel_block_fallback_matches_serial_classic_and_htj2k_output() {
8850        #[derive(Default)]
8851        struct SerialCpuFallbackAccelerator;
8852
8853        impl crate::J2kEncodeStageAccelerator for SerialCpuFallbackAccelerator {}
8854
8855        let pixels = gradient_u8(96, 80);
8856        for use_ht_block_coding in [false, true] {
8857            let options = EncodeOptions {
8858                num_decomposition_levels: 1,
8859                code_block_width_exp: 2,
8860                code_block_height_exp: 2,
8861                use_ht_block_coding,
8862                ..EncodeOptions::default()
8863            };
8864            let parallel = encode(&pixels, 96, 80, 1, 8, false, &options)
8865                .expect("parallel CPU fallback encode");
8866            let mut serial_accelerator = SerialCpuFallbackAccelerator;
8867            let serial = encode_with_accelerator(
8868                &pixels,
8869                96,
8870                80,
8871                1,
8872                8,
8873                false,
8874                &options,
8875                &mut serial_accelerator,
8876            )
8877            .expect("serial CPU fallback encode");
8878
8879            assert_eq!(parallel, serial);
8880        }
8881    }
8882
8883    #[test]
8884    fn precomputed_htj2k53_offers_ht_code_blocks_to_encode_accelerator() {
8885        let image = sample_precomputed_htj2k53_image();
8886        let options = EncodeOptions {
8887            num_decomposition_levels: 1,
8888            reversible: true,
8889            guard_bits: 2,
8890            code_block_width_exp: 2,
8891            code_block_height_exp: 2,
8892            ..EncodeOptions::default()
8893        };
8894        let mut accelerator = CountingHtEncodeAccelerator::default();
8895
8896        let encoded =
8897            encode_precomputed_htj2k_53_with_accelerator(&image, &options, &mut accelerator)
8898                .expect("precomputed 5/3 encode accepts encode accelerator");
8899
8900        assert!(encoded.starts_with(&[0xff, 0x4f]));
8901        assert_eq!(accelerator.forward_dwt53, 0);
8902        assert_eq!(accelerator.forward_dwt97, 0);
8903        assert_eq!(accelerator.ht_batches, 1);
8904        assert!(accelerator.ht_jobs > 0);
8905        assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
8906    }
8907
8908    #[test]
8909    fn precomputed_htj2k97_offers_ht_code_blocks_to_encode_accelerator() {
8910        let image = sample_precomputed_htj2k97_image();
8911        let options = EncodeOptions {
8912            num_decomposition_levels: 1,
8913            reversible: false,
8914            guard_bits: 2,
8915            code_block_width_exp: 2,
8916            code_block_height_exp: 2,
8917            ..EncodeOptions::default()
8918        };
8919        let mut accelerator = CountingHtEncodeAccelerator::default();
8920
8921        let encoded =
8922            encode_precomputed_htj2k_97_with_accelerator(&image, &options, &mut accelerator)
8923                .expect("precomputed 9/7 encode accepts encode accelerator");
8924
8925        assert!(encoded.starts_with(&[0xff, 0x4f]));
8926        assert_eq!(accelerator.forward_dwt53, 0);
8927        assert_eq!(accelerator.forward_dwt97, 0);
8928        assert_eq!(accelerator.ht_batches, 1);
8929        assert!(accelerator.ht_jobs > 0);
8930        assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
8931    }
8932
8933    #[test]
8934    fn precomputed_dwt_geometry_validation_rejects_recursive_mismatch_for_both_filters() {
8935        let mut dwt53 = sample_precomputed_htj2k53_image();
8936        dwt53.components[0].dwt.levels[0].low_width += 1;
8937        assert_eq!(
8938            validate_precomputed_dwt_geometry(&dwt53),
8939            Err("precomputed DWT recursive geometry mismatch")
8940        );
8941
8942        let mut dwt97 = sample_precomputed_htj2k97_image();
8943        dwt97.components[0].dwt.levels[0].low_width += 1;
8944        assert_eq!(
8945            validate_precomputed_dwt97_geometry(&dwt97),
8946            Err("precomputed DWT recursive geometry mismatch")
8947        );
8948    }
8949
8950    #[test]
8951    fn prequantized_htj2k97_offers_ht_code_blocks_to_encode_accelerator() {
8952        let image = sample_precomputed_htj2k97_image();
8953        let options = EncodeOptions {
8954            num_decomposition_levels: 1,
8955            reversible: false,
8956            guard_bits: 2,
8957            code_block_width_exp: 2,
8958            code_block_height_exp: 2,
8959            ..EncodeOptions::default()
8960        };
8961        let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
8962            .expect("test prequantized image");
8963        let mut accelerator = CountingHtEncodeAccelerator::default();
8964
8965        let encoded = encode_prequantized_htj2k_97_with_accelerator(
8966            &prequantized,
8967            &options,
8968            &mut accelerator,
8969        )
8970        .expect("prequantized 9/7 encode accepts encode accelerator");
8971
8972        assert!(encoded.starts_with(&[0xff, 0x4f]));
8973        assert_eq!(accelerator.forward_dwt53, 0);
8974        assert_eq!(accelerator.forward_dwt97, 0);
8975        assert_eq!(accelerator.ht_batches, 1);
8976        assert!(accelerator.ht_jobs > 0);
8977        assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
8978    }
8979
8980    #[test]
8981    fn precomputed_htj2k97_batch_offers_all_ht_code_blocks_in_one_accelerator_call() {
8982        let image = sample_precomputed_htj2k97_image();
8983        let options = EncodeOptions {
8984            num_decomposition_levels: 1,
8985            reversible: false,
8986            guard_bits: 2,
8987            code_block_width_exp: 2,
8988            code_block_height_exp: 2,
8989            ..EncodeOptions::default()
8990        };
8991        let mut accelerator = CountingHtEncodeAccelerator::default();
8992
8993        let encoded = encode_precomputed_htj2k_97_batch_with_accelerator(
8994            &[image.clone(), image],
8995            &options,
8996            &mut accelerator,
8997        )
8998        .expect("batch precomputed 9/7 encode accepts encode accelerator");
8999
9000        assert_eq!(encoded.len(), 2);
9001        assert!(encoded
9002            .iter()
9003            .all(|codestream| codestream.starts_with(&[0xff, 0x4f])));
9004        assert_eq!(accelerator.forward_dwt53, 0);
9005        assert_eq!(accelerator.forward_dwt97, 0);
9006        assert_eq!(accelerator.ht_batches, 1);
9007        assert!(accelerator.ht_jobs > 0);
9008        assert_eq!(accelerator.ht_single_blocks, accelerator.ht_jobs);
9009    }
9010
9011    #[derive(Default)]
9012    struct CountingHtEncodeAccelerator {
9013        forward_dwt53: usize,
9014        forward_dwt97: usize,
9015        ht_batches: usize,
9016        ht_jobs: usize,
9017        ht_single_blocks: usize,
9018    }
9019
9020    impl crate::J2kEncodeStageAccelerator for CountingHtEncodeAccelerator {
9021        fn encode_forward_dwt53(
9022            &mut self,
9023            _job: crate::J2kForwardDwt53Job<'_>,
9024        ) -> core::result::Result<Option<crate::J2kForwardDwt53Output>, &'static str> {
9025            self.forward_dwt53 += 1;
9026            Ok(None)
9027        }
9028
9029        fn encode_forward_dwt97(
9030            &mut self,
9031            _job: crate::J2kForwardDwt97Job<'_>,
9032        ) -> core::result::Result<Option<crate::J2kForwardDwt97Output>, &'static str> {
9033            self.forward_dwt97 += 1;
9034            Ok(None)
9035        }
9036
9037        fn encode_ht_code_blocks(
9038            &mut self,
9039            jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
9040        ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str> {
9041            self.ht_batches += 1;
9042            self.ht_jobs += jobs.len();
9043            Ok(None)
9044        }
9045
9046        fn encode_ht_code_block(
9047            &mut self,
9048            _job: crate::J2kHtCodeBlockEncodeJob<'_>,
9049        ) -> core::result::Result<Option<crate::EncodedHtJ2kCodeBlock>, &'static str> {
9050            self.ht_single_blocks += 1;
9051            Ok(None)
9052        }
9053    }
9054
9055    #[test]
9056    fn prepare_subband_uses_fused_ht_subband_without_host_quantized_codeblocks() {
9057        #[derive(Default)]
9058        struct FusedHtSubbandAccelerator {
9059            subband_calls: usize,
9060            quantize_calls: usize,
9061            ht_batch_calls: usize,
9062        }
9063
9064        impl crate::J2kEncodeStageAccelerator for FusedHtSubbandAccelerator {
9065            fn encode_ht_subband(
9066                &mut self,
9067                job: crate::J2kHtSubbandEncodeJob<'_>,
9068            ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str>
9069            {
9070                self.subband_calls += 1;
9071                let count = (job.width.div_ceil(job.code_block_width) as usize)
9072                    .checked_mul(job.height.div_ceil(job.code_block_height) as usize)
9073                    .ok_or("test code-block count overflow")?;
9074                Ok(Some(
9075                    (0..count)
9076                        .map(|idx| crate::EncodedHtJ2kCodeBlock {
9077                            data: vec![u8::try_from(idx).expect("test block index fits"), 0],
9078                            cleanup_length: 2,
9079                            refinement_length: 0,
9080                            num_coding_passes: 1,
9081                            num_zero_bitplanes: 0,
9082                        })
9083                        .collect(),
9084                ))
9085            }
9086
9087            fn encode_quantize_subband(
9088                &mut self,
9089                _job: crate::J2kQuantizeSubbandJob<'_>,
9090            ) -> core::result::Result<Option<Vec<i32>>, &'static str> {
9091                self.quantize_calls += 1;
9092                Ok(None)
9093            }
9094
9095            fn encode_ht_code_blocks(
9096                &mut self,
9097                _jobs: &[crate::J2kHtCodeBlockEncodeJob<'_>],
9098            ) -> core::result::Result<Option<Vec<crate::EncodedHtJ2kCodeBlock>>, &'static str>
9099            {
9100                self.ht_batch_calls += 1;
9101                Ok(None)
9102            }
9103        }
9104
9105        let coefficients = vec![0.0; 16];
9106        let mut accelerator = FusedHtSubbandAccelerator::default();
9107        let prepared = prepare_subband(
9108            &coefficients,
9109            4,
9110            4,
9111            &QuantStepSize {
9112                exponent: 8,
9113                mantissa: 0,
9114            },
9115            8,
9116            2,
9117            true,
9118            BlockCodingMode::HighThroughput,
9119            2,
9120            2,
9121            SubBandType::LowLow,
9122            0,
9123            &[],
9124            1,
9125            1,
9126            &mut accelerator,
9127        )
9128        .expect("fused HT subband prepare");
9129
9130        assert_eq!(accelerator.subband_calls, 1);
9131        assert_eq!(accelerator.quantize_calls, 0);
9132        assert!(prepared.preencoded_ht_code_blocks.is_some());
9133        assert!(prepared
9134            .code_blocks
9135            .iter()
9136            .all(|block| block.coefficients.is_empty()));
9137
9138        let precincts = encode_prepared_subbands(vec![prepared], &mut accelerator)
9139            .expect("preencoded HT subband packet data");
9140
9141        assert_eq!(accelerator.ht_batch_calls, 0);
9142        assert_eq!(precincts[0].code_blocks.len(), 4);
9143        assert_eq!(precincts[0].code_blocks[2].data, vec![2, 0]);
9144    }
9145
9146    #[test]
9147    fn ht_target_coding_passes_tracks_ht_quality_layers() {
9148        let mut options = EncodeOptions {
9149            use_ht_block_coding: true,
9150            reversible: false,
9151            num_layers: 1,
9152            ..EncodeOptions::default()
9153        };
9154
9155        assert_eq!(ht_target_coding_passes_for_options(&options), 1);
9156
9157        options.num_layers = 2;
9158        assert_eq!(ht_target_coding_passes_for_options(&options), 2);
9159
9160        options.num_layers = 3;
9161        assert_eq!(ht_target_coding_passes_for_options(&options), 3);
9162
9163        options.num_layers = 4;
9164        assert_eq!(ht_target_coding_passes_for_options(&options), 3);
9165
9166        options.reversible = true;
9167        assert_eq!(ht_target_coding_passes_for_options(&options), 3);
9168
9169        options.reversible = false;
9170        options.use_ht_block_coding = false;
9171        assert_eq!(ht_target_coding_passes_for_options(&options), 1);
9172    }
9173
9174    #[test]
9175    fn packet_header_validation_allows_chunked_ppm_and_ppt_payloads() {
9176        const MARKER_PAYLOAD_LIMIT: usize = u16::MAX as usize - 3;
9177        let ppm_headers = vec![vec![0_u8; MARKER_PAYLOAD_LIMIT - 2], vec![1_u8; 1]];
9178        let ppt_headers = vec![vec![2_u8; MARKER_PAYLOAD_LIMIT + 1]];
9179
9180        validate_packet_header_marker_payloads(true, false, &[&ppm_headers])
9181            .expect("chunked PPM payload should validate");
9182        validate_packet_header_marker_payloads(false, true, &[&ppt_headers])
9183            .expect("chunked PPT payload should validate");
9184    }
9185
9186    #[test]
9187    fn ht_cpu_fallback_encodes_two_pass_sigprop_refinement() {
9188        let coefficients: Vec<i32> = (0usize..64 * 64)
9189            .map(|index| {
9190                let value = ((((index * 31) ^ (index / 3)) & 0x00ff) as i32 - 127) * 2;
9191                if index.is_multiple_of(11) {
9192                    0
9193                } else {
9194                    value
9195                }
9196            })
9197            .collect();
9198        let jobs = [crate::J2kHtCodeBlockEncodeJob {
9199            coefficients: &coefficients,
9200            width: 64,
9201            height: 64,
9202            total_bitplanes: 10,
9203            target_coding_passes: 2,
9204        }];
9205
9206        let encoded = encode_all_ht_code_blocks_serial_cpu(&jobs).expect("two-pass CPU HT encode");
9207
9208        assert_eq!(encoded.len(), 1);
9209        assert_eq!(encoded[0].num_coding_passes, 2);
9210        assert_eq!(encoded[0].ht_refinement_length, 48);
9211        assert_eq!(
9212            encoded[0].data.len(),
9213            encoded[0].ht_cleanup_length as usize + encoded[0].ht_refinement_length as usize
9214        );
9215        assert!(encoded[0].data[encoded[0].ht_cleanup_length as usize..]
9216            .iter()
9217            .all(|byte| *byte == 0));
9218
9219        let segments = crate::j2c::ht_block_decode::HtCodeBlockSegments::from_combined_payload(
9220            &encoded[0].data,
9221            encoded[0].ht_cleanup_length,
9222            encoded[0].ht_refinement_length,
9223        )
9224        .expect("split HT segments");
9225        let mut decoded = vec![0u32; coefficients.len()];
9226        crate::j2c::ht_block_decode::decode_segments_validated(
9227            &segments,
9228            encoded[0].num_zero_bitplanes,
9229            10,
9230            encoded[0].num_coding_passes,
9231            false,
9232            true,
9233            &mut decoded,
9234            64,
9235            64,
9236            64,
9237        )
9238        .expect("decode two-pass HT block");
9239        let decoded_i32 = decoded
9240            .into_iter()
9241            .map(|value| crate::j2c::ht_block_decode::coefficient_to_i32(value, 10))
9242            .collect::<Vec<_>>();
9243        let max_abs_delta = decoded_i32
9244            .iter()
9245            .zip(&coefficients)
9246            .map(|(actual, expected)| actual.abs_diff(*expected))
9247            .max()
9248            .unwrap_or(0);
9249
9250        assert!(
9251            max_abs_delta <= 1,
9252            "two-pass HT sigprop decode must stay within one coefficient LSB"
9253        );
9254    }
9255
9256    #[test]
9257    fn ht_cpu_fallback_sigprop_refinement_encodes_new_significance_bits() {
9258        let mut coefficients = vec![0_i32; 8 * 8];
9259        for row in 0..8 {
9260            coefficients[row * 8] = 3;
9261            coefficients[row * 8 + 1] = 1;
9262            coefficients[row * 8 + 2] = -1;
9263        }
9264        let jobs = [crate::J2kHtCodeBlockEncodeJob {
9265            coefficients: &coefficients,
9266            width: 8,
9267            height: 8,
9268            total_bitplanes: 4,
9269            target_coding_passes: 2,
9270        }];
9271
9272        let encoded = encode_all_ht_code_blocks_serial_cpu(&jobs).expect("two-pass CPU HT encode");
9273
9274        assert_eq!(encoded[0].num_coding_passes, 2);
9275        assert!(encoded[0].ht_refinement_length > 0);
9276        assert!(
9277            encoded[0].data[encoded[0].ht_cleanup_length as usize..]
9278                .iter()
9279                .any(|byte| *byte != 0),
9280            "sigprop refinement should encode new significance/sign bits"
9281        );
9282
9283        let segments = crate::j2c::ht_block_decode::HtCodeBlockSegments::from_combined_payload(
9284            &encoded[0].data,
9285            encoded[0].ht_cleanup_length,
9286            encoded[0].ht_refinement_length,
9287        )
9288        .expect("split HT segments");
9289        let mut decoded = vec![0u32; coefficients.len()];
9290        crate::j2c::ht_block_decode::decode_segments_validated(
9291            &segments,
9292            encoded[0].num_zero_bitplanes,
9293            4,
9294            encoded[0].num_coding_passes,
9295            false,
9296            true,
9297            &mut decoded,
9298            8,
9299            8,
9300            8,
9301        )
9302        .expect("decode two-pass HT block");
9303        let decoded_i32 = decoded
9304            .into_iter()
9305            .map(|value| crate::j2c::ht_block_decode::coefficient_to_i32(value, 4))
9306            .collect::<Vec<_>>();
9307
9308        assert_eq!(decoded_i32, coefficients);
9309    }
9310
9311    #[test]
9312    fn ht_cpu_fallback_encodes_three_pass_magref_refinement() {
9313        let mut coefficients = vec![0_i32; 8 * 8];
9314        for row in 0..8 {
9315            let base = row * 8;
9316            coefficients[base] = 2;
9317            coefficients[base + 1] = 3;
9318            coefficients[base + 2] = 1;
9319            coefficients[base + 3] = -1;
9320            coefficients[base + 4] = -2;
9321            coefficients[base + 5] = -3;
9322        }
9323        let jobs = [crate::J2kHtCodeBlockEncodeJob {
9324            coefficients: &coefficients,
9325            width: 8,
9326            height: 8,
9327            total_bitplanes: 4,
9328            target_coding_passes: 3,
9329        }];
9330
9331        let encoded =
9332            encode_all_ht_code_blocks_serial_cpu(&jobs).expect("three-pass CPU HT encode");
9333
9334        assert_eq!(encoded[0].num_coding_passes, 3);
9335        assert!(encoded[0].ht_refinement_length > 0);
9336
9337        let segments = crate::j2c::ht_block_decode::HtCodeBlockSegments::from_combined_payload(
9338            &encoded[0].data,
9339            encoded[0].ht_cleanup_length,
9340            encoded[0].ht_refinement_length,
9341        )
9342        .expect("split HT segments");
9343        let mut decoded = vec![0u32; coefficients.len()];
9344        crate::j2c::ht_block_decode::decode_segments_validated(
9345            &segments,
9346            encoded[0].num_zero_bitplanes,
9347            4,
9348            encoded[0].num_coding_passes,
9349            false,
9350            true,
9351            &mut decoded,
9352            8,
9353            8,
9354            8,
9355        )
9356        .expect("decode three-pass HT block");
9357        let decoded_i32 = decoded
9358            .into_iter()
9359            .map(|value| crate::j2c::ht_block_decode::coefficient_to_i32(value, 4))
9360            .collect::<Vec<_>>();
9361
9362        assert_eq!(decoded_i32, coefficients);
9363    }
9364
9365    #[test]
9366    fn ht_cpu_fallback_rejects_unsupported_refinement_pass_count() {
9367        let coefficients = vec![1_i32; 64 * 64];
9368        let jobs = [crate::J2kHtCodeBlockEncodeJob {
9369            coefficients: &coefficients,
9370            width: 64,
9371            height: 64,
9372            total_bitplanes: 2,
9373            target_coding_passes: 4,
9374        }];
9375
9376        let err = encode_all_ht_code_blocks_serial_cpu(&jobs)
9377            .expect_err("CPU HT encode must reject unsupported pass requests");
9378
9379        assert!(err.contains("at most three HT coding passes"));
9380    }
9381
9382    #[test]
9383    fn ht_cpu_parallel_fallback_threshold_matches_parallel_output() {
9384        assert_eq!(HT_CPU_PARALLEL_FALLBACK_MIN_JOBS, 4);
9385
9386        let blocks: Vec<Vec<i32>> = (0..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS)
9387            .map(|seed| {
9388                (0usize..64 * 64)
9389                    .map(|index| {
9390                        let value = (((index * 31) ^ (seed * 17)) & 0x01ff) as i32 - 255;
9391                        if (index + seed).is_multiple_of(11) {
9392                            0
9393                        } else {
9394                            value
9395                        }
9396                    })
9397                    .collect()
9398            })
9399            .collect();
9400        let jobs: Vec<_> = blocks
9401            .iter()
9402            .map(|coefficients| crate::J2kHtCodeBlockEncodeJob {
9403                coefficients,
9404                width: 64,
9405                height: 64,
9406                total_bitplanes: 10,
9407                target_coding_passes: 1,
9408            })
9409            .collect();
9410
9411        let serial =
9412            encode_all_ht_code_blocks_serial_cpu(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS - 1])
9413                .expect("serial tiny HT encode");
9414        let parallel =
9415            encode_all_ht_code_blocks_parallel(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS])
9416                .expect("parallel HT encode");
9417        let serial_threshold =
9418            encode_all_ht_code_blocks_serial_cpu(&jobs[..HT_CPU_PARALLEL_FALLBACK_MIN_JOBS])
9419                .expect("serial threshold HT encode");
9420
9421        assert_eq!(serial.len(), HT_CPU_PARALLEL_FALLBACK_MIN_JOBS - 1);
9422        assert_eq!(parallel.len(), HT_CPU_PARALLEL_FALLBACK_MIN_JOBS);
9423        assert_eq!(serial_threshold.len(), parallel.len());
9424        for (serial, parallel) in serial_threshold.iter().zip(&parallel) {
9425            assert_eq!(serial.data, parallel.data);
9426            assert_eq!(serial.num_coding_passes, parallel.num_coding_passes);
9427            assert_eq!(serial.num_zero_bitplanes, parallel.num_zero_bitplanes);
9428        }
9429    }
9430
9431    #[test]
9432    fn code_block_extraction_copies_partial_edge_blocks_rowwise() {
9433        let quantized: Vec<i32> = (0..20).collect();
9434
9435        let block = copy_code_block_coefficients(&quantized, 5, 3, 1, 2, 3);
9436
9437        assert_eq!(block, vec![8, 9, 13, 14, 18, 19]);
9438    }
9439
9440    #[test]
9441    fn test_encode_lossy() {
9442        let pixels: Vec<u8> = (0..64).collect();
9443
9444        let result = encode(
9445            &pixels,
9446            8,
9447            8,
9448            1,
9449            8,
9450            false,
9451            &EncodeOptions {
9452                num_decomposition_levels: 2,
9453                reversible: false,
9454                guard_bits: 2,
9455                ..Default::default()
9456            },
9457        );
9458
9459        assert!(result.is_ok());
9460    }
9461
9462    #[test]
9463    fn prequantized_htj2k97_matches_precomputed_dwt97_codestream() {
9464        let image = sample_precomputed_htj2k97_image();
9465        let options = EncodeOptions {
9466            num_decomposition_levels: 1,
9467            reversible: false,
9468            guard_bits: 2,
9469            code_block_width_exp: 2,
9470            code_block_height_exp: 2,
9471            ..EncodeOptions::default()
9472        };
9473
9474        let precomputed =
9475            encode_precomputed_htj2k_97(&image, &options).expect("precomputed DWT encode");
9476        let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
9477            .expect("test prequantized image");
9478        let direct =
9479            encode_prequantized_htj2k_97(&prequantized, &options).expect("prequantized encode");
9480
9481        assert_eq!(direct, precomputed);
9482    }
9483
9484    #[test]
9485    fn preencoded_htj2k97_matches_prequantized_codestream() {
9486        let image = sample_precomputed_htj2k97_image();
9487        let options = EncodeOptions {
9488            num_decomposition_levels: 1,
9489            reversible: false,
9490            guard_bits: 2,
9491            code_block_width_exp: 2,
9492            code_block_height_exp: 2,
9493            ..EncodeOptions::default()
9494        };
9495        let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
9496            .expect("test prequantized image");
9497        let expected =
9498            encode_prequantized_htj2k_97(&prequantized, &options).expect("prequantized encode");
9499        let preencoded = preencoded_htj2k97_image_from_prequantized_for_test(&prequantized)
9500            .expect("test preencoded image");
9501        let actual = encode_preencoded_htj2k_97(&preencoded, &options).expect("preencoded encode");
9502
9503        assert_eq!(actual, expected);
9504    }
9505
9506    #[test]
9507    fn preencoded_htj2k97_preserves_refinement_segments_in_packet_body() {
9508        let options = EncodeOptions {
9509            num_decomposition_levels: 0,
9510            reversible: false,
9511            guard_bits: 2,
9512            code_block_width_exp: 2,
9513            code_block_height_exp: 2,
9514            ..EncodeOptions::default()
9515        };
9516        let guard_bits = options.guard_bits.max(2);
9517        let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
9518            8,
9519            0,
9520            false,
9521            guard_bits,
9522            options.irreversible_quantization_scale,
9523            options.irreversible_quantization_subband_scales,
9524        );
9525        let total_bitplanes = guard_bits
9526            .saturating_add(step_sizes[0].exponent as u8)
9527            .saturating_sub(1);
9528        let payload = [0x12, 0x34, 0x56, 0x78];
9529        let image = PreencodedHtj2k97Image {
9530            width: 1,
9531            height: 1,
9532            bit_depth: 8,
9533            signed: false,
9534            components: vec![PreencodedHtj2k97Component {
9535                x_rsiz: 1,
9536                y_rsiz: 1,
9537                resolutions: vec![PreencodedHtj2k97Resolution {
9538                    subbands: vec![PreencodedHtj2k97Subband {
9539                        sub_band_type: J2kSubBandType::LowLow,
9540                        num_cbs_x: 1,
9541                        num_cbs_y: 1,
9542                        total_bitplanes,
9543                        code_blocks: vec![PreencodedHtj2k97CodeBlock {
9544                            width: 1,
9545                            height: 1,
9546                            encoded: EncodedHtJ2kCodeBlock {
9547                                data: payload.to_vec(),
9548                                cleanup_length: 2,
9549                                refinement_length: 2,
9550                                num_coding_passes: 3,
9551                                num_zero_bitplanes: 0,
9552                            },
9553                        }],
9554                    }],
9555                }],
9556            }],
9557        };
9558
9559        let codestream =
9560            encode_preencoded_htj2k_97(&image, &options).expect("preencoded refinement encode");
9561        let eoc = codestream
9562            .windows(2)
9563            .rposition(|marker| marker == [0xff, crate::j2c::codestream::markers::EOC])
9564            .expect("EOC marker");
9565
9566        assert_eq!(&codestream[eoc - payload.len()..eoc], payload);
9567    }
9568
9569    #[test]
9570    fn preencoded_htj2k97_rejects_empty_block_with_wrong_zero_bitplanes() {
9571        let (mut image, options) = sample_preencoded_htj2k97_for_test();
9572        let block = &mut image.components[0].resolutions[0].subbands[0].code_blocks[0];
9573        block.encoded = EncodedHtJ2kCodeBlock {
9574            data: Vec::new(),
9575            cleanup_length: 0,
9576            refinement_length: 0,
9577            num_coding_passes: 0,
9578            num_zero_bitplanes: 0,
9579        };
9580
9581        let error = encode_preencoded_htj2k_97(&image, &options)
9582            .expect_err("invalid all-zero block metadata must be rejected");
9583
9584        assert_eq!(error, "empty HTJ2K code-block zero-bitplane count mismatch");
9585    }
9586
9587    #[test]
9588    fn preencoded_htj2k97_rejects_coded_block_with_too_many_zero_bitplanes() {
9589        let (mut image, options) = sample_preencoded_htj2k97_for_test();
9590        let subband = &mut image.components[0].resolutions[0].subbands[0];
9591        subband.code_blocks[0].encoded.num_zero_bitplanes = subband.total_bitplanes;
9592
9593        let error = encode_preencoded_htj2k_97(&image, &options)
9594            .expect_err("coded block with no coded bitplanes must be rejected");
9595
9596        assert_eq!(error, "HTJ2K code-block zero-bitplane count out of range");
9597    }
9598
9599    #[cfg(feature = "std")]
9600    #[test]
9601    fn preencoded_htj2k97_rejects_too_many_coding_passes_without_panic() {
9602        let (mut image, options) = sample_preencoded_htj2k97_for_test();
9603        image.components[0].resolutions[0].subbands[0].code_blocks[0]
9604            .encoded
9605            .num_coding_passes = 165;
9606
9607        let result = std::panic::catch_unwind(|| encode_preencoded_htj2k_97(&image, &options));
9608
9609        assert!(result.is_ok(), "invalid coding pass count must not panic");
9610        assert_eq!(
9611            result.expect("catch_unwind returned checked result"),
9612            Err("HTJ2K code-block coding pass count out of range")
9613        );
9614    }
9615
9616    #[test]
9617    fn prequantized_htj2k97_accepts_empty_high_subbands() {
9618        let options = EncodeOptions {
9619            num_decomposition_levels: 1,
9620            reversible: false,
9621            guard_bits: 2,
9622            code_block_width_exp: 2,
9623            code_block_height_exp: 2,
9624            ..EncodeOptions::default()
9625        };
9626        let image = PrequantizedHtj2k97Image {
9627            width: 1,
9628            height: 1,
9629            bit_depth: 8,
9630            signed: false,
9631            components: vec![PrequantizedHtj2k97Component {
9632                x_rsiz: 1,
9633                y_rsiz: 1,
9634                resolutions: vec![
9635                    PrequantizedHtj2k97Resolution {
9636                        subbands: vec![PrequantizedHtj2k97Subband {
9637                            sub_band_type: J2kSubBandType::LowLow,
9638                            num_cbs_x: 1,
9639                            num_cbs_y: 1,
9640                            total_bitplanes: 11,
9641                            code_blocks: vec![PrequantizedHtj2k97CodeBlock {
9642                                coefficients: vec![0],
9643                                width: 1,
9644                                height: 1,
9645                            }],
9646                        }],
9647                    },
9648                    PrequantizedHtj2k97Resolution {
9649                        subbands: vec![
9650                            empty_prequantized_subband(J2kSubBandType::HighLow),
9651                            empty_prequantized_subband(J2kSubBandType::LowHigh),
9652                            empty_prequantized_subband(J2kSubBandType::HighHigh),
9653                        ],
9654                    },
9655                ],
9656            }],
9657        };
9658
9659        let encoded =
9660            encode_prequantized_htj2k_97(&image, &options).expect("empty high subbands encode");
9661
9662        assert!(encoded.starts_with(&[0xff, 0x4f]));
9663    }
9664
9665    fn empty_prequantized_subband(sub_band_type: J2kSubBandType) -> PrequantizedHtj2k97Subband {
9666        PrequantizedHtj2k97Subband {
9667            sub_band_type,
9668            num_cbs_x: 0,
9669            num_cbs_y: 0,
9670            total_bitplanes: 0,
9671            code_blocks: Vec::new(),
9672        }
9673    }
9674
9675    fn sample_precomputed_htj2k97_image() -> PrecomputedHtj2k97Image {
9676        let width = 17u32;
9677        let height = 13u32;
9678        let low_width = width.div_ceil(2);
9679        let low_height = height.div_ceil(2);
9680        let high_width = width / 2;
9681        let high_height = height / 2;
9682
9683        PrecomputedHtj2k97Image {
9684            width,
9685            height,
9686            bit_depth: 8,
9687            signed: false,
9688            components: vec![PrecomputedHtj2k97Component {
9689                x_rsiz: 1,
9690                y_rsiz: 1,
9691                dwt: J2kForwardDwt97Output {
9692                    ll: sample_f32_coefficients(low_width * low_height, 0.25),
9693                    ll_width: low_width,
9694                    ll_height: low_height,
9695                    levels: vec![J2kForwardDwt97Level {
9696                        hl: sample_f32_coefficients(high_width * low_height, -0.75),
9697                        lh: sample_f32_coefficients(low_width * high_height, 1.25),
9698                        hh: sample_f32_coefficients(high_width * high_height, -1.5),
9699                        width,
9700                        height,
9701                        low_width,
9702                        low_height,
9703                        high_width,
9704                        high_height,
9705                    }],
9706                },
9707            }],
9708        }
9709    }
9710
9711    fn sample_precomputed_htj2k53_image() -> PrecomputedHtj2k53Image {
9712        let width = 17u32;
9713        let height = 13u32;
9714        let low_width = width.div_ceil(2);
9715        let low_height = height.div_ceil(2);
9716        let high_width = width / 2;
9717        let high_height = height / 2;
9718
9719        PrecomputedHtj2k53Image {
9720            width,
9721            height,
9722            bit_depth: 8,
9723            signed: false,
9724            components: vec![PrecomputedHtj2k53Component {
9725                x_rsiz: 1,
9726                y_rsiz: 1,
9727                dwt: J2kForwardDwt53Output {
9728                    ll: sample_f32_coefficients(low_width * low_height, 0.0),
9729                    ll_width: low_width,
9730                    ll_height: low_height,
9731                    levels: vec![J2kForwardDwt53Level {
9732                        hl: sample_f32_coefficients(high_width * low_height, -2.0),
9733                        lh: sample_f32_coefficients(low_width * high_height, 2.0),
9734                        hh: sample_f32_coefficients(high_width * high_height, -4.0),
9735                        width,
9736                        height,
9737                        low_width,
9738                        low_height,
9739                        high_width,
9740                        high_height,
9741                    }],
9742                },
9743            }],
9744        }
9745    }
9746
9747    fn sample_f32_coefficients(len: u32, offset: f32) -> Vec<f32> {
9748        (0..len)
9749            .map(|idx| ((idx % 17) as f32 - 8.0) * 0.5 + offset)
9750            .collect()
9751    }
9752
9753    fn prequantized_htj2k97_image_from_precomputed_for_test(
9754        image: &PrecomputedHtj2k97Image,
9755        options: &EncodeOptions,
9756    ) -> Result<PrequantizedHtj2k97Image, &'static str> {
9757        let guard_bits = options.guard_bits.max(2);
9758        let step_sizes = quantize::compute_step_sizes_with_irreversible_profile(
9759            image.bit_depth,
9760            1,
9761            false,
9762            guard_bits,
9763            options.irreversible_quantization_scale,
9764            options.irreversible_quantization_subband_scales,
9765        );
9766        let cb_width = 1u32 << (options.code_block_width_exp + 2);
9767        let cb_height = 1u32 << (options.code_block_height_exp + 2);
9768
9769        let components = image
9770            .components
9771            .iter()
9772            .map(|component| {
9773                let mut resolutions = Vec::with_capacity(component.dwt.levels.len() + 1);
9774                resolutions.push(PrequantizedHtj2k97Resolution {
9775                    subbands: vec![prequantized_subband_for_test(
9776                        &component.dwt.ll,
9777                        component.dwt.ll_width,
9778                        component.dwt.ll_height,
9779                        SubBandType::LowLow,
9780                        &step_sizes[0],
9781                        image.bit_depth,
9782                        guard_bits,
9783                        cb_width,
9784                        cb_height,
9785                    )?],
9786                });
9787
9788                for (level_index, level) in component.dwt.levels.iter().enumerate() {
9789                    let step_base = 1 + level_index * 3;
9790                    resolutions.push(PrequantizedHtj2k97Resolution {
9791                        subbands: vec![
9792                            prequantized_subband_for_test(
9793                                &level.hl,
9794                                level.high_width,
9795                                level.low_height,
9796                                SubBandType::HighLow,
9797                                &step_sizes[step_base],
9798                                image.bit_depth,
9799                                guard_bits,
9800                                cb_width,
9801                                cb_height,
9802                            )?,
9803                            prequantized_subband_for_test(
9804                                &level.lh,
9805                                level.low_width,
9806                                level.high_height,
9807                                SubBandType::LowHigh,
9808                                &step_sizes[step_base + 1],
9809                                image.bit_depth,
9810                                guard_bits,
9811                                cb_width,
9812                                cb_height,
9813                            )?,
9814                            prequantized_subband_for_test(
9815                                &level.hh,
9816                                level.high_width,
9817                                level.high_height,
9818                                SubBandType::HighHigh,
9819                                &step_sizes[step_base + 2],
9820                                image.bit_depth,
9821                                guard_bits,
9822                                cb_width,
9823                                cb_height,
9824                            )?,
9825                        ],
9826                    });
9827                }
9828
9829                Ok(PrequantizedHtj2k97Component {
9830                    x_rsiz: component.x_rsiz,
9831                    y_rsiz: component.y_rsiz,
9832                    resolutions,
9833                })
9834            })
9835            .collect::<Result<Vec<_>, &'static str>>()?;
9836
9837        Ok(PrequantizedHtj2k97Image {
9838            width: image.width,
9839            height: image.height,
9840            bit_depth: image.bit_depth,
9841            signed: image.signed,
9842            components,
9843        })
9844    }
9845
9846    #[allow(clippy::too_many_arguments)]
9847    fn prequantized_subband_for_test(
9848        coefficients: &[f32],
9849        width: u32,
9850        height: u32,
9851        sub_band_type: SubBandType,
9852        step_size: &QuantStepSize,
9853        bit_depth: u8,
9854        guard_bits: u8,
9855        cb_width: u32,
9856        cb_height: u32,
9857    ) -> Result<PrequantizedHtj2k97Subband, &'static str> {
9858        let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
9859        let prepared = prepare_subband(
9860            coefficients,
9861            width,
9862            height,
9863            step_size,
9864            bit_depth,
9865            guard_bits,
9866            false,
9867            BlockCodingMode::HighThroughput,
9868            cb_width,
9869            cb_height,
9870            sub_band_type,
9871            0,
9872            &[],
9873            1,
9874            1,
9875            &mut accelerator,
9876        )?;
9877
9878        Ok(PrequantizedHtj2k97Subband {
9879            sub_band_type: public_sub_band_type(sub_band_type),
9880            num_cbs_x: prepared.num_cbs_x,
9881            num_cbs_y: prepared.num_cbs_y,
9882            total_bitplanes: prepared.total_bitplanes,
9883            code_blocks: prepared
9884                .code_blocks
9885                .into_iter()
9886                .map(|block| {
9887                    Ok(PrequantizedHtj2k97CodeBlock {
9888                        coefficients: downcast_i64_coefficients_to_i32(&block.coefficients)?,
9889                        width: block.width,
9890                        height: block.height,
9891                    })
9892                })
9893                .collect::<Result<Vec<_>, &'static str>>()?,
9894        })
9895    }
9896
9897    fn preencoded_htj2k97_image_from_prequantized_for_test(
9898        image: &PrequantizedHtj2k97Image,
9899    ) -> Result<PreencodedHtj2k97Image, &'static str> {
9900        let components = image
9901            .components
9902            .iter()
9903            .map(|component| {
9904                Ok(PreencodedHtj2k97Component {
9905                    x_rsiz: component.x_rsiz,
9906                    y_rsiz: component.y_rsiz,
9907                    resolutions: component
9908                        .resolutions
9909                        .iter()
9910                        .map(|resolution| {
9911                            Ok(PreencodedHtj2k97Resolution {
9912                                subbands: resolution
9913                                    .subbands
9914                                    .iter()
9915                                    .map(preencoded_subband_from_prequantized_for_test)
9916                                    .collect::<Result<Vec<_>, &'static str>>()?,
9917                            })
9918                        })
9919                        .collect::<Result<Vec<_>, &'static str>>()?,
9920                })
9921            })
9922            .collect::<Result<Vec<_>, &'static str>>()?;
9923
9924        Ok(PreencodedHtj2k97Image {
9925            width: image.width,
9926            height: image.height,
9927            bit_depth: image.bit_depth,
9928            signed: image.signed,
9929            components,
9930        })
9931    }
9932
9933    fn sample_preencoded_htj2k97_for_test() -> (PreencodedHtj2k97Image, EncodeOptions) {
9934        let image = sample_precomputed_htj2k97_image();
9935        let options = EncodeOptions {
9936            num_decomposition_levels: 1,
9937            reversible: false,
9938            guard_bits: 2,
9939            code_block_width_exp: 2,
9940            code_block_height_exp: 2,
9941            ..EncodeOptions::default()
9942        };
9943        let prequantized = prequantized_htj2k97_image_from_precomputed_for_test(&image, &options)
9944            .expect("test prequantized image");
9945        let preencoded = preencoded_htj2k97_image_from_prequantized_for_test(&prequantized)
9946            .expect("test preencoded image");
9947        (preencoded, options)
9948    }
9949
9950    fn preencoded_subband_from_prequantized_for_test(
9951        subband: &PrequantizedHtj2k97Subband,
9952    ) -> Result<PreencodedHtj2k97Subband, &'static str> {
9953        let code_blocks = subband
9954            .code_blocks
9955            .iter()
9956            .map(|block| {
9957                let encoded = ht_block_encode::encode_code_block(
9958                    &block.coefficients,
9959                    block.width,
9960                    block.height,
9961                    subband.total_bitplanes,
9962                )?;
9963                Ok(PreencodedHtj2k97CodeBlock {
9964                    width: block.width,
9965                    height: block.height,
9966                    encoded: EncodedHtJ2kCodeBlock {
9967                        data: encoded.data,
9968                        cleanup_length: encoded.ht_cleanup_length,
9969                        refinement_length: encoded.ht_refinement_length,
9970                        num_coding_passes: encoded.num_coding_passes,
9971                        num_zero_bitplanes: encoded.num_zero_bitplanes,
9972                    },
9973                })
9974            })
9975            .collect::<Result<Vec<_>, &'static str>>()?;
9976
9977        Ok(PreencodedHtj2k97Subband {
9978            sub_band_type: subband.sub_band_type,
9979            num_cbs_x: subband.num_cbs_x,
9980            num_cbs_y: subband.num_cbs_y,
9981            total_bitplanes: subband.total_bitplanes,
9982            code_blocks,
9983        })
9984    }
9985
9986    fn assert_htj2k_lossless_roundtrip(
9987        pixels: &[u8],
9988        width: u32,
9989        height: u32,
9990        bit_depth: u8,
9991        num_decomposition_levels: u8,
9992    ) {
9993        let codestream = encode_htj2k(
9994            pixels,
9995            width,
9996            height,
9997            1,
9998            bit_depth,
9999            false,
10000            &EncodeOptions {
10001                num_decomposition_levels,
10002                ..Default::default()
10003            },
10004        )
10005        .expect("HTJ2K encode");
10006
10007        assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10008        let cod_offset = codestream
10009            .windows(2)
10010            .position(|window| window == [0xFF, 0x52])
10011            .expect("COD marker");
10012        assert_eq!(codestream[cod_offset + 12], 0x40);
10013
10014        let image = Image::new(
10015            &codestream,
10016            &DecodeSettings {
10017                resolve_palette_indices: true,
10018                strict: true,
10019                target_resolution: None,
10020            },
10021        )
10022        .expect("parse HT codestream");
10023        let decoded = image.decode_native().expect("decode HT codestream");
10024
10025        assert_eq!(decoded.width, width);
10026        assert_eq!(decoded.height, height);
10027        assert_eq!(decoded.bit_depth, bit_depth);
10028        assert_eq!(decoded.data, pixels);
10029    }
10030
10031    fn gradient_u8(width: u32, height: u32) -> Vec<u8> {
10032        let mut pixels = Vec::with_capacity((width * height) as usize);
10033        for y in 0..height {
10034            for x in 0..width {
10035                pixels.push(((x * 17 + y * 31) % 256) as u8);
10036            }
10037        }
10038        pixels
10039    }
10040
10041    fn lossy_htj2k_roundtrip_u8(
10042        pixels: &[u8],
10043        width: u32,
10044        height: u32,
10045        num_decomposition_levels: u8,
10046    ) -> (Vec<u8>, usize) {
10047        let codestream = encode_htj2k(
10048            pixels,
10049            width,
10050            height,
10051            1,
10052            8,
10053            false,
10054            &EncodeOptions {
10055                num_decomposition_levels,
10056                reversible: false,
10057                guard_bits: 2,
10058                ..Default::default()
10059            },
10060        )
10061        .expect("lossy HT encode");
10062
10063        assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10064
10065        let image = Image::new(
10066            &codestream,
10067            &DecodeSettings {
10068                resolve_palette_indices: true,
10069                strict: true,
10070                target_resolution: None,
10071            },
10072        )
10073        .expect("parse lossy HT codestream");
10074        let decoded = image.decode_native().expect("decode lossy HT codestream");
10075
10076        assert_eq!(decoded.width, width);
10077        assert_eq!(decoded.height, height);
10078        assert_eq!(decoded.bit_depth, 8);
10079
10080        (decoded.data, codestream.len())
10081    }
10082
10083    fn max_abs_error(expected: &[u8], actual: &[u8]) -> u8 {
10084        expected
10085            .iter()
10086            .zip(actual)
10087            .map(|(&expected, &actual)| expected.abs_diff(actual))
10088            .max()
10089            .unwrap_or(0)
10090    }
10091
10092    fn psnr_db(expected: &[u8], actual: &[u8]) -> f64 {
10093        let mse = expected
10094            .iter()
10095            .zip(actual)
10096            .map(|(&expected, &actual)| {
10097                let diff = f64::from(expected) - f64::from(actual);
10098                diff * diff
10099            })
10100            .sum::<f64>()
10101            / expected.len() as f64;
10102
10103        if mse == 0.0 {
10104            f64::INFINITY
10105        } else {
10106            20.0 * 255.0f64.log10() - 10.0 * mse.log10()
10107        }
10108    }
10109
10110    fn assert_not_flat_128(decoded: &[u8]) {
10111        assert!(
10112            decoded.iter().any(|&sample| sample != 128),
10113            "lossy decode collapsed to flat 128"
10114        );
10115    }
10116
10117    #[test]
10118    fn test_encode_high_throughput_zero_image_roundtrip() {
10119        let width = 4u32;
10120        let height = 4u32;
10121        let sample = 2048u16.to_le_bytes();
10122        let mut pixels = Vec::with_capacity((width * height * 2) as usize);
10123        for _ in 0..(width * height) {
10124            pixels.extend_from_slice(&sample);
10125        }
10126
10127        let codestream = encode(
10128            &pixels,
10129            width,
10130            height,
10131            1,
10132            12,
10133            false,
10134            &EncodeOptions {
10135                num_decomposition_levels: 2,
10136                use_ht_block_coding: true,
10137                ..Default::default()
10138            },
10139        )
10140        .expect("HT all-zero encode");
10141
10142        assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10143        let cod_offset = codestream
10144            .windows(2)
10145            .position(|window| window == [0xFF, 0x52])
10146            .expect("COD marker");
10147        assert_eq!(codestream[cod_offset + 12], 0x40);
10148
10149        let image =
10150            Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10151        let decoded = image.decode_native().expect("decode HT codestream");
10152
10153        assert_eq!(decoded.width, width);
10154        assert_eq!(decoded.height, height);
10155        assert_eq!(decoded.bit_depth, 12);
10156        assert_eq!(decoded.data, pixels);
10157    }
10158
10159    #[test]
10160    fn test_encode_high_throughput_nonzero_roundtrip() {
10161        let width = 1u32;
10162        let height = 1u32;
10163        let pixels = 2049u16.to_le_bytes().to_vec();
10164
10165        let codestream = encode_htj2k(
10166            &pixels,
10167            width,
10168            height,
10169            1,
10170            12,
10171            false,
10172            &EncodeOptions {
10173                num_decomposition_levels: 0,
10174                ..Default::default()
10175            },
10176        )
10177        .expect("HT non-zero encode");
10178
10179        assert!(codestream.windows(2).any(|window| window == [0xFF, 0x50]));
10180        let image =
10181            Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10182        let decoded = image.decode_native().expect("decode HT codestream");
10183
10184        assert_eq!(decoded.width, width);
10185        assert_eq!(decoded.height, height);
10186        assert_eq!(decoded.bit_depth, 12);
10187        assert_eq!(decoded.data, pixels);
10188    }
10189
10190    #[test]
10191    fn test_encode_high_throughput_varied_12bit_roundtrip() {
10192        let mut pixels = Vec::with_capacity(32);
10193        for i in 0u16..16 {
10194            pixels.extend_from_slice(&((i * 257) & 0x0FFF).to_le_bytes());
10195        }
10196
10197        let codestream = encode_htj2k(
10198            &pixels,
10199            4,
10200            4,
10201            1,
10202            12,
10203            false,
10204            &EncodeOptions {
10205                num_decomposition_levels: 1,
10206                ..Default::default()
10207            },
10208        )
10209        .expect("HT varied encode");
10210
10211        let image =
10212            Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10213        let decoded = image.decode_native().expect("decode HT codestream");
10214
10215        assert_eq!(decoded.width, 4);
10216        assert_eq!(decoded.height, 4);
10217        assert_eq!(decoded.bit_depth, 12);
10218        assert_eq!(decoded.data, pixels);
10219    }
10220
10221    #[test]
10222    fn test_encode_high_throughput_gradient_8bit_roundtrip() {
10223        let pixels: Vec<u8> = (0..64).collect();
10224
10225        let codestream = encode_htj2k(
10226            &pixels,
10227            8,
10228            8,
10229            1,
10230            8,
10231            false,
10232            &EncodeOptions {
10233                num_decomposition_levels: 3,
10234                ..Default::default()
10235            },
10236        )
10237        .expect("HT gradient encode");
10238
10239        let image =
10240            Image::new(&codestream, &DecodeSettings::default()).expect("parse HT codestream");
10241        let decoded = image.decode_native().expect("decode HT codestream");
10242
10243        assert_eq!(decoded.width, 8);
10244        assert_eq!(decoded.height, 8);
10245        assert_eq!(decoded.bit_depth, 8);
10246        assert_eq!(decoded.data, pixels);
10247    }
10248
10249    #[test]
10250    fn test_encode_high_throughput_varied_12bit_large_roundtrip() {
10251        let width = 16u32;
10252        let height = 8u32;
10253        let mut pixels = Vec::with_capacity((width * height * 2) as usize);
10254        for y in 0u16..height as u16 {
10255            for x in 0u16..width as u16 {
10256                let value = (x * 257 + y * 17) & 0x0FFF;
10257                pixels.extend_from_slice(&value.to_le_bytes());
10258            }
10259        }
10260
10261        assert_htj2k_lossless_roundtrip(&pixels, width, height, 12, 4);
10262    }
10263
10264    #[test]
10265    fn test_encode_high_throughput_ramp_16bit_roundtrip() {
10266        let width = 48u32;
10267        let height = 24u32;
10268        let mut pixels = Vec::with_capacity((width * height * 2) as usize);
10269        for y in 0u16..height as u16 {
10270            for x in 0u16..width as u16 {
10271                let value = x * 521 + y * 997;
10272                pixels.extend_from_slice(&value.to_le_bytes());
10273            }
10274        }
10275
10276        assert_htj2k_lossless_roundtrip(&pixels, width, height, 16, 4);
10277    }
10278
10279    #[test]
10280    fn test_encode_high_throughput_lossy_large_gradient_is_parseable() {
10281        let pixels = gradient_u8(128, 128);
10282
10283        let (decoded, codestream_len) = lossy_htj2k_roundtrip_u8(&pixels, 128, 128, 5);
10284
10285        assert!(codestream_len > 110);
10286        assert_not_flat_128(&decoded);
10287        assert!(
10288            psnr_db(&pixels, &decoded) >= 30.0,
10289            "psnr={} max_abs={}",
10290            psnr_db(&pixels, &decoded),
10291            max_abs_error(&pixels, &decoded)
10292        );
10293    }
10294
10295    #[test]
10296    fn test_encode_high_throughput_lossy_constant_extremes_are_not_midgray() {
10297        for sample in [0u8, 255] {
10298            let pixels = vec![sample; 64 * 64];
10299            let (decoded, codestream_len) = lossy_htj2k_roundtrip_u8(&pixels, 64, 64, 4);
10300
10301            assert!(codestream_len > 110);
10302            assert_not_flat_128(&decoded);
10303            assert!(
10304                max_abs_error(&pixels, &decoded) <= 2,
10305                "sample={sample} max_abs={} decoded_min={} decoded_max={}",
10306                max_abs_error(&pixels, &decoded),
10307                decoded.iter().min().unwrap(),
10308                decoded.iter().max().unwrap()
10309            );
10310        }
10311    }
10312
10313    #[test]
10314    fn test_encode_invalid_dimensions() {
10315        let result = encode(&[], 0, 0, 1, 8, false, &EncodeOptions::default());
10316        assert!(result.is_err());
10317    }
10318
10319    #[test]
10320    fn test_encode_too_short() {
10321        let pixels = vec![0u8; 10]; // Way too short for 8x8
10322        let result = encode(&pixels, 8, 8, 1, 8, false, &EncodeOptions::default());
10323        assert!(result.is_err());
10324    }
10325
10326    #[test]
10327    fn test_deinterleave_rgb() {
10328        let pixels = vec![
10329            10u8, 20, 30, // pixel 0: R=10, G=20, B=30
10330            40, 50, 60, // pixel 1: R=40, G=50, B=60
10331        ];
10332        let comps = deinterleave_to_f32(&pixels, 2, 3, 8, false);
10333        assert_eq!(comps[0], vec![-118.0, -88.0]); // R
10334        assert_eq!(comps[1], vec![-108.0, -78.0]); // G
10335        assert_eq!(comps[2], vec![-98.0, -68.0]); // B
10336    }
10337
10338    #[test]
10339    fn deinterleave_rgb8_unsigned_fast_path_matches_generic_output() {
10340        let pixels = (0..96)
10341            .map(|value| ((value * 19 + value / 3) & 0xff) as u8)
10342            .collect::<Vec<_>>();
10343
10344        let expected = deinterleave_to_f32(&pixels, 32, 3, 8, false);
10345        let actual = deinterleave_rgb8_unsigned_to_f32(&pixels, 32);
10346
10347        assert_eq!(actual, expected);
10348    }
10349
10350    #[test]
10351    fn test_encode_decode_roundtrip_gray_8bit() {
10352        use crate::{DecodeSettings, Image};
10353
10354        // Constant image: all pixels = 42 — simplest possible test
10355        let original: Vec<u8> = vec![42u8; 64]; // 8x8, all same value
10356        let encoded = encode(
10357            &original,
10358            8,
10359            8,
10360            1,
10361            8,
10362            false,
10363            &EncodeOptions {
10364                num_decomposition_levels: 0,
10365                reversible: true,
10366                ..Default::default()
10367            },
10368        )
10369        .expect("encode failed");
10370
10371        let settings = DecodeSettings {
10372            resolve_palette_indices: false,
10373            strict: false,
10374            target_resolution: None,
10375        };
10376        let image = Image::new(&encoded, &settings).expect("parse failed");
10377        let decoded = image.decode_native().expect("decode failed");
10378
10379        assert_eq!(decoded.width, 8);
10380        assert_eq!(decoded.height, 8);
10381        assert_eq!(decoded.data, original, "round-trip mismatch");
10382    }
10383
10384    #[test]
10385    fn test_encode_decode_roundtrip_gray_8bit_single_dwt_level() {
10386        use crate::{DecodeSettings, Image};
10387
10388        let original: Vec<u8> = (0..64 * 64)
10389            .map(|value| ((value * 37 + value / 7) & 0xFF) as u8)
10390            .collect();
10391        let encoded = encode(
10392            &original,
10393            64,
10394            64,
10395            1,
10396            8,
10397            false,
10398            &EncodeOptions {
10399                num_decomposition_levels: 1,
10400                reversible: true,
10401                ..Default::default()
10402            },
10403        )
10404        .expect("encode failed");
10405
10406        let image = Image::new(&encoded, &DecodeSettings::default()).expect("parse failed");
10407        let decoded = image.decode_native().expect("decode failed");
10408
10409        assert_eq!(decoded.width, 64);
10410        assert_eq!(decoded.height, 64);
10411        assert_eq!(decoded.data, original, "round-trip mismatch");
10412    }
10413
10414    /// Precondition gate: native encode_htj2k must produce byte-identical output
10415    /// across repeated invocations with the same input before CUDA parity can be
10416    /// asserted.  96x80 with 3 components and 5 decomposition levels exercises
10417    /// multi-codeblock subbands.
10418    #[cfg(feature = "std")]
10419    #[test]
10420    fn encode_htj2k_is_byte_deterministic() {
10421        const WIDTH: u32 = 96;
10422        const HEIGHT: u32 = 80;
10423        const NUM_COMPONENTS: u8 = 3;
10424        const BIT_DEPTH: u8 = 8;
10425        const REPETITIONS: usize = 8;
10426
10427        // Deterministic pseudo-random pixel data: simple LCG-like sequence.
10428        let pixel_count = (WIDTH * HEIGHT) as usize * usize::from(NUM_COMPONENTS);
10429        let pixels: Vec<u8> = (0..pixel_count)
10430            .map(|i| {
10431                let v = i
10432                    .wrapping_mul(6364136223846793005)
10433                    .wrapping_add(1442695040888963407);
10434                (v >> 56) as u8
10435            })
10436            .collect();
10437
10438        let options = EncodeOptions {
10439            use_ht_block_coding: true,
10440            reversible: true,
10441            num_decomposition_levels: 5,
10442            validate_high_throughput_codestream: true,
10443            ..EncodeOptions::default()
10444        };
10445
10446        let baseline = encode_htj2k(
10447            &pixels,
10448            WIDTH,
10449            HEIGHT,
10450            NUM_COMPONENTS.into(),
10451            BIT_DEPTH,
10452            false,
10453            &options,
10454        )
10455        .expect("encode_htj2k baseline failed");
10456
10457        assert!(
10458            !baseline.is_empty(),
10459            "baseline codestream must not be empty"
10460        );
10461
10462        for i in 0..REPETITIONS {
10463            let result = encode_htj2k(
10464                &pixels,
10465                WIDTH,
10466                HEIGHT,
10467                NUM_COMPONENTS.into(),
10468                BIT_DEPTH,
10469                false,
10470                &options,
10471            )
10472            .unwrap_or_else(|e| panic!("encode_htj2k repetition {i} failed: {e}"));
10473            assert_eq!(
10474                result,
10475                baseline,
10476                "encode_htj2k repetition {i} produced different bytes \
10477                 (len baseline={}, len result={})",
10478                baseline.len(),
10479                result.len()
10480            );
10481        }
10482
10483        println!(
10484            "encode_htj2k_is_byte_deterministic: {} bytes, {} repetitions all identical",
10485            baseline.len(),
10486            REPETITIONS
10487        );
10488    }
10489
10490    /// Precondition gate: prove native encode_htj2k round-trips 2-component
10491    /// 8-bit lossless images exactly with independent component channels.
10492    #[cfg(feature = "std")]
10493    #[test]
10494    fn native_htj2k_roundtrips_two_component_lossless() {
10495        const WIDTH: u32 = 32;
10496        const HEIGHT: u32 = 24;
10497        const NUM_COMPONENTS: u8 = 2;
10498        const BIT_DEPTH: u8 = 8;
10499
10500        // Deterministic per-pixel pattern: each sample is a function of its
10501        // flat index so the two planes carry different, non-trivial data.
10502        let pixel_count = WIDTH as usize * HEIGHT as usize * usize::from(NUM_COMPONENTS);
10503        let pixels: Vec<u8> = (0..pixel_count)
10504            .map(|i| ((i.wrapping_mul(251).wrapping_add(i / 7)) & 0xFF) as u8)
10505            .collect();
10506
10507        let codestream = encode_htj2k(
10508            &pixels,
10509            WIDTH,
10510            HEIGHT,
10511            NUM_COMPONENTS.into(),
10512            BIT_DEPTH,
10513            false,
10514            &EncodeOptions::default(),
10515        )
10516        .expect("native 2-component HTJ2K encode failed");
10517
10518        let image = Image::new(
10519            &codestream,
10520            &DecodeSettings {
10521                resolve_palette_indices: true,
10522                strict: true,
10523                target_resolution: None,
10524            },
10525        )
10526        .expect("native 2-component HTJ2K parse failed");
10527        let decoded = image
10528            .decode_native()
10529            .expect("native 2-component HTJ2K decode failed");
10530
10531        assert_eq!(decoded.width, WIDTH, "width mismatch");
10532        assert_eq!(decoded.height, HEIGHT, "height mismatch");
10533        assert_eq!(decoded.bit_depth, BIT_DEPTH, "bit_depth mismatch");
10534        assert_eq!(
10535            decoded.num_components,
10536            u16::from(NUM_COMPONENTS),
10537            "component count mismatch"
10538        );
10539        assert_eq!(
10540            decoded.data, pixels,
10541            "2-component HTJ2K lossless round-trip mismatch"
10542        );
10543
10544        println!(
10545            "native_htj2k_roundtrips_two_component_lossless: {} bytes codestream, {} pixel bytes",
10546            codestream.len(),
10547            pixels.len()
10548        );
10549    }
10550
10551    /// Precondition gate: prove native encode_htj2k round-trips 4-component
10552    /// (e.g. RGBA) 8-bit lossless images exactly.
10553    /// Required before a CUDA parity oracle can be established for this component count.
10554    #[cfg(feature = "std")]
10555    #[test]
10556    fn native_htj2k_roundtrips_four_component_lossless() {
10557        const WIDTH: u32 = 32;
10558        const HEIGHT: u32 = 24;
10559        const NUM_COMPONENTS: u8 = 4;
10560        const BIT_DEPTH: u8 = 8;
10561
10562        // Deterministic per-sample pattern across all four planes.
10563        let pixel_count = WIDTH as usize * HEIGHT as usize * usize::from(NUM_COMPONENTS);
10564        let pixels: Vec<u8> = (0..pixel_count)
10565            .map(|i| ((i.wrapping_mul(197).wrapping_add(i / 13)) & 0xFF) as u8)
10566            .collect();
10567
10568        let codestream = encode_htj2k(
10569            &pixels,
10570            WIDTH,
10571            HEIGHT,
10572            NUM_COMPONENTS.into(),
10573            BIT_DEPTH,
10574            false,
10575            &EncodeOptions::default(),
10576        )
10577        .expect("native 4-component HTJ2K encode failed");
10578
10579        let image = Image::new(
10580            &codestream,
10581            &DecodeSettings {
10582                resolve_palette_indices: true,
10583                strict: true,
10584                target_resolution: None,
10585            },
10586        )
10587        .expect("native 4-component HTJ2K parse failed");
10588        let decoded = image
10589            .decode_native()
10590            .expect("native 4-component HTJ2K decode failed");
10591
10592        assert_eq!(decoded.width, WIDTH, "width mismatch");
10593        assert_eq!(decoded.height, HEIGHT, "height mismatch");
10594        assert_eq!(decoded.bit_depth, BIT_DEPTH, "bit_depth mismatch");
10595        assert_eq!(
10596            decoded.num_components,
10597            u16::from(NUM_COMPONENTS),
10598            "component count mismatch"
10599        );
10600        assert_eq!(
10601            decoded.data, pixels,
10602            "4-component HTJ2K lossless round-trip mismatch"
10603        );
10604
10605        println!(
10606            "native_htj2k_roundtrips_four_component_lossless: {} bytes codestream, {} pixel bytes",
10607            codestream.len(),
10608            pixels.len()
10609        );
10610    }
10611
10612    #[test]
10613    fn classic_pcrd_assigns_limited_budget_by_distortion_slope() {
10614        let candidates = vec![
10615            ClassicSegmentAssignmentCandidate {
10616                block_index: 0,
10617                segment_index: 0,
10618                rate: 500,
10619                distortion_delta: 500.0,
10620            },
10621            ClassicSegmentAssignmentCandidate {
10622                block_index: 1,
10623                segment_index: 0,
10624                rate: 700,
10625                distortion_delta: 7_000.0,
10626            },
10627            ClassicSegmentAssignmentCandidate {
10628                block_index: 2,
10629                segment_index: 0,
10630                rate: 600,
10631                distortion_delta: 3_000.0,
10632            },
10633        ];
10634
10635        let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 3_000])
10636            .expect("PCRD assignment");
10637
10638        assert_eq!(
10639            assignments,
10640            vec![1, 0, 1],
10641            "the highest slope contribution should consume the constrained first-layer budget"
10642        );
10643    }
10644
10645    #[test]
10646    fn classic_pcrd_allows_byte_target_tolerance_for_first_legal_truncation() {
10647        let candidates = vec![ClassicSegmentAssignmentCandidate {
10648            block_index: 0,
10649            segment_index: 0,
10650            rate: 300,
10651            distortion_delta: 1_000.0,
10652        }];
10653
10654        let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 1_000])
10655            .expect("PCRD assignment");
10656
10657        assert_eq!(assignments, vec![0]);
10658    }
10659
10660    #[test]
10661    fn classic_pcrd_does_not_spend_budget_on_non_prefix_segments() {
10662        let candidates = vec![
10663            ClassicSegmentAssignmentCandidate {
10664                block_index: 0,
10665                segment_index: 0,
10666                rate: 1_000,
10667                distortion_delta: 1_000.0,
10668            },
10669            ClassicSegmentAssignmentCandidate {
10670                block_index: 0,
10671                segment_index: 1,
10672                rate: 500,
10673                distortion_delta: 10_000.0,
10674            },
10675            ClassicSegmentAssignmentCandidate {
10676                block_index: 1,
10677                segment_index: 0,
10678                rate: 300,
10679                distortion_delta: 600.0,
10680            },
10681        ];
10682
10683        let assignments = assign_classic_segment_layers_by_slope(&candidates, 2, &[256, 2_000])
10684            .expect("PCRD assignment");
10685
10686        assert_eq!(
10687            assignments,
10688            vec![1, 1, 0],
10689            "first-layer budget must go to the best legal prefix contribution"
10690        );
10691    }
10692
10693    #[test]
10694    fn ht_layer_assignment_uses_segment_budget_before_block_index() {
10695        let candidates = vec![
10696            HtSegmentAssignmentCandidate {
10697                block_index: 0,
10698                segment_index: 0,
10699                rate: 900,
10700            },
10701            HtSegmentAssignmentCandidate {
10702                block_index: 1,
10703                segment_index: 0,
10704                rate: 200,
10705            },
10706            HtSegmentAssignmentCandidate {
10707                block_index: 2,
10708                segment_index: 0,
10709                rate: 200,
10710            },
10711        ];
10712
10713        let assignments = assign_ht_segment_layers_by_budget(&candidates, 2, &[256, 2_000])
10714            .expect("HTJ2K segment assignment");
10715
10716        assert_eq!(
10717            assignments,
10718            vec![1, 0, 0],
10719            "HTJ2K early layers should be filled by segment byte budget, not block index"
10720        );
10721    }
10722
10723    #[test]
10724    fn ht_layer_assignment_keeps_refinement_after_cleanup() {
10725        let candidates = vec![
10726            HtSegmentAssignmentCandidate {
10727                block_index: 0,
10728                segment_index: 0,
10729                rate: 200,
10730            },
10731            HtSegmentAssignmentCandidate {
10732                block_index: 0,
10733                segment_index: 1,
10734                rate: 50,
10735            },
10736        ];
10737
10738        let assignments = assign_ht_segment_layers_by_budget(&candidates, 2, &[256, 2_000])
10739            .expect("HTJ2K segment assignment");
10740
10741        assert_eq!(
10742            assignments,
10743            vec![0, 0],
10744            "a refinement segment may share the cleanup layer but must not precede it"
10745        );
10746    }
10747
10748    #[test]
10749    fn ht_layer_contributions_split_cleanup_and_refinement_across_layers() {
10750        let encoded = bitplane_encode::EncodedCodeBlock {
10751            data: vec![0x11, 0x22, 0x33, 0x44, 0x55],
10752            num_coding_passes: 3,
10753            num_zero_bitplanes: 2,
10754            ht_cleanup_length: 3,
10755            ht_refinement_length: 2,
10756        };
10757
10758        let contributions = ht_layer_contributions(encoded, 2, &[0, 1]).expect("split HT layers");
10759
10760        assert_eq!(contributions.len(), 2);
10761        assert_eq!(contributions[0].data, vec![0x11, 0x22, 0x33]);
10762        assert_eq!(contributions[0].ht_cleanup_length, 3);
10763        assert_eq!(contributions[0].ht_refinement_length, 0);
10764        assert_eq!(contributions[0].num_coding_passes, 1);
10765        assert_eq!(contributions[1].data, vec![0x44, 0x55]);
10766        assert_eq!(contributions[1].ht_cleanup_length, 0);
10767        assert_eq!(contributions[1].ht_refinement_length, 2);
10768        assert_eq!(contributions[1].num_coding_passes, 2);
10769    }
10770
10771    #[test]
10772    fn htj2k_lossy_quality_layers_decode_split_refinement_layer() {
10773        let width = 32;
10774        let height = 32;
10775        let pixels = gradient_u8(width, height);
10776        let codestream = encode_htj2k(
10777            &pixels,
10778            width,
10779            height,
10780            1,
10781            8,
10782            false,
10783            &EncodeOptions {
10784                num_decomposition_levels: 0,
10785                reversible: false,
10786                guard_bits: 2,
10787                num_layers: 2,
10788                ..Default::default()
10789            },
10790        )
10791        .expect("HTJ2K layered encode");
10792
10793        let image = Image::new(
10794            &codestream,
10795            &DecodeSettings {
10796                resolve_palette_indices: true,
10797                strict: true,
10798                target_resolution: None,
10799            },
10800        )
10801        .expect("parse layered HT codestream");
10802        let decoded = image.decode_native().expect("decode layered HT codestream");
10803
10804        assert_eq!(decoded.width, width);
10805        assert_eq!(decoded.height, height);
10806        assert_eq!(decoded.bit_depth, 8);
10807        assert_not_flat_128(&decoded.data);
10808        assert!(
10809            psnr_db(&pixels, &decoded.data) >= 30.0,
10810            "psnr={} max_abs={}",
10811            psnr_db(&pixels, &decoded.data),
10812            max_abs_error(&pixels, &decoded.data)
10813        );
10814    }
10815}