1use crate::consts::DCT_BLOCK_SIZE;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
7#[non_exhaustive]
8#[repr(u8)]
9pub enum ColorSpace {
10 #[default]
12 Unknown = 0,
13 Grayscale = 1,
15 Rgb = 2,
17 YCbCr = 3,
19 Cmyk = 4,
21 Ycck = 5,
23 Xyb = 6,
25}
26
27impl ColorSpace {
28 #[must_use]
30 pub const fn num_components(self) -> usize {
31 match self {
32 Self::Unknown => 0,
33 Self::Grayscale => 1,
34 Self::Rgb | Self::YCbCr | Self::Xyb => 3,
35 Self::Cmyk | Self::Ycck => 4,
36 }
37 }
38
39 #[must_use]
41 pub const fn default_subsampling(self) -> bool {
42 matches!(self, Self::YCbCr | Self::Ycck)
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
48#[non_exhaustive]
49pub enum PixelFormat {
50 Gray,
52 #[default]
54 Rgb,
55 Rgba,
57 Bgr,
59 Bgra,
61 Cmyk,
63}
64
65impl PixelFormat {
66 #[must_use]
68 pub const fn bytes_per_pixel(self) -> usize {
69 match self {
70 Self::Gray => 1,
71 Self::Rgb | Self::Bgr => 3,
72 Self::Rgba | Self::Bgra | Self::Cmyk => 4,
73 }
74 }
75
76 #[must_use]
78 pub const fn num_channels(self) -> usize {
79 match self {
80 Self::Gray => 1,
81 Self::Rgb | Self::Bgr | Self::Rgba | Self::Bgra => 3,
82 Self::Cmyk => 4,
83 }
84 }
85
86 #[must_use]
88 pub const fn color_space(self) -> ColorSpace {
89 match self {
90 Self::Gray => ColorSpace::Grayscale,
91 Self::Rgb | Self::Rgba | Self::Bgr | Self::Bgra => ColorSpace::Rgb,
92 Self::Cmyk => ColorSpace::Cmyk,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
99#[non_exhaustive]
100pub enum SampleDepth {
101 #[default]
103 Bits8,
104 Bits16,
106 Float32,
108}
109
110impl SampleDepth {
111 #[must_use]
113 pub const fn bytes_per_sample(self) -> usize {
114 match self {
115 Self::Bits8 => 1,
116 Self::Bits16 => 2,
117 Self::Float32 => 4,
118 }
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
128#[non_exhaustive]
129pub enum OutputDataType {
130 #[default]
132 Uint8,
133 Uint16,
135 Float32,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
141#[non_exhaustive]
142pub enum Subsampling {
143 #[default]
145 S444,
146 S422,
148 S420,
150 S440,
152}
153
154impl Subsampling {
155 #[must_use]
157 pub const fn h_samp_factor_luma(self) -> u8 {
158 match self {
159 Self::S444 | Self::S440 => 1,
160 Self::S422 | Self::S420 => 2,
161 }
162 }
163
164 #[must_use]
166 pub const fn v_samp_factor_luma(self) -> u8 {
167 match self {
168 Self::S444 | Self::S422 => 1,
169 Self::S420 | Self::S440 => 2,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
176#[non_exhaustive]
177pub enum JpegMode {
178 #[default]
180 Baseline,
181 Extended,
183 Progressive,
185 Lossless,
187}
188
189#[derive(Debug, Clone)]
191pub struct Component {
192 pub id: u8,
194 pub h_samp_factor: u8,
196 pub v_samp_factor: u8,
198 pub quant_table_idx: u8,
200 pub dc_huffman_idx: u8,
202 pub ac_huffman_idx: u8,
204}
205
206impl Default for Component {
207 fn default() -> Self {
208 Self {
209 id: 0,
210 h_samp_factor: 1,
211 v_samp_factor: 1,
212 quant_table_idx: 0,
213 dc_huffman_idx: 0,
214 ac_huffman_idx: 0,
215 }
216 }
217}
218
219#[derive(Debug, Clone)]
221pub struct QuantTable {
222 pub values: [u16; DCT_BLOCK_SIZE],
224 pub precision: u8,
226}
227
228impl Default for QuantTable {
229 fn default() -> Self {
230 Self {
231 values: [16; DCT_BLOCK_SIZE], precision: 0,
233 }
234 }
235}
236
237impl QuantTable {
238 #[must_use]
240 pub fn from_natural_order(values: &[u16; DCT_BLOCK_SIZE]) -> Self {
241 let mut zigzag = [0u16; DCT_BLOCK_SIZE];
242 for (i, &v) in values.iter().enumerate() {
243 let zi = crate::consts::JPEG_ZIGZAG_ORDER[i] as usize;
244 zigzag[zi] = v;
245 }
246 Self {
247 values: zigzag,
248 precision: if values.iter().any(|&v| v > 255) {
249 1
250 } else {
251 0
252 },
253 }
254 }
255
256 #[must_use]
258 pub fn to_natural_order(&self) -> [u16; DCT_BLOCK_SIZE] {
259 let mut natural = [0u16; DCT_BLOCK_SIZE];
260 for (i, &zi) in crate::consts::JPEG_NATURAL_ORDER[..DCT_BLOCK_SIZE]
261 .iter()
262 .enumerate()
263 {
264 natural[zi as usize] = self.values[i];
265 }
266 natural
267 }
268}
269
270#[derive(Debug, Clone)]
272pub struct HuffmanTable {
273 pub bits: [u8; 16],
275 pub values: Vec<u8>,
277 pub is_dc: bool,
279}
280
281impl Default for HuffmanTable {
282 fn default() -> Self {
283 Self {
284 bits: [0; 16],
285 values: Vec::new(),
286 is_dc: true,
287 }
288 }
289}
290
291pub type Coeff = i16;
293
294pub type CoeffBlock = [Coeff; DCT_BLOCK_SIZE];
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
299pub struct Dimensions {
300 pub width: u32,
302 pub height: u32,
304}
305
306impl Dimensions {
307 #[must_use]
309 pub const fn new(width: u32, height: u32) -> Self {
310 Self { width, height }
311 }
312
313 #[must_use]
315 pub const fn width_in_blocks(self) -> u32 {
316 (self.width + 7) / 8
317 }
318
319 #[must_use]
321 pub const fn height_in_blocks(self) -> u32 {
322 (self.height + 7) / 8
323 }
324
325 #[must_use]
327 pub const fn num_pixels(self) -> u64 {
328 self.width as u64 * self.height as u64
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub struct ScanSpec {
335 pub comp_start: u8,
337 pub num_comps: u8,
339 pub ss: u8,
341 pub se: u8,
343 pub ah: u8,
345 pub al: u8,
347}
348
349impl Default for ScanSpec {
350 fn default() -> Self {
351 Self {
352 comp_start: 0,
353 num_comps: 3,
354 ss: 0,
355 se: 63,
356 ah: 0,
357 al: 0,
358 }
359 }
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
364pub struct RestartInterval(pub u16);
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_color_space_components() {
372 assert_eq!(ColorSpace::Grayscale.num_components(), 1);
373 assert_eq!(ColorSpace::Rgb.num_components(), 3);
374 assert_eq!(ColorSpace::YCbCr.num_components(), 3);
375 assert_eq!(ColorSpace::Cmyk.num_components(), 4);
376 }
377
378 #[test]
379 fn test_pixel_format_bytes() {
380 assert_eq!(PixelFormat::Gray.bytes_per_pixel(), 1);
381 assert_eq!(PixelFormat::Rgb.bytes_per_pixel(), 3);
382 assert_eq!(PixelFormat::Rgba.bytes_per_pixel(), 4);
383 }
384
385 #[test]
386 fn test_quant_table_order_conversion() {
387 let natural = [
388 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
389 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
390 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
391 ];
392 let table = QuantTable::from_natural_order(&natural);
393 let recovered = table.to_natural_order();
394 assert_eq!(natural, recovered);
395 }
396}