use alloc::vec::Vec;
use crate::{
accelerator::{DeviceMemoryRange, ExecutionStats, SurfaceResidency},
backend::{BackendKind, BackendRequest},
batch::{TileRegionScaledDecodeJob, TileRegionScaledDeviceDecodeRequest},
context::{CodecContext, DecoderContext},
error::CodecError,
pixel::PixelFormat,
row_sink::RowSink,
sample::Sample,
scale::Downscale,
scratch::ScratchPool,
types::{DecodeOutcome, Info, Rect},
};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum DecodeRowsError<D, E>
where
D: core::error::Error + 'static,
E: core::error::Error + 'static,
{
#[error(transparent)]
Decode(D),
#[error(transparent)]
Sink(E),
}
pub trait ImageCodec {
type Error: CodecError;
type Warning: core::fmt::Debug + core::fmt::Display + Send + Sync + 'static;
type Pool: ScratchPool;
}
pub trait DeviceSurface {
fn backend_kind(&self) -> BackendKind;
fn residency(&self) -> SurfaceResidency {
SurfaceResidency::for_backend(self.backend_kind())
}
fn dimensions(&self) -> (u32, u32);
fn pixel_format(&self) -> PixelFormat;
fn byte_len(&self) -> usize;
fn execution_stats(&self) -> ExecutionStats {
ExecutionStats::default()
}
fn memory_range(&self) -> Option<DeviceMemoryRange> {
None
}
}
pub trait DeviceSubmission {
type Output;
type Error;
fn wait(self) -> Result<Self::Output, Self::Error>;
}
#[derive(Debug)]
#[doc(hidden)]
pub struct ReadySubmission<T, E>(Result<T, E>);
impl<T, E> ReadySubmission<T, E> {
pub fn from_result(result: Result<T, E>) -> Self {
Self(result)
}
}
impl<T, E> DeviceSubmission for ReadySubmission<T, E> {
type Output = T;
type Error = E;
fn wait(self) -> Result<Self::Output, Self::Error> {
self.0
}
}
#[doc(hidden)]
pub trait DeviceSubmitSession {
fn record_submit(&mut self);
}
#[doc(hidden)]
pub fn submit_ready_device<S, T, E>(
session: &mut S,
submit: impl FnOnce(&mut S) -> Result<T, E>,
) -> ReadySubmission<T, E>
where
S: DeviceSubmitSession + ?Sized,
{
session.record_submit();
ReadySubmission::from_result(submit(session))
}
pub trait ImageDecode<'a>: ImageCodec + Sized + 'a {
type View: 'a;
fn inspect(input: &'a [u8]) -> Result<Info, Self::Error>;
fn parse(input: &'a [u8]) -> Result<Self::View, Self::Error>;
fn from_view(view: Self::View) -> Result<Self, Self::Error>;
fn decode_into(
&mut self,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_into_with_scratch(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_region_into(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
roi: Rect,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_scaled_into(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
scale: Downscale,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_region_scaled_into(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
roi: Rect,
scale: Downscale,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
}
#[doc(hidden)]
pub trait CpuBackedImageDecode<'a>: ImageCodec + Sized + 'a {
type Cpu: ImageDecode<'a, Pool = Self::Pool>;
type View: 'a;
fn inspect_cpu(input: &'a [u8]) -> Result<Info, Self::Error>;
fn parse_cpu(input: &'a [u8]) -> Result<Self::View, Self::Error>;
fn from_cpu_view(view: Self::View) -> Result<Self, Self::Error>;
fn cpu_decoder_mut(&mut self) -> &mut Self::Cpu;
fn map_cpu_outcome(
outcome: DecodeOutcome<<Self::Cpu as ImageCodec>::Warning>,
) -> DecodeOutcome<Self::Warning>;
}
#[doc(hidden)]
impl<'a, T> ImageDecode<'a> for T
where
T: CpuBackedImageDecode<'a>,
<T::Cpu as ImageCodec>::Error: Into<T::Error>,
{
type View = T::View;
fn inspect(input: &'a [u8]) -> Result<Info, Self::Error> {
T::inspect_cpu(input)
}
fn parse(input: &'a [u8]) -> Result<Self::View, Self::Error> {
T::parse_cpu(input)
}
fn from_view(view: Self::View) -> Result<Self, Self::Error> {
T::from_cpu_view(view)
}
fn decode_into(
&mut self,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
let outcome = self
.cpu_decoder_mut()
.decode_into(out, stride, fmt)
.map_err(Into::into)?;
Ok(T::map_cpu_outcome(outcome))
}
fn decode_into_with_scratch(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
let outcome = self
.cpu_decoder_mut()
.decode_into_with_scratch(pool, out, stride, fmt)
.map_err(Into::into)?;
Ok(T::map_cpu_outcome(outcome))
}
fn decode_region_into(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
roi: Rect,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
let outcome = self
.cpu_decoder_mut()
.decode_region_into(pool, out, stride, fmt, roi)
.map_err(Into::into)?;
Ok(T::map_cpu_outcome(outcome))
}
fn decode_scaled_into(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
scale: Downscale,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
let outcome = self
.cpu_decoder_mut()
.decode_scaled_into(pool, out, stride, fmt, scale)
.map_err(Into::into)?;
Ok(T::map_cpu_outcome(outcome))
}
fn decode_region_scaled_into(
&mut self,
pool: &mut Self::Pool,
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
roi: Rect,
scale: Downscale,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
let outcome = self
.cpu_decoder_mut()
.decode_region_scaled_into(pool, out, stride, fmt, roi, scale)
.map_err(Into::into)?;
Ok(T::map_cpu_outcome(outcome))
}
}
pub trait ImageDecodeSubmit<'a>: ImageDecode<'a> {
type Session: Default + Send;
type DeviceSurface: DeviceSurface;
type SubmittedSurface: DeviceSubmission<Output = Self::DeviceSurface, Error = Self::Error>;
fn submit_to_device(
&mut self,
session: &mut Self::Session,
fmt: PixelFormat,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
fn submit_region_to_device(
&mut self,
session: &mut Self::Session,
fmt: PixelFormat,
roi: Rect,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
fn submit_scaled_to_device(
&mut self,
session: &mut Self::Session,
fmt: PixelFormat,
scale: Downscale,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
fn submit_region_scaled_to_device(
&mut self,
session: &mut Self::Session,
fmt: PixelFormat,
roi: Rect,
scale: Downscale,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
}
pub trait ImageDecodeDevice<'a>: ImageDecode<'a> {
type DeviceSurface: DeviceSurface;
fn decode_to_device(
&mut self,
fmt: PixelFormat,
backend: BackendRequest,
) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
where
Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
{
let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
<Self as ImageDecodeSubmit<'a>>::submit_to_device(self, &mut session, fmt, backend)?.wait()
}
fn decode_region_to_device(
&mut self,
fmt: PixelFormat,
roi: Rect,
backend: BackendRequest,
) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
where
Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
{
let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
<Self as ImageDecodeSubmit<'a>>::submit_region_to_device(
self,
&mut session,
fmt,
roi,
backend,
)?
.wait()
}
fn decode_scaled_to_device(
&mut self,
fmt: PixelFormat,
scale: Downscale,
backend: BackendRequest,
) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
where
Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
{
let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
<Self as ImageDecodeSubmit<'a>>::submit_scaled_to_device(
self,
&mut session,
fmt,
scale,
backend,
)?
.wait()
}
fn decode_region_scaled_to_device(
&mut self,
fmt: PixelFormat,
roi: Rect,
scale: Downscale,
backend: BackendRequest,
) -> Result<<Self as ImageDecodeDevice<'a>>::DeviceSurface, Self::Error>
where
Self: ImageDecodeSubmit<'a, DeviceSurface = <Self as ImageDecodeDevice<'a>>::DeviceSurface>,
{
let mut session = <Self as ImageDecodeSubmit<'a>>::Session::default();
<Self as ImageDecodeSubmit<'a>>::submit_region_scaled_to_device(
self,
&mut session,
fmt,
roi,
scale,
backend,
)?
.wait()
}
}
pub trait ImageDecodeRows<'a, S: Sample>: ImageDecode<'a> {
#[expect(
clippy::type_complexity,
reason = "the public contract must preserve distinct codec and sink error types"
)]
fn decode_rows<R: RowSink<S>>(
&mut self,
sink: &mut R,
) -> Result<DecodeOutcome<Self::Warning>, DecodeRowsError<Self::Error, R::Error>>;
}
pub trait TileBatchDecode: ImageCodec {
type Context: CodecContext;
fn decode_tile(
ctx: &mut DecoderContext<Self::Context>,
pool: &mut Self::Pool,
input: &[u8],
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_tile_region(
ctx: &mut DecoderContext<Self::Context>,
pool: &mut Self::Pool,
input: &[u8],
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
roi: Rect,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_tile_scaled(
ctx: &mut DecoderContext<Self::Context>,
pool: &mut Self::Pool,
input: &[u8],
out: &mut [u8],
stride: usize,
fmt: PixelFormat,
scale: Downscale,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
fn decode_tile_region_scaled(
ctx: &mut DecoderContext<Self::Context>,
pool: &mut Self::Pool,
fmt: PixelFormat,
job: TileRegionScaledDecodeJob<'_, '_>,
) -> Result<DecodeOutcome<Self::Warning>, Self::Error>;
}
pub trait TileBatchDecodeDevice: ImageCodec {
type Context: CodecContext;
type DeviceSurface: DeviceSurface;
fn decode_tile_to_device(
ctx: &mut DecoderContext<<Self as TileBatchDecodeDevice>::Context>,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
backend: BackendRequest,
) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
where
Self: TileBatchDecodeSubmit<
Context = <Self as TileBatchDecodeDevice>::Context,
DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
>,
{
let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
<Self as TileBatchDecodeSubmit>::submit_tile_to_device(
ctx,
&mut session,
pool,
input,
fmt,
backend,
)?
.wait()
}
fn decode_tile_region_to_device(
ctx: &mut DecoderContext<<Self as TileBatchDecodeDevice>::Context>,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
roi: Rect,
backend: BackendRequest,
) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
where
Self: TileBatchDecodeSubmit<
Context = <Self as TileBatchDecodeDevice>::Context,
DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
>,
{
let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
<Self as TileBatchDecodeSubmit>::submit_tile_region_to_device(
ctx,
&mut session,
pool,
input,
fmt,
roi,
backend,
)?
.wait()
}
fn decode_tile_scaled_to_device(
ctx: &mut DecoderContext<<Self as TileBatchDecodeDevice>::Context>,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
scale: Downscale,
backend: BackendRequest,
) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
where
Self: TileBatchDecodeSubmit<
Context = <Self as TileBatchDecodeDevice>::Context,
DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
>,
{
let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
<Self as TileBatchDecodeSubmit>::submit_tile_scaled_to_device(
ctx,
&mut session,
pool,
input,
fmt,
scale,
backend,
)?
.wait()
}
fn decode_tile_region_scaled_to_device(
ctx: &mut DecoderContext<<Self as TileBatchDecodeDevice>::Context>,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
roi: Rect,
scale: Downscale,
backend: BackendRequest,
) -> Result<<Self as TileBatchDecodeDevice>::DeviceSurface, Self::Error>
where
Self: TileBatchDecodeSubmit<
Context = <Self as TileBatchDecodeDevice>::Context,
DeviceSurface = <Self as TileBatchDecodeDevice>::DeviceSurface,
>,
{
let mut session = <Self as TileBatchDecodeSubmit>::Session::default();
<Self as TileBatchDecodeSubmit>::submit_tile_region_scaled_to_device(
ctx,
&mut session,
pool,
TileRegionScaledDeviceDecodeRequest {
input,
fmt,
roi,
scale,
backend,
},
)?
.wait()
}
}
pub trait TileBatchDecodeManyDevice: ImageCodec {
type Context: CodecContext;
type DeviceSurface: DeviceSurface;
fn decode_tiles_to_device(
ctx: &mut DecoderContext<Self::Context>,
pool: &mut Self::Pool,
inputs: &[&[u8]],
fmt: PixelFormat,
backend: BackendRequest,
) -> Result<Vec<Self::DeviceSurface>, Self::Error>;
}
pub trait TileBatchDecodeSubmit: ImageCodec {
type Context: CodecContext;
type Session: Default + Send;
type DeviceSurface: DeviceSurface;
type SubmittedSurface: DeviceSubmission<Output = Self::DeviceSurface, Error = Self::Error>;
fn submit_tile_to_device(
ctx: &mut DecoderContext<Self::Context>,
session: &mut Self::Session,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
fn submit_tile_region_to_device(
ctx: &mut DecoderContext<Self::Context>,
session: &mut Self::Session,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
roi: Rect,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
fn submit_tile_scaled_to_device(
ctx: &mut DecoderContext<Self::Context>,
session: &mut Self::Session,
pool: &mut Self::Pool,
input: &[u8],
fmt: PixelFormat,
scale: Downscale,
backend: BackendRequest,
) -> Result<Self::SubmittedSurface, Self::Error>;
fn submit_tile_region_scaled_to_device(
ctx: &mut DecoderContext<Self::Context>,
session: &mut Self::Session,
pool: &mut Self::Pool,
request: TileRegionScaledDeviceDecodeRequest<'_>,
) -> Result<Self::SubmittedSurface, Self::Error>;
}
pub trait TileDecompress {
type Error: CodecError;
type Pool: ScratchPool;
fn expected_size(input: &[u8]) -> Result<Option<usize>, Self::Error>;
fn decompress_into(
pool: &mut Self::Pool,
input: &[u8],
out: &mut [u8],
) -> Result<usize, Self::Error>;
}
#[cfg(test)]
mod tests;