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}