Skip to main content

j2k_jpeg/
baseline_encode_contract.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Public data and error contracts for baseline JPEG encoding.
4
5use alloc::vec::Vec;
6
7use thiserror::Error;
8
9use crate::dct_contract::JpegDctImageError;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12/// JPEG encoder backend selector.
13pub enum JpegBackend {
14    /// Choose the best available backend for the platform.
15    Auto,
16    /// Use the portable CPU encoder.
17    Cpu,
18    /// Use a Metal encoder when called through the Metal integration.
19    Metal,
20    /// Use a CUDA encoder when called through the CUDA integration.
21    Cuda,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25/// JPEG baseline chroma subsampling mode.
26pub enum JpegSubsampling {
27    /// Single-component grayscale.
28    Gray,
29    /// Three-component YBR/RGB 4:4:4 sampling.
30    Ybr444,
31    /// Three-component YBR/RGB 4:2:2 sampling.
32    Ybr422,
33    /// Three-component YBR/RGB 4:2:0 sampling.
34    Ybr420,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38/// Options controlling baseline JPEG encoding.
39pub struct JpegEncodeOptions {
40    /// JPEG quality in the conventional 1..=100 range.
41    pub quality: u8,
42    /// Output component sampling.
43    pub subsampling: JpegSubsampling,
44    /// Optional restart interval in MCUs.
45    pub restart_interval: Option<u16>,
46    /// Requested encoder backend.
47    pub backend: JpegBackend,
48}
49
50impl Default for JpegEncodeOptions {
51    fn default() -> Self {
52        Self {
53            quality: 90,
54            subsampling: JpegSubsampling::Ybr422,
55            restart_interval: None,
56            backend: JpegBackend::Auto,
57        }
58    }
59}
60
61#[derive(Debug, Clone, Copy)]
62/// Borrowed input samples for baseline JPEG encoding.
63pub enum JpegSamples<'a> {
64    /// Interleaved 8-bit grayscale samples.
65    Gray8 {
66        /// Pixel data, one byte per pixel.
67        data: &'a [u8],
68        /// Image width in pixels.
69        width: u32,
70        /// Image height in pixels.
71        height: u32,
72    },
73    /// Interleaved 8-bit RGB samples.
74    Rgb8 {
75        /// Pixel data, three bytes per pixel.
76        data: &'a [u8],
77        /// Image width in pixels.
78        width: u32,
79        /// Image height in pixels.
80        height: u32,
81    },
82}
83
84#[derive(Debug, PartialEq, Eq)]
85/// Encoded baseline JPEG bytes and the backend that produced them.
86///
87/// The retained codestream can approach the shared host-allocation cap, so
88/// this owner is intentionally move-only rather than exposing infallible
89/// full-payload cloning.
90pub struct EncodedJpeg {
91    /// Complete JPEG codestream.
92    pub data: Vec<u8>,
93    /// Backend used to encode the codestream.
94    pub backend: JpegBackend,
95}
96
97#[derive(Clone, Debug, Error)]
98/// Errors produced by baseline JPEG encoding.
99pub enum JpegEncodeError {
100    #[error("JPEG encode requires nonzero dimensions")]
101    /// Width or height was zero.
102    EmptyDimensions,
103    #[error("JPEG baseline dimensions must fit in u16, got {width}x{height}")]
104    /// JPEG baseline SOF dimensions exceed the 16-bit marker fields.
105    DimensionsTooLarge {
106        /// Requested width in pixels.
107        width: u32,
108        /// Requested height in pixels.
109        height: u32,
110    },
111    #[error("JPEG sample buffer length mismatch: expected {expected}, got {actual}")]
112    /// Input sample buffer length does not match width, height, and format.
113    SampleLength {
114        /// Required byte count.
115        expected: usize,
116        /// Supplied byte count.
117        actual: usize,
118    },
119    #[error("JPEG host buffer requires {requested} bytes, exceeding the {cap}-byte cap")]
120    /// A sample layout or encoded output exceeds the shared host allocation cap.
121    MemoryCapExceeded {
122        /// Required byte count, saturated when arithmetic overflows.
123        requested: usize,
124        /// Maximum accepted host allocation size.
125        cap: usize,
126    },
127    #[error("JPEG host allocation failed for {bytes} bytes")]
128    /// A required host buffer could not reserve its capacity.
129    HostAllocationFailed {
130        /// Requested allocation size in bytes.
131        bytes: usize,
132    },
133    #[error("JPEG subsampling {subsampling:?} is incompatible with {samples}")]
134    /// Requested subsampling is incompatible with the supplied sample format.
135    IncompatibleSubsampling {
136        /// Requested output sampling.
137        subsampling: JpegSubsampling,
138        /// Human-readable sample format name.
139        samples: &'static str,
140    },
141    #[error("JPEG restart interval must be nonzero when provided")]
142    /// Restart interval was explicitly set to zero.
143    InvalidRestartInterval,
144    #[error("JPEG encode backend {backend:?} is unavailable in j2k-jpeg CPU crate")]
145    /// Requested backend is not available in this crate.
146    UnsupportedBackend {
147        /// Requested backend.
148        backend: JpegBackend,
149    },
150    #[error("JPEG encoded marker segment is too large: {name}")]
151    /// A marker segment would exceed the JPEG 16-bit length field.
152    SegmentTooLarge {
153        /// Marker segment name.
154        name: &'static str,
155    },
156    #[error("JPEG entropy symbol has no Huffman code: {symbol}")]
157    /// Encoder attempted to emit a symbol absent from the active Huffman table.
158    MissingHuffmanCode {
159        /// Missing entropy symbol.
160        symbol: u8,
161    },
162    #[error("invalid JPEG DCT image: {reason}")]
163    /// Caller-supplied coefficient-domain input cannot be re-emitted as baseline JPEG.
164    InvalidDctImage {
165        /// Typed invalid-input reason.
166        #[source]
167        reason: JpegDctImageError,
168    },
169    #[error("JPEG encode internal invariant failed: {reason}")]
170    /// A heap-free diagnostic for an impossible encoder state.
171    InternalInvariant {
172        /// Static invariant description.
173        reason: &'static str,
174    },
175}