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)]
124#[non_exhaustive]
125pub enum Subsampling {
126 #[default]
128 S444,
129 S422,
131 S420,
133 S440,
135}
136
137impl Subsampling {
138 #[must_use]
140 pub const fn h_samp_factor_luma(self) -> u8 {
141 match self {
142 Self::S444 | Self::S440 => 1,
143 Self::S422 | Self::S420 => 2,
144 }
145 }
146
147 #[must_use]
149 pub const fn v_samp_factor_luma(self) -> u8 {
150 match self {
151 Self::S444 | Self::S422 => 1,
152 Self::S420 | Self::S440 => 2,
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
159#[non_exhaustive]
160pub enum JpegMode {
161 #[default]
163 Baseline,
164 Extended,
166 Progressive,
168 Lossless,
170}
171
172#[derive(Debug, Clone)]
174pub struct Component {
175 pub id: u8,
177 pub h_samp_factor: u8,
179 pub v_samp_factor: u8,
181 pub quant_table_idx: u8,
183 pub dc_huffman_idx: u8,
185 pub ac_huffman_idx: u8,
187}
188
189impl Default for Component {
190 fn default() -> Self {
191 Self {
192 id: 0,
193 h_samp_factor: 1,
194 v_samp_factor: 1,
195 quant_table_idx: 0,
196 dc_huffman_idx: 0,
197 ac_huffman_idx: 0,
198 }
199 }
200}
201
202#[derive(Debug, Clone)]
204pub struct QuantTable {
205 pub values: [u16; DCT_BLOCK_SIZE],
207 pub precision: u8,
209}
210
211impl Default for QuantTable {
212 fn default() -> Self {
213 Self {
214 values: [16; DCT_BLOCK_SIZE], precision: 0,
216 }
217 }
218}
219
220impl QuantTable {
221 #[must_use]
223 pub fn from_natural_order(values: &[u16; DCT_BLOCK_SIZE]) -> Self {
224 let mut zigzag = [0u16; DCT_BLOCK_SIZE];
225 for (i, &v) in values.iter().enumerate() {
226 let zi = crate::consts::JPEG_ZIGZAG_ORDER[i] as usize;
227 zigzag[zi] = v;
228 }
229 Self {
230 values: zigzag,
231 precision: if values.iter().any(|&v| v > 255) {
232 1
233 } else {
234 0
235 },
236 }
237 }
238
239 #[must_use]
241 pub fn to_natural_order(&self) -> [u16; DCT_BLOCK_SIZE] {
242 let mut natural = [0u16; DCT_BLOCK_SIZE];
243 for (i, &zi) in crate::consts::JPEG_NATURAL_ORDER[..DCT_BLOCK_SIZE]
244 .iter()
245 .enumerate()
246 {
247 natural[zi as usize] = self.values[i];
248 }
249 natural
250 }
251}
252
253#[derive(Debug, Clone)]
255pub struct HuffmanTable {
256 pub bits: [u8; 16],
258 pub values: Vec<u8>,
260 pub is_dc: bool,
262}
263
264impl Default for HuffmanTable {
265 fn default() -> Self {
266 Self {
267 bits: [0; 16],
268 values: Vec::new(),
269 is_dc: true,
270 }
271 }
272}
273
274pub type Coeff = i16;
276
277pub type CoeffBlock = [Coeff; DCT_BLOCK_SIZE];
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
282pub struct Dimensions {
283 pub width: u32,
285 pub height: u32,
287}
288
289impl Dimensions {
290 #[must_use]
292 pub const fn new(width: u32, height: u32) -> Self {
293 Self { width, height }
294 }
295
296 #[must_use]
298 pub const fn width_in_blocks(self) -> u32 {
299 (self.width + 7) / 8
300 }
301
302 #[must_use]
304 pub const fn height_in_blocks(self) -> u32 {
305 (self.height + 7) / 8
306 }
307
308 #[must_use]
310 pub const fn num_pixels(self) -> u64 {
311 self.width as u64 * self.height as u64
312 }
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub struct ScanSpec {
318 pub comp_start: u8,
320 pub num_comps: u8,
322 pub ss: u8,
324 pub se: u8,
326 pub ah: u8,
328 pub al: u8,
330}
331
332impl Default for ScanSpec {
333 fn default() -> Self {
334 Self {
335 comp_start: 0,
336 num_comps: 3,
337 ss: 0,
338 se: 63,
339 ah: 0,
340 al: 0,
341 }
342 }
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
347pub struct RestartInterval(pub u16);
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 #[test]
354 fn test_color_space_components() {
355 assert_eq!(ColorSpace::Grayscale.num_components(), 1);
356 assert_eq!(ColorSpace::Rgb.num_components(), 3);
357 assert_eq!(ColorSpace::YCbCr.num_components(), 3);
358 assert_eq!(ColorSpace::Cmyk.num_components(), 4);
359 }
360
361 #[test]
362 fn test_pixel_format_bytes() {
363 assert_eq!(PixelFormat::Gray.bytes_per_pixel(), 1);
364 assert_eq!(PixelFormat::Rgb.bytes_per_pixel(), 3);
365 assert_eq!(PixelFormat::Rgba.bytes_per_pixel(), 4);
366 }
367
368 #[test]
369 fn test_quant_table_order_conversion() {
370 let natural = [
371 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
372 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
373 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
374 ];
375 let table = QuantTable::from_natural_order(&natural);
376 let recovered = table.to_natural_order();
377 assert_eq!(natural, recovered);
378 }
379}