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