Skip to main content

j2k_transcode/
jpeg_to_htj2k.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Experimental JPEG DCT to HTJ2K codestream transcode entry point.
4
5use core::fmt;
6use std::time::Instant;
7
8use j2k::{
9    CpuOnlyJ2kEncodeStageAccelerator, J2kEncodeDispatchReport, J2kEncodeStageAccelerator,
10    J2kForwardDwt53Level, J2kForwardDwt53Output, J2kForwardDwt97Level, J2kForwardDwt97Output,
11    PrecomputedHtj2k53Component, PrecomputedHtj2k53Image, PrecomputedHtj2k97Component,
12    PrecomputedHtj2k97Image, PreencodedHtj2k97CompactComponent, PreencodedHtj2k97CompactImage,
13    PreencodedHtj2k97Component, PreencodedHtj2k97Image, PrequantizedHtj2k97Component,
14    PrequantizedHtj2k97Image,
15};
16use j2k_jpeg::transcode::{
17    extract_dct_blocks, idct_islow_block, DctExtractOptions, JpegDctComponent, JpegDctImage,
18};
19use j2k_native::{
20    encode_precomputed_htj2k_53_with_accelerator_and_max_host_bytes,
21    encode_precomputed_htj2k_97_batch_owned_with_accelerator_and_max_host_bytes,
22    encode_precomputed_htj2k_97_with_accelerator_and_max_host_bytes,
23    encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes,
24    encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes,
25    encode_prequantized_htj2k_97_with_accelerator_and_max_host_bytes,
26};
27use rayon::prelude::*;
28
29use crate::allocation::try_vec_with_capacity;
30use crate::dct53_2d::{
31    dct8x8_blocks_then_dwt53_float, dct8x8_blocks_to_dwt53_float_linear_with_scratch,
32    linearized_53_2d_from_plane, Dct53GridScratch,
33};
34use crate::dct97_2d::{
35    dct8x8_blocks_then_dwt97_float, dct8x8_blocks_then_dwt97_float_with_scratch,
36    linearized_97_2d_from_plane_with_scratch, Dct97GridScratch,
37};
38use crate::metrics::{error_metrics_i32_with_live_budget, ErrorMetrics, MetricsError};
39use crate::reversible53::{
40    reversible_lift_53_high_at_fallible, reversible_lift_53_i32, reversible_lift_53_low_at_fallible,
41};
42use crate::{
43    CpuOnlyDctToWaveletStageAccelerator, DctGridI16ToHtj2k97CodeBlockBatch,
44    DctGridI16ToHtj2k97CodeBlockJob, DctGridToDwt53Job, DctGridToDwt97Job,
45    DctGridToHtj2k97CodeBlockJob, DctGridToReversibleDwt53Job, DctToWaveletStageAccelerator,
46    DctTransformError, Dwt53TwoDimensional, Dwt97BatchStageTimings, Dwt97TwoDimensional,
47    Htj2k97CodeBlockOptions, ReversibleDwt53FirstLevel, TranscodeStageError,
48};
49
50mod options;
51pub use self::options::{
52    JpegToHtj2kCoefficientPath, JpegToHtj2kEncodeOptions, JpegToHtj2kOptions,
53    JPEG_TO_HTJ2K_LOSSY_97_QUANTIZATION_SCALE,
54};
55mod report;
56pub use self::report::{
57    BatchTranscodeReport, TranscodeBatchProfileRequest, TranscodeBatchProfileRow,
58    TranscodeComponentReport, TranscodeReport, TranscodeTimingReport,
59    TranscodeValidationClassification, TranscodeValidationMetrics,
60};
61mod error;
62use self::error::{dct53_transform_error, dct97_transform_error, map_encode_error};
63pub use self::error::{Htj2kEncodeError, Htj2kEncodeErrorKind, JpegToHtj2kError};
64mod validation;
65use self::validation::{
66    component_sampling_for_jpeg, decomposition_levels_for_components,
67    validate_component_block_grid, validate_transcode_options,
68};
69mod workspace;
70use self::workspace::validate_jpeg_transcode_workspace;
71mod scratch;
72use self::scratch::JpegToHtj2kScratch;
73mod output;
74pub use self::output::{EncodedTranscode, EncodedTranscodeBatch, JpegTileBatchInput};
75mod live_budget;
76use self::live_budget::{
77    encoded_transcode_retained_bytes, precomputed_batch_retained_bytes,
78    validation_metrics_retained_bytes, HostLiveBudget,
79};
80mod component_plan;
81use self::component_plan::{
82    integer_dct_job_for_component, transcode_component_batch, transcode_path_name,
83    ComponentBatchRequest, ComponentTranscodeBatch, PrecomputedComponentBatch,
84};
85mod component_groups;
86use self::component_groups::same_geometry_component_groups;
87mod float_reference;
88use self::float_reference::{
89    dct_blocks_to_8x8_f64, decompose_97_from_first_level, float97_reference_coefficients,
90    float_direct_97_wavelet_from_component, float_direct_wavelet_from_component,
91    float_reference_coefficients, ComponentWavelet, ComponentWavelet97,
92};
93mod float_output;
94use self::float_output::{
95    j2k_dwt97_from_wavelet, j2k_dwt_from_integer_wavelet, j2k_dwt_from_wavelet,
96    rounded_wavelet97_i32, rounded_wavelet_i32,
97};
98mod integer_reference;
99use self::integer_reference::{
100    flatten_integer_wavelet, integer_direct_wavelet_from_component, integer_reference_coefficients,
101    integer_wavelet_from_first_level, IntegerWavelet,
102};
103mod integer_storage;
104mod single;
105use self::single::jpeg_to_htj2k_with_scratch;
106mod single_tile_encode;
107use self::single_tile_encode::encode_component_batch;
108mod batch;
109pub use self::batch::jpeg_to_htj2k_batch;
110#[cfg(test)]
111use self::batch::{
112    encode_float97_prepared_tiles, store_compact_preencoded_component,
113    transform_float97_batch_tiles, Float97BatchTile,
114};
115use self::batch::{
116    jpeg_tile_batch_to_htj2k_with_scratch, record_accelerator_attempt, record_accelerator_dispatch,
117    record_batch_attempt, record_cpu_fallback, record_encode_dispatch_delta,
118};
119
120/// Reusable experimental JPEG-to-HTJ2K transcoder state.
121///
122/// Create one value per worker thread and reuse it across many tiles to keep
123/// scratch buffers allocated between calls. The scalar math and output are the
124/// same as [`jpeg_to_htj2k`].
125#[derive(Debug, Default)]
126pub struct JpegToHtj2kTranscoder {
127    scratch: JpegToHtj2kScratch,
128}
129
130impl JpegToHtj2kTranscoder {
131    /// Transcode a constrained baseline JPEG tile into HTJ2K using this
132    /// instance's reusable scratch buffers.
133    pub fn transcode(
134        &mut self,
135        bytes: &[u8],
136        options: &JpegToHtj2kOptions,
137    ) -> Result<EncodedTranscode, JpegToHtj2kError> {
138        let mut accelerator = CpuOnlyDctToWaveletStageAccelerator;
139        self.transcode_with_accelerator(bytes, options, &mut accelerator)
140    }
141
142    /// Transcode with an optional stage accelerator.
143    ///
144    /// Accelerators may handle direct DCT-grid projection stages and return
145    /// `None` for scalar fallback. Integer-direct 5/3 is offered in
146    /// same-geometry batches before falling back to per-component work.
147    pub fn transcode_with_accelerator<A: DctToWaveletStageAccelerator>(
148        &mut self,
149        bytes: &[u8],
150        options: &JpegToHtj2kOptions,
151        accelerator: &mut A,
152    ) -> Result<EncodedTranscode, JpegToHtj2kError> {
153        let mut encode_accelerator = CpuOnlyJ2kEncodeStageAccelerator;
154        self.transcode_with_accelerators(bytes, options, accelerator, &mut encode_accelerator)
155    }
156
157    /// Transcode with separate transform-stage and HTJ2K encode-stage
158    /// accelerators.
159    pub fn transcode_with_accelerators<
160        A: DctToWaveletStageAccelerator,
161        E: J2kEncodeStageAccelerator,
162    >(
163        &mut self,
164        bytes: &[u8],
165        options: &JpegToHtj2kOptions,
166        transform_accelerator: &mut A,
167        encode_accelerator: &mut E,
168    ) -> Result<EncodedTranscode, JpegToHtj2kError> {
169        jpeg_to_htj2k_with_scratch(
170            bytes,
171            options,
172            &mut self.scratch,
173            transform_accelerator,
174            encode_accelerator,
175            0,
176        )
177    }
178
179    /// Transcode many JPEG tiles, preserving per-tile failures in the returned
180    /// batch. Integer-direct 5/3 groups same-geometry components across tiles
181    /// before calling the accelerator.
182    pub fn transcode_batch(
183        &mut self,
184        tiles: &[JpegTileBatchInput<'_>],
185        options: &JpegToHtj2kOptions,
186    ) -> Result<EncodedTranscodeBatch, JpegToHtj2kError> {
187        let mut accelerator = CpuOnlyDctToWaveletStageAccelerator;
188        self.transcode_batch_with_accelerator(tiles, options, &mut accelerator)
189    }
190
191    /// Transcode many JPEG tiles with an optional stage accelerator.
192    pub fn transcode_batch_with_accelerator<A: DctToWaveletStageAccelerator>(
193        &mut self,
194        tiles: &[JpegTileBatchInput<'_>],
195        options: &JpegToHtj2kOptions,
196        accelerator: &mut A,
197    ) -> Result<EncodedTranscodeBatch, JpegToHtj2kError> {
198        let mut encode_accelerator = CpuOnlyJ2kEncodeStageAccelerator;
199        self.transcode_batch_with_accelerators(tiles, options, accelerator, &mut encode_accelerator)
200    }
201
202    /// Transcode many JPEG tiles with separate transform-stage and HTJ2K
203    /// encode-stage accelerators.
204    pub fn transcode_batch_with_accelerators<
205        A: DctToWaveletStageAccelerator,
206        E: J2kEncodeStageAccelerator,
207    >(
208        &mut self,
209        tiles: &[JpegTileBatchInput<'_>],
210        options: &JpegToHtj2kOptions,
211        transform_accelerator: &mut A,
212        encode_accelerator: &mut E,
213    ) -> Result<EncodedTranscodeBatch, JpegToHtj2kError> {
214        jpeg_tile_batch_to_htj2k_with_scratch(
215            tiles,
216            options,
217            &mut self.scratch,
218            transform_accelerator,
219            encode_accelerator,
220        )
221    }
222}
223
224/// Transcode a constrained baseline grayscale JPEG tile into an HTJ2K
225/// codestream using direct DCT-domain wavelet coefficients.
226///
227/// Current implementation scope is baseline JPEG with one or more components
228/// at native JPEG component resolution. Component subsampling is preserved
229/// through SIZ `XRsiz`/`YRsiz` instead of chroma upsampling.
230pub fn jpeg_to_htj2k(
231    bytes: &[u8],
232    options: &JpegToHtj2kOptions,
233) -> Result<EncodedTranscode, JpegToHtj2kError> {
234    JpegToHtj2kTranscoder::default().transcode(bytes, options)
235}
236
237#[cfg(test)]
238mod tests;