Skip to main content

j2k_core/
traits.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use alloc::vec::Vec;
4
5use crate::{
6    accelerator::{DeviceMemoryRange, ExecutionStats, SurfaceResidency},
7    backend::{BackendKind, BackendRequest},
8    batch::{TileRegionScaledDecodeJob, TileRegionScaledDeviceDecodeRequest},
9    context::CodecContext,
10    error::CodecError,
11    pixel::PixelFormat,
12    row_sink::RowSink,
13    sample::Sample,
14    scale::Downscale,
15    scratch::ScratchPool,
16    types::{DecodeOutcome, Info, Rect},
17};
18
19/// Error wrapper used by row-streaming decode when either the codec or the
20/// caller-provided row sink can fail.
21#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
22pub enum DecodeRowsError<D, E>
23where
24    D: core::error::Error + 'static,
25    E: core::error::Error + 'static,
26{
27    #[error(transparent)]
28    /// Codec decode failure.
29    Decode(D),
30    #[error(transparent)]
31    /// Caller-provided row sink failure.
32    Sink(E),
33}
34
35/// Common associated types shared by image codecs.
36pub trait ImageCodec {
37    /// Codec-specific error type.
38    type Error: CodecError;
39    /// Non-fatal warning type returned in successful decode outcomes.
40    type Warning: core::fmt::Debug + core::fmt::Display + Send + Sync + 'static;
41    /// Caller-owned scratch pool type used to reuse allocations.
42    type Pool: ScratchPool;
43}
44
45/// Decoded image data resident on a specific backend.
46pub trait DeviceSurface {
47    /// Backend that owns or produced the surface.
48    fn backend_kind(&self) -> BackendKind;
49    /// Memory residency of the surface.
50    fn residency(&self) -> SurfaceResidency {
51        SurfaceResidency::for_backend(self.backend_kind())
52    }
53    /// Surface dimensions in pixels.
54    fn dimensions(&self) -> (u32, u32);
55    /// Pixel format stored by the surface.
56    fn pixel_format(&self) -> PixelFormat;
57    /// Number of bytes represented by the surface.
58    fn byte_len(&self) -> usize;
59    /// Execution statistics attached to the surface.
60    fn execution_stats(&self) -> ExecutionStats {
61        ExecutionStats::default()
62    }
63    /// Backend-visible memory range, when the backend can expose one safely.
64    fn memory_range(&self) -> Option<DeviceMemoryRange> {
65        None
66    }
67}
68
69/// Completed codestream bytes resident in backend-visible memory.
70pub trait DeviceCodestream {
71    /// Backend-visible range covering the codestream capacity.
72    fn codestream_memory_range(&self) -> Option<DeviceMemoryRange>;
73    /// Total byte length of the backing allocation, when known.
74    fn codestream_allocation_len(&self) -> Option<usize>;
75    /// Number of valid codestream bytes.
76    fn codestream_byte_len(&self) -> usize;
77    /// Writable capacity beginning at the memory range offset.
78    fn codestream_capacity(&self) -> usize;
79}
80
81/// Submitted device decode operation that can be waited on for completion.
82pub trait DeviceSubmission {
83    /// Completed output type.
84    type Output;
85    /// Submission or decode error type.
86    type Error;
87
88    /// Wait for the submission and return its output.
89    ///
90    /// # Errors
91    ///
92    /// Returns [`Self::Error`] if submission or device execution fails.
93    fn wait(self) -> Result<Self::Output, Self::Error>;
94}
95
96/// Already-completed submission used by synchronous fallback paths.
97#[derive(Debug)]
98#[doc(hidden)]
99pub struct ReadySubmission<T, E>(Result<T, E>);
100
101impl<T, E> ReadySubmission<T, E> {
102    /// Wrap an immediate result as a submission.
103    pub fn from_result(result: Result<T, E>) -> Self {
104        Self(result)
105    }
106}
107
108impl<T, E> DeviceSubmission for ReadySubmission<T, E> {
109    type Output = T;
110    type Error = E;
111
112    fn wait(self) -> Result<Self::Output, Self::Error> {
113        self.0
114    }
115}
116
117/// Mutable device session that tracks submitted backend work.
118#[doc(hidden)]
119pub trait DeviceSubmitSession {
120    /// Record a submitted device operation.
121    fn record_submit(&mut self);
122}
123
124/// Record a device submission and wrap an immediate result as ready.
125#[doc(hidden)]
126pub fn submit_ready_device<S, T, E>(
127    session: &mut S,
128    submit: impl FnOnce(&mut S) -> Result<T, E>,
129) -> ReadySubmission<T, E>
130where
131    S: DeviceSubmitSession + ?Sized,
132{
133    session.record_submit();
134    ReadySubmission::from_result(submit(session))
135}
136
137/// Borrowed-image decode API for codecs that parse compressed bytes directly.
138pub trait ImageDecode<'a>: ImageCodec + Sized + 'a {
139    /// Borrowed parse product that can later construct a decoder.
140    type View: 'a;
141
142    /// Inspect metadata without decoding pixels.
143    ///
144    /// # Errors
145    ///
146    /// Returns the codec-specific [`ImageCodec::Error`] when the compressed input is invalid or
147    /// unsupported.
148    fn inspect(input: &'a [u8]) -> Result<Info, Self::Error>;
149    /// Parse compressed bytes into a borrowed view.
150    ///
151    /// # Errors
152    ///
153    /// Returns the codec-specific [`ImageCodec::Error`] when the compressed input cannot be
154    /// parsed.
155    fn parse(input: &'a [u8]) -> Result<Self::View, Self::Error>;
156    /// Build a decoder from a parsed view.
157    ///
158    /// # Errors
159    ///
160    /// Returns the codec-specific [`ImageCodec::Error`] when the parsed view is unsupported or
161    /// inconsistent.
162    fn from_view(view: Self::View) -> Result<Self, Self::Error>;
163
164    /// Decode the full image into caller-owned output.
165    ///
166    /// # Errors
167    ///
168    /// Returns the codec-specific [`ImageCodec::Error`] for invalid input, unsupported output, or
169    /// an undersized or invalid output layout.
170    fn decode_into(
171        &mut self,
172        out: &mut [u8],
173        stride: usize,
174        fmt: PixelFormat,
175    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
176
177    /// Decode the full image into caller-owned output with reusable scratch.
178    ///
179    /// # Errors
180    ///
181    /// Returns the codec-specific [`ImageCodec::Error`] for invalid input, unsupported output,
182    /// scratch failure, or an invalid output layout.
183    fn decode_into_with_scratch(
184        &mut self,
185        pool: &mut Self::Pool,
186        out: &mut [u8],
187        stride: usize,
188        fmt: PixelFormat,
189    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
190
191    /// Decode a source-coordinate region into caller-owned output.
192    ///
193    /// # Errors
194    ///
195    /// Returns the codec-specific [`ImageCodec::Error`] when the input, region, output layout, or
196    /// scratch state cannot be decoded.
197    fn decode_region_into(
198        &mut self,
199        pool: &mut Self::Pool,
200        out: &mut [u8],
201        stride: usize,
202        fmt: PixelFormat,
203        roi: Rect,
204    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
205
206    /// Decode the full image at reduced resolution into caller-owned output.
207    ///
208    /// # Errors
209    ///
210    /// Returns the codec-specific [`ImageCodec::Error`] when the input, scale, output layout, or
211    /// scratch state cannot be decoded.
212    fn decode_scaled_into(
213        &mut self,
214        pool: &mut Self::Pool,
215        out: &mut [u8],
216        stride: usize,
217        fmt: PixelFormat,
218        scale: Downscale,
219    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
220
221    /// Decode a source-coordinate region at reduced resolution into caller-owned output.
222    ///
223    /// # Errors
224    ///
225    /// Returns the codec-specific [`ImageCodec::Error`] when the input, region, scale, output
226    /// layout, or scratch state cannot be decoded.
227    fn decode_region_scaled_into(
228        &mut self,
229        pool: &mut Self::Pool,
230        out: &mut [u8],
231        stride: usize,
232        fmt: PixelFormat,
233        roi: Rect,
234        scale: Downscale,
235    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
236}
237
238/// Adapter hook for decoders whose host-output path delegates to a CPU decoder.
239///
240/// GPU adapter crates implement this trait when their public decoder wraps a
241/// CPU decoder for host output but has backend-specific device submission
242/// methods. The blanket [`ImageDecode`] impl below keeps the CPU-host
243/// delegation in one place.
244#[doc(hidden)]
245pub trait CpuBackedImageDecode<'a>: ImageCodec + Sized + 'a {
246    /// CPU decoder that owns the host-output implementation.
247    type Cpu: ImageDecode<'a, Pool = Self::Pool>;
248    /// Borrowed parse product used by this adapter.
249    type View: 'a;
250
251    /// Inspect metadata through the CPU codec and map it to core info.
252    fn inspect_cpu(input: &'a [u8]) -> Result<Info, Self::Error>;
253    /// Parse compressed bytes through the CPU codec or adapter view.
254    fn parse_cpu(input: &'a [u8]) -> Result<Self::View, Self::Error>;
255    /// Build this adapter from a parsed CPU view.
256    fn from_cpu_view(view: Self::View) -> Result<Self, Self::Error>;
257    /// Borrow the wrapped CPU decoder mutably.
258    fn cpu_decoder_mut(&mut self) -> &mut Self::Cpu;
259    /// Convert a CPU decode outcome into this adapter's warning type.
260    fn map_cpu_outcome(
261        outcome: DecodeOutcome<<Self::Cpu as ImageCodec>::Warning>,
262    ) -> DecodeOutcome<Self::Warning>;
263}
264
265#[doc(hidden)]
266impl<'a, T> ImageDecode<'a> for T
267where
268    T: CpuBackedImageDecode<'a>,
269    <T::Cpu as ImageCodec>::Error: Into<T::Error>,
270{
271    type View = T::View;
272
273    fn inspect(input: &'a [u8]) -> Result<Info, Self::Error> {
274        T::inspect_cpu(input)
275    }
276
277    fn parse(input: &'a [u8]) -> Result<Self::View, Self::Error> {
278        T::parse_cpu(input)
279    }
280
281    fn from_view(view: Self::View) -> Result<Self, Self::Error> {
282        T::from_cpu_view(view)
283    }
284
285    fn decode_into(
286        &mut self,
287        out: &mut [u8],
288        stride: usize,
289        fmt: PixelFormat,
290    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
291        let outcome = self
292            .cpu_decoder_mut()
293            .decode_into(out, stride, fmt)
294            .map_err(Into::into)?;
295        Ok(T::map_cpu_outcome(outcome))
296    }
297
298    fn decode_into_with_scratch(
299        &mut self,
300        pool: &mut Self::Pool,
301        out: &mut [u8],
302        stride: usize,
303        fmt: PixelFormat,
304    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
305        let outcome = self
306            .cpu_decoder_mut()
307            .decode_into_with_scratch(pool, out, stride, fmt)
308            .map_err(Into::into)?;
309        Ok(T::map_cpu_outcome(outcome))
310    }
311
312    fn decode_region_into(
313        &mut self,
314        pool: &mut Self::Pool,
315        out: &mut [u8],
316        stride: usize,
317        fmt: PixelFormat,
318        roi: Rect,
319    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
320        let outcome = self
321            .cpu_decoder_mut()
322            .decode_region_into(pool, out, stride, fmt, roi)
323            .map_err(Into::into)?;
324        Ok(T::map_cpu_outcome(outcome))
325    }
326
327    fn decode_scaled_into(
328        &mut self,
329        pool: &mut Self::Pool,
330        out: &mut [u8],
331        stride: usize,
332        fmt: PixelFormat,
333        scale: Downscale,
334    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
335        let outcome = self
336            .cpu_decoder_mut()
337            .decode_scaled_into(pool, out, stride, fmt, scale)
338            .map_err(Into::into)?;
339        Ok(T::map_cpu_outcome(outcome))
340    }
341
342    fn decode_region_scaled_into(
343        &mut self,
344        pool: &mut Self::Pool,
345        out: &mut [u8],
346        stride: usize,
347        fmt: PixelFormat,
348        roi: Rect,
349        scale: Downscale,
350    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
351        let outcome = self
352            .cpu_decoder_mut()
353            .decode_region_scaled_into(pool, out, stride, fmt, roi, scale)
354            .map_err(Into::into)?;
355        Ok(T::map_cpu_outcome(outcome))
356    }
357}
358
359/// Decode API for implementations that can submit work to a device backend.
360pub trait ImageDecodeSubmit<'a>: ImageDecode<'a> {
361    /// Mutable session state shared across submissions.
362    type Session: Default + Send;
363    /// Device surface returned by completed submissions.
364    type DeviceSurface: DeviceSurface;
365    /// Submission handle type.
366    type SubmittedSurface: DeviceSubmission<Output = Self::DeviceSurface, Error = Self::Error>;
367
368    /// Submit full-image decode to the requested backend.
369    ///
370    /// # Errors
371    ///
372    /// Returns the codec-specific [`ImageCodec::Error`] when the request is invalid, unsupported,
373    /// or cannot be submitted.
374    fn submit_to_device(
375        &mut self,
376        session: &mut Self::Session,
377        fmt: PixelFormat,
378        backend: BackendRequest,
379    ) -> Result<Self::SubmittedSurface, Self::Error>;
380
381    /// Submit region decode to the requested backend.
382    ///
383    /// # Errors
384    ///
385    /// Returns the codec-specific [`ImageCodec::Error`] when the region or backend request is
386    /// invalid, unsupported, or cannot be submitted.
387    fn submit_region_to_device(
388        &mut self,
389        session: &mut Self::Session,
390        fmt: PixelFormat,
391        roi: Rect,
392        backend: BackendRequest,
393    ) -> Result<Self::SubmittedSurface, Self::Error>;
394
395    /// Submit reduced-resolution decode to the requested backend.
396    ///
397    /// # Errors
398    ///
399    /// Returns the codec-specific [`ImageCodec::Error`] when the scale or backend request is
400    /// invalid, unsupported, or cannot be submitted.
401    fn submit_scaled_to_device(
402        &mut self,
403        session: &mut Self::Session,
404        fmt: PixelFormat,
405        scale: Downscale,
406        backend: BackendRequest,
407    ) -> Result<Self::SubmittedSurface, Self::Error>;
408
409    /// Submit region decode at reduced resolution to the requested backend.
410    ///
411    /// # Errors
412    ///
413    /// Returns the codec-specific [`ImageCodec::Error`] when the region, scale, or backend request
414    /// is invalid, unsupported, or cannot be submitted.
415    fn submit_region_scaled_to_device(
416        &mut self,
417        session: &mut Self::Session,
418        fmt: PixelFormat,
419        roi: Rect,
420        scale: Downscale,
421        backend: BackendRequest,
422    ) -> Result<Self::SubmittedSurface, Self::Error>;
423}
424
425/// Synchronous device-output decode API.
426pub trait ImageDecodeDevice<'a>: ImageDecode<'a> {
427    /// Device surface returned by decode calls.
428    type DeviceSurface: DeviceSurface;
429
430    /// Decode the full image to the requested backend.
431    ///
432    /// # Errors
433    ///
434    /// Returns the codec-specific [`ImageCodec::Error`] if submission or device execution fails.
435    fn decode_to_device(
436        &mut self,
437        fmt: PixelFormat,
438        backend: BackendRequest,
439    ) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
440    where
441        Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
442    {
443        let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
444        <Self as ImageDecodeSubmit<'a>>::submit_to_device(self, &mut session, fmt, backend)?.wait()
445    }
446
447    /// Decode a source-coordinate region to the requested backend.
448    ///
449    /// # Errors
450    ///
451    /// Returns the codec-specific [`ImageCodec::Error`] if the region is invalid or submission or
452    /// device execution fails.
453    fn decode_region_to_device(
454        &mut self,
455        fmt: PixelFormat,
456        roi: Rect,
457        backend: BackendRequest,
458    ) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
459    where
460        Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
461    {
462        let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
463        <Self as ImageDecodeSubmit<'a>>::submit_region_to_device(
464            self,
465            &mut session,
466            fmt,
467            roi,
468            backend,
469        )?
470        .wait()
471    }
472
473    /// Decode the full image at reduced resolution to the requested backend.
474    ///
475    /// # Errors
476    ///
477    /// Returns the codec-specific [`ImageCodec::Error`] if the scale is unsupported or submission
478    /// or device execution fails.
479    fn decode_scaled_to_device(
480        &mut self,
481        fmt: PixelFormat,
482        scale: Downscale,
483        backend: BackendRequest,
484    ) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
485    where
486        Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
487    {
488        let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
489        <Self as ImageDecodeSubmit<'a>>::submit_scaled_to_device(
490            self,
491            &mut session,
492            fmt,
493            scale,
494            backend,
495        )?
496        .wait()
497    }
498
499    /// Decode a source-coordinate region at reduced resolution to the requested backend.
500    ///
501    /// # Errors
502    ///
503    /// Returns the codec-specific [`ImageCodec::Error`] if the region or scale is invalid or
504    /// submission or device execution fails.
505    fn decode_region_scaled_to_device(
506        &mut self,
507        fmt: PixelFormat,
508        roi: Rect,
509        scale: Downscale,
510        backend: BackendRequest,
511    ) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
512    where
513        Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
514    {
515        let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
516        <Self as ImageDecodeSubmit<'a>>::submit_region_scaled_to_device(
517            self,
518            &mut session,
519            fmt,
520            roi,
521            scale,
522            backend,
523        )?
524        .wait()
525    }
526}
527
528/// Row-streaming decode API for large images or stripe-oriented callers.
529pub trait ImageDecodeRows<'a, S: Sample>: ImageDecode<'a> {
530    /// Decode rows into `sink` without requiring one contiguous output buffer.
531    ///
532    /// # Errors
533    ///
534    /// Returns [`DecodeRowsError::Decode`] for codec failures or
535    /// [`DecodeRowsError::Sink`] when the destination rejects a row.
536    #[expect(
537        clippy::type_complexity,
538        reason = "the public contract must preserve distinct codec and sink error types"
539    )]
540    fn decode_rows<R: RowSink<S>>(
541        &mut self,
542        sink: &mut R,
543    ) -> Result<DecodeOutcome<Self::Warning>, DecodeRowsError<Self::Error, R::Error>>;
544}
545
546/// Stateless tile-batch decode helpers that reuse caller-owned context.
547pub trait TileBatchDecode: ImageCodec {
548    /// Codec-specific context cached across tiles.
549    type Context: CodecContext;
550
551    /// Decode one tile into caller-owned output.
552    ///
553    /// # Errors
554    ///
555    /// Returns the codec-specific [`ImageCodec::Error`] when the tile input or output layout cannot
556    /// be decoded.
557    fn decode_tile(
558        ctx: &mut Self::Context,
559        pool: &mut Self::Pool,
560        input: &[u8],
561        out: &mut [u8],
562        stride: usize,
563        fmt: PixelFormat,
564    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
565
566    /// Decode one tile region into caller-owned output.
567    ///
568    /// # Errors
569    ///
570    /// Returns the codec-specific [`ImageCodec::Error`] when the tile, region, or output layout
571    /// cannot be decoded.
572    fn decode_tile_region(
573        ctx: &mut Self::Context,
574        pool: &mut Self::Pool,
575        input: &[u8],
576        out: &mut [u8],
577        stride: usize,
578        fmt: PixelFormat,
579        roi: Rect,
580    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
581
582    /// Decode one tile at reduced resolution into caller-owned output.
583    ///
584    /// # Errors
585    ///
586    /// Returns the codec-specific [`ImageCodec::Error`] when the tile, scale, or output layout
587    /// cannot be decoded.
588    fn decode_tile_scaled(
589        ctx: &mut Self::Context,
590        pool: &mut Self::Pool,
591        input: &[u8],
592        out: &mut [u8],
593        stride: usize,
594        fmt: PixelFormat,
595        scale: Downscale,
596    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
597
598    /// Decode one tile region at reduced resolution into caller-owned output.
599    ///
600    /// # Errors
601    ///
602    /// Returns the codec-specific [`ImageCodec::Error`] when the tile, region, scale, or output
603    /// layout cannot be decoded.
604    fn decode_tile_region_scaled(
605        ctx: &mut Self::Context,
606        pool: &mut Self::Pool,
607        fmt: PixelFormat,
608        job: TileRegionScaledDecodeJob<'_, '_>,
609    ) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
610}
611
612/// Tile-batch helpers that return synchronous device surfaces.
613pub trait TileBatchDecodeDevice: ImageCodec {
614    /// Codec-specific context cached across tiles.
615    type Context: CodecContext;
616    /// Device surface returned by decode calls.
617    type DeviceSurface: DeviceSurface;
618
619    /// Decode one tile to the requested backend.
620    ///
621    /// # Errors
622    ///
623    /// Returns the codec-specific [`ImageCodec::Error`] if the tile cannot be submitted or device
624    /// execution fails.
625    fn decode_tile_to_device(
626        ctx: &mut <Self as TileBatchDecodeDevice>::Context,
627        pool: &mut Self::Pool,
628        input: &[u8],
629        fmt: PixelFormat,
630        backend: BackendRequest,
631    ) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
632    where
633        Self: TileBatchDecodeSubmit<
634            Context = <Self as TileBatchDecodeDevice>::Context,
635            DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
636        >,
637    {
638        let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
639        <Self as TileBatchDecodeSubmit>::submit_tile_to_device(
640            ctx,
641            &mut session,
642            pool,
643            input,
644            fmt,
645            backend,
646        )?
647        .wait()
648    }
649
650    /// Decode one tile region to the requested backend.
651    ///
652    /// # Errors
653    ///
654    /// Returns the codec-specific [`ImageCodec::Error`] if the region is invalid or submission or
655    /// device execution fails.
656    fn decode_tile_region_to_device(
657        ctx: &mut <Self as TileBatchDecodeDevice>::Context,
658        pool: &mut Self::Pool,
659        input: &[u8],
660        fmt: PixelFormat,
661        roi: Rect,
662        backend: BackendRequest,
663    ) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
664    where
665        Self: TileBatchDecodeSubmit<
666            Context = <Self as TileBatchDecodeDevice>::Context,
667            DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
668        >,
669    {
670        let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
671        <Self as TileBatchDecodeSubmit>::submit_tile_region_to_device(
672            ctx,
673            &mut session,
674            pool,
675            input,
676            fmt,
677            roi,
678            backend,
679        )?
680        .wait()
681    }
682
683    /// Decode one tile at reduced resolution to the requested backend.
684    ///
685    /// # Errors
686    ///
687    /// Returns the codec-specific [`ImageCodec::Error`] if the scale is unsupported or submission
688    /// or device execution fails.
689    fn decode_tile_scaled_to_device(
690        ctx: &mut <Self as TileBatchDecodeDevice>::Context,
691        pool: &mut Self::Pool,
692        input: &[u8],
693        fmt: PixelFormat,
694        scale: Downscale,
695        backend: BackendRequest,
696    ) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
697    where
698        Self: TileBatchDecodeSubmit<
699            Context = <Self as TileBatchDecodeDevice>::Context,
700            DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
701        >,
702    {
703        let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
704        <Self as TileBatchDecodeSubmit>::submit_tile_scaled_to_device(
705            ctx,
706            &mut session,
707            pool,
708            input,
709            fmt,
710            scale,
711            backend,
712        )?
713        .wait()
714    }
715
716    /// Decode one tile region at reduced resolution to the requested backend.
717    ///
718    /// # Errors
719    ///
720    /// Returns the codec-specific [`ImageCodec::Error`] if the region or scale is invalid or
721    /// submission or device execution fails.
722    fn decode_tile_region_scaled_to_device(
723        ctx: &mut <Self as TileBatchDecodeDevice>::Context,
724        pool: &mut Self::Pool,
725        input: &[u8],
726        fmt: PixelFormat,
727        roi: Rect,
728        scale: Downscale,
729        backend: BackendRequest,
730    ) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
731    where
732        Self: TileBatchDecodeSubmit<
733            Context = <Self as TileBatchDecodeDevice>::Context,
734            DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
735        >,
736    {
737        let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
738        <Self as TileBatchDecodeSubmit>::submit_tile_region_scaled_to_device(
739            ctx,
740            &mut session,
741            pool,
742            TileRegionScaledDeviceDecodeRequest {
743                input,
744                fmt,
745                roi,
746                scale,
747                backend,
748            },
749        )?
750        .wait()
751    }
752}
753
754/// Full-tile batch helpers that decode many independent tiles to device surfaces.
755pub trait TileBatchDecodeManyDevice: ImageCodec {
756    /// Codec-specific context cached across tiles.
757    type Context: CodecContext;
758    /// Device surface returned by decode calls.
759    type DeviceSurface: DeviceSurface;
760
761    /// Decode many full tiles to the requested backend, preserving input order.
762    ///
763    /// # Errors
764    ///
765    /// Returns the codec-specific [`ImageCodec::Error`] if any tile cannot be decoded by the
766    /// requested backend.
767    fn decode_tiles_to_device(
768        ctx: &mut Self::Context,
769        pool: &mut Self::Pool,
770        inputs: &[&[u8]],
771        fmt: PixelFormat,
772        backend: BackendRequest,
773    ) -> Result<Vec<Self::DeviceSurface>, Self::Error>;
774}
775
776/// Tile-batch helpers that queue device submissions.
777pub trait TileBatchDecodeSubmit: ImageCodec {
778    /// Codec-specific context cached across tiles.
779    type Context: CodecContext;
780    /// Mutable session state shared across submissions.
781    type Session: Default + Send;
782    /// Device surface returned by completed submissions.
783    type DeviceSurface: DeviceSurface;
784    /// Submission handle type.
785    type SubmittedSurface: DeviceSubmission<Output = Self::DeviceSurface, Error = Self::Error>;
786
787    /// Submit one full tile to the requested backend.
788    ///
789    /// # Errors
790    ///
791    /// Returns the codec-specific [`ImageCodec::Error`] when the request is invalid, unsupported,
792    /// or cannot be submitted.
793    fn submit_tile_to_device(
794        ctx: &mut Self::Context,
795        session: &mut Self::Session,
796        pool: &mut Self::Pool,
797        input: &[u8],
798        fmt: PixelFormat,
799        backend: BackendRequest,
800    ) -> Result<Self::SubmittedSurface, Self::Error>;
801
802    /// Submit one tile region to the requested backend.
803    ///
804    /// # Errors
805    ///
806    /// Returns the codec-specific [`ImageCodec::Error`] when the region or backend request is
807    /// invalid, unsupported, or cannot be submitted.
808    fn submit_tile_region_to_device(
809        ctx: &mut Self::Context,
810        session: &mut Self::Session,
811        pool: &mut Self::Pool,
812        input: &[u8],
813        fmt: PixelFormat,
814        roi: Rect,
815        backend: BackendRequest,
816    ) -> Result<Self::SubmittedSurface, Self::Error>;
817
818    /// Submit one tile at reduced resolution to the requested backend.
819    ///
820    /// # Errors
821    ///
822    /// Returns the codec-specific [`ImageCodec::Error`] when the scale or backend request is
823    /// invalid, unsupported, or cannot be submitted.
824    fn submit_tile_scaled_to_device(
825        ctx: &mut Self::Context,
826        session: &mut Self::Session,
827        pool: &mut Self::Pool,
828        input: &[u8],
829        fmt: PixelFormat,
830        scale: Downscale,
831        backend: BackendRequest,
832    ) -> Result<Self::SubmittedSurface, Self::Error>;
833
834    /// Submit one tile region at reduced resolution to the requested backend.
835    ///
836    /// # Errors
837    ///
838    /// Returns the codec-specific [`ImageCodec::Error`] when the request is invalid, unsupported,
839    /// or cannot be submitted.
840    fn submit_tile_region_scaled_to_device(
841        ctx: &mut Self::Context,
842        session: &mut Self::Session,
843        pool: &mut Self::Pool,
844        request: TileRegionScaledDeviceDecodeRequest<'_>,
845    ) -> Result<Self::SubmittedSurface, Self::Error>;
846}
847
848/// Tile payload decompression API for container codecs such as Deflate, Zstd,
849/// LZW, and uncompressed data.
850pub trait TileDecompress {
851    /// Codec-specific error type.
852    type Error: CodecError;
853    /// Caller-owned scratch pool type.
854    type Pool: ScratchPool;
855
856    /// Return the expected decoded size when the compressed payload encodes it.
857    ///
858    /// # Errors
859    ///
860    /// Returns [`Self::Error`] when the payload header is invalid or unsupported.
861    fn expected_size(input: &[u8]) -> Result<Option<usize>, Self::Error>;
862
863    /// Decompress `input` into `out`, returning the number of bytes written.
864    ///
865    /// # Errors
866    ///
867    /// Returns [`Self::Error`] when the payload is invalid, scratch allocation
868    /// fails, or `out` cannot hold the decoded bytes.
869    fn decompress_into(
870        pool: &mut Self::Pool,
871        input: &[u8],
872        out: &mut [u8],
873    ) -> Result<usize, Self::Error>;
874}
875
876#[cfg(test)]
877mod tests;