Skip to main content

j2k_types/dispatch/
accelerator.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Low-level encode-stage accelerator integration contract.
4
5use alloc::vec::Vec;
6
7use super::{J2kEncodeDispatchReport, J2kEncodeStageResult};
8use crate::{
9    EncodedHtJ2kCodeBlock, EncodedHtJ2kCodeBlockSet, EncodedJ2kCodeBlock, J2kForwardDwt53Job,
10    J2kForwardDwt53Output, J2kForwardDwt97Job, J2kForwardDwt97Output, J2kForwardIctJob,
11    J2kForwardRctJob, J2kHtCodeBlockEncodeJob, J2kHtCodeBlockSetEncodeJob, J2kHtSubbandEncodeJob,
12    J2kPacketizationEncodeJob, J2kPacketizationProgressionOrder, J2kQuantizeSubbandJob,
13    J2kResidentHtj2kTileEncodeJob, J2kTier1CodeBlockEncodeJob,
14};
15
16/// Pixel deinterleave and level-shift job supplied to an accelerator.
17#[derive(Debug, Clone, Copy)]
18pub struct J2kDeinterleaveToF32Job<'a> {
19    /// Interleaved source pixel bytes.
20    pub pixels: &'a [u8],
21    /// Number of pixels to convert.
22    pub num_pixels: usize,
23    /// Number of interleaved components per pixel.
24    pub num_components: u16,
25    /// Source sample bit depth.
26    pub bit_depth: u8,
27    /// Whether source samples are signed.
28    pub signed: bool,
29}
30
31/// Combined pixel deinterleave, level-shift, and forward MCT job supplied to an accelerator.
32///
33/// The native encoder only offers this job for three-component inputs with MCT enabled.
34#[derive(Debug, Clone, Copy)]
35pub struct J2kDeinterleaveMctToF32Job<'a> {
36    /// Interleaved source pixel bytes.
37    pub pixels: &'a [u8],
38    /// Number of pixels to convert.
39    pub num_pixels: usize,
40    /// Source sample bit depth.
41    pub bit_depth: u8,
42    /// Whether source samples are signed.
43    pub signed: bool,
44    /// Whether to apply the reversible RCT (`true`) or irreversible ICT (`false`).
45    pub reversible: bool,
46}
47
48/// Validated image and coding context supplied before encode-stage dispatch.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub struct J2kEncodeContext {
51    /// Number of pixels in the encoded image or tile.
52    pub num_pixels: usize,
53    /// Number of interleaved source components.
54    pub num_components: u16,
55    /// Source sample bit depth.
56    pub bit_depth: u8,
57    /// Whether source samples are signed.
58    pub signed: bool,
59    /// Whether the codestream uses reversible coding.
60    pub reversible: bool,
61}
62
63/// HTJ2K tile-body encode job for a backend-resident full-tile path.
64#[derive(Debug, Clone, Copy)]
65pub struct J2kHtj2kTileEncodeJob<'a> {
66    /// Interleaved source pixel bytes.
67    pub pixels: &'a [u8],
68    /// Tile/image width in samples.
69    pub width: u32,
70    /// Tile/image height in samples.
71    pub height: u32,
72    /// Number of interleaved image components.
73    pub num_components: u16,
74    /// Source component bit depth.
75    pub bit_depth: u8,
76    /// Whether source samples are signed.
77    pub signed: bool,
78    /// Number of DWT decomposition levels.
79    pub num_decomposition_levels: u8,
80    /// Whether the codestream uses reversible coding.
81    pub reversible: bool,
82    /// Whether a multi-component transform should be applied.
83    pub use_mct: bool,
84    /// JPEG 2000 guard bits used to derive total coded bitplanes.
85    pub guard_bits: u8,
86    /// Code-block width in samples.
87    pub code_block_width: u32,
88    /// Code-block height in samples.
89    pub code_block_height: u32,
90    /// Packet progression order to emit.
91    pub progression_order: J2kPacketizationProgressionOrder,
92    /// Per-component sampling factors, as `(x_rsiz, y_rsiz)`.
93    pub component_sampling: &'a [(u8, u8)],
94    /// Quantization step sizes, as `(exponent, mantissa)`, in codestream order.
95    pub quantization_steps: &'a [(u16, u16)],
96}
97
98/// CPU-only encode accelerator that always falls back to native stages.
99#[derive(Debug, Default, Clone, Copy)]
100pub struct CpuOnlyJ2kEncodeStageAccelerator;
101
102/// Low-level JPEG 2000 encode-stage accelerator integration contract.
103pub trait J2kEncodeStageAccelerator {
104    /// Supply validated context before any encode-stage hook is invoked.
105    fn begin_encode(&mut self, _context: J2kEncodeContext) -> J2kEncodeStageResult<()> {
106        Ok(())
107    }
108
109    /// Report cumulative backend dispatches completed by this accelerator.
110    fn dispatch_report(&self) -> J2kEncodeDispatchReport {
111        J2kEncodeDispatchReport::default()
112    }
113
114    /// Report the exact maximum cleanup magnitude from the latest fused HT subband encode.
115    fn ht_subband_maximum_cleanup_magnitude(&self) -> Option<u64> {
116        None
117    }
118
119    /// Report the exact Part 15 magnitude bound from the latest complete HT tile encode.
120    fn ht_tile_required_magnitude_bound(&self) -> Option<u8> {
121        None
122    }
123
124    /// Optionally deinterleave interleaved pixel bytes into f32 component planes.
125    fn encode_deinterleave(
126        &mut self,
127        _job: J2kDeinterleaveToF32Job<'_>,
128    ) -> J2kEncodeStageResult<Option<Vec<Vec<f32>>>> {
129        Ok(None)
130    }
131
132    /// Optionally combine three-component deinterleave, level shift, and forward MCT.
133    fn encode_deinterleave_mct(
134        &mut self,
135        _job: J2kDeinterleaveMctToF32Job<'_>,
136    ) -> J2kEncodeStageResult<Option<Vec<Vec<f32>>>> {
137        Ok(None)
138    }
139
140    /// Optionally apply forward RCT in place.
141    fn encode_forward_rct(&mut self, _job: J2kForwardRctJob<'_>) -> J2kEncodeStageResult<bool> {
142        Ok(false)
143    }
144
145    /// Optionally apply forward ICT in place.
146    fn encode_forward_ict(&mut self, _job: J2kForwardIctJob<'_>) -> J2kEncodeStageResult<bool> {
147        Ok(false)
148    }
149
150    /// Optionally run a forward reversible 5/3 DWT.
151    fn encode_forward_dwt53(
152        &mut self,
153        _job: J2kForwardDwt53Job<'_>,
154    ) -> J2kEncodeStageResult<Option<J2kForwardDwt53Output>> {
155        Ok(None)
156    }
157
158    /// Optionally run reversible DWT at an absolute tile-component origin.
159    ///
160    /// The default delegates to the existing hook only when every level has
161    /// the same phase as a zero-origin transform. Otherwise it declines the
162    /// stage, so an older accelerator cannot silently transform the wrong phase.
163    fn encode_forward_dwt53_at_origin(
164        &mut self,
165        job: J2kForwardDwt53Job<'_>,
166        origin: (u32, u32),
167    ) -> J2kEncodeStageResult<Option<J2kForwardDwt53Output>> {
168        if zero_origin_dwt_phase(origin, job.num_levels) {
169            self.encode_forward_dwt53(job)
170        } else {
171            Ok(None)
172        }
173    }
174
175    /// Optionally run a forward irreversible 9/7 DWT.
176    fn encode_forward_dwt97(
177        &mut self,
178        _job: J2kForwardDwt97Job<'_>,
179    ) -> J2kEncodeStageResult<Option<J2kForwardDwt97Output>> {
180        Ok(None)
181    }
182
183    /// Optionally run irreversible DWT at an absolute tile-component origin.
184    /// The default preserves the zero-origin hook's phase contract, as for 5/3.
185    fn encode_forward_dwt97_at_origin(
186        &mut self,
187        job: J2kForwardDwt97Job<'_>,
188        origin: (u32, u32),
189    ) -> J2kEncodeStageResult<Option<J2kForwardDwt97Output>> {
190        if zero_origin_dwt_phase(origin, job.num_levels) {
191            self.encode_forward_dwt97(job)
192        } else {
193            Ok(None)
194        }
195    }
196
197    /// Optionally quantize one subband.
198    fn encode_quantize_subband(
199        &mut self,
200        _job: J2kQuantizeSubbandJob<'_>,
201    ) -> J2kEncodeStageResult<Option<Vec<i32>>> {
202        Ok(None)
203    }
204
205    /// Optionally encode one classic Tier-1 code block.
206    fn encode_tier1_code_block(
207        &mut self,
208        _job: J2kTier1CodeBlockEncodeJob<'_>,
209    ) -> J2kEncodeStageResult<Option<EncodedJ2kCodeBlock>> {
210        Ok(None)
211    }
212
213    /// Optionally encode multiple classic Tier-1 code blocks in one backend dispatch.
214    fn encode_tier1_code_blocks(
215        &mut self,
216        _jobs: &[J2kTier1CodeBlockEncodeJob<'_>],
217    ) -> J2kEncodeStageResult<Option<Vec<EncodedJ2kCodeBlock>>> {
218        Ok(None)
219    }
220
221    /// Optionally encode one HTJ2K code block.
222    fn encode_ht_code_block(
223        &mut self,
224        _job: J2kHtCodeBlockEncodeJob<'_>,
225    ) -> J2kEncodeStageResult<Option<EncodedHtJ2kCodeBlock>> {
226        Ok(None)
227    }
228
229    /// Optionally encode multiple HTJ2K code blocks in one backend dispatch.
230    fn encode_ht_code_blocks(
231        &mut self,
232        _jobs: &[J2kHtCodeBlockEncodeJob<'_>],
233    ) -> J2kEncodeStageResult<Option<Vec<EncodedHtJ2kCodeBlock>>> {
234        Ok(None)
235    }
236
237    /// Optionally encode exact HT cleanup/refinement-set candidates in one dispatch.
238    #[doc(hidden)]
239    fn encode_ht_code_block_sets(
240        &mut self,
241        _jobs: &[J2kHtCodeBlockSetEncodeJob<'_>],
242    ) -> J2kEncodeStageResult<Option<Vec<EncodedHtJ2kCodeBlockSet>>> {
243        Ok(None)
244    }
245
246    /// Optionally quantize and encode one HTJ2K cleanup/refinement subband.
247    fn encode_ht_subband(
248        &mut self,
249        _job: J2kHtSubbandEncodeJob<'_>,
250    ) -> J2kEncodeStageResult<Option<Vec<EncodedHtJ2kCodeBlock>>> {
251        Ok(None)
252    }
253
254    /// Optionally encode the complete HTJ2K tile packet body.
255    fn encode_htj2k_tile(
256        &mut self,
257        _job: J2kHtj2kTileEncodeJob<'_>,
258    ) -> J2kEncodeStageResult<Option<Vec<u8>>> {
259        Ok(None)
260    }
261
262    /// Optionally encode a complete HTJ2K tile whose pixels remain backend-resident.
263    fn encode_resident_htj2k_tile(
264        &mut self,
265        _job: J2kResidentHtj2kTileEncodeJob<'_>,
266    ) -> J2kEncodeStageResult<Option<Vec<u8>>> {
267        Ok(None)
268    }
269
270    /// Return whether CPU code-block fallback should use internal rayon parallelism.
271    fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
272        false
273    }
274
275    /// Return whether callers may parallelize whole-tile CPU-only batch encode.
276    fn prefer_parallel_cpu_tile_encode(&self) -> bool {
277        false
278    }
279
280    /// Whether native encode may skip the remaining per-stage hooks for the
281    /// current encode. Native encode asks after `begin_encode` and after the
282    /// whole-tile hooks have declined; `true` lets it skip those hooks and the
283    /// inputs only they read, and fuse CPU stages. Return `false`, the default,
284    /// unless every remaining hook would decline.
285    fn declines_every_stage(&self) -> bool {
286        false
287    }
288
289    /// Optionally packetize prepared packet contributions.
290    fn encode_packetization(
291        &mut self,
292        _job: J2kPacketizationEncodeJob<'_>,
293    ) -> J2kEncodeStageResult<Option<Vec<u8>>> {
294        Ok(None)
295    }
296}
297
298#[doc(hidden)]
299impl J2kEncodeStageAccelerator for CpuOnlyJ2kEncodeStageAccelerator {
300    fn prefer_parallel_cpu_code_block_fallback(&self) -> bool {
301        true
302    }
303
304    fn prefer_parallel_cpu_tile_encode(&self) -> bool {
305        true
306    }
307
308    fn declines_every_stage(&self) -> bool {
309        true
310    }
311}
312
313fn zero_origin_dwt_phase(origin: (u32, u32), levels: u8) -> bool {
314    1_u64.checked_shl(u32::from(levels)).is_some_and(|scale| {
315        u64::from(origin.0).is_multiple_of(scale) && u64::from(origin.1).is_multiple_of(scale)
316    })
317}
318
319#[cfg(test)]
320mod tests {
321    use super::{CpuOnlyJ2kEncodeStageAccelerator, J2kEncodeStageAccelerator};
322    use crate::J2kHtCodeBlockSetEncodeJob;
323
324    #[test]
325    fn legacy_accelerators_decline_explicit_ht_sets_by_default() {
326        let coefficients = [7_i32];
327        let jobs = [J2kHtCodeBlockSetEncodeJob {
328            coefficients: &coefficients,
329            width: 1,
330            height: 1,
331            total_bitplanes: 3,
332            cleanup_bitplane: 1,
333            target_coding_passes: 3,
334        }];
335        let mut accelerator = CpuOnlyJ2kEncodeStageAccelerator;
336
337        assert!(accelerator
338            .encode_ht_code_block_sets(&jobs)
339            .expect("default set hook")
340            .is_none());
341    }
342}