Skip to content

Commit 1c45cab

Browse files
Parse jpeg files
1 parent d427235 commit 1c45cab

7 files changed

Lines changed: 393 additions & 7 deletions

File tree

src/image/reader.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
ImageExt,
55
ImageKind,
66
},
7+
jpeg::JpegDecoder,
78
png::PngDecoder,
89
};
910
use anyhow::Result;
@@ -18,12 +19,10 @@ impl ImageReader {
1819

1920
let image_kind = image_kind.expect("how do you infer which image decoder to run?");
2021

21-
let image: Box<dyn ImageExt> = Box::new(match image_kind {
22-
ImageKind::Png => PngDecoder::new(&data).decode()?,
23-
ImageKind::Jpeg => {
24-
todo!();
25-
}
26-
});
22+
let image: Box<dyn ImageExt> = match image_kind {
23+
ImageKind::Png => Box::new(PngDecoder::new(&data).decode()?),
24+
ImageKind::Jpeg => Box::new(JpegDecoder::new(&data).decode()?),
25+
};
2726

2827
Ok(image)
2928
}

src/jpeg/decoder.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
use crate::{
2+
eof,
3+
jpeg::grammar::{
4+
Component,
5+
EncodingProcess,
6+
HuffmanTable,
7+
Jpeg,
8+
Marker,
9+
QuantizationTable,
10+
StartOfFrame,
11+
StartOfScan,
12+
JFIF,
13+
},
14+
read,
15+
util::read_bytes::{
16+
U16_BYTES,
17+
U8_BYTES,
18+
},
19+
};
20+
use anyhow::{
21+
anyhow,
22+
ensure,
23+
Result,
24+
};
25+
use std::ops::{
26+
Range,
27+
RangeInclusive,
28+
};
29+
30+
#[derive(Debug)]
31+
pub struct JpegDecoder<'a> {
32+
cursor: usize,
33+
data: &'a [u8],
34+
}
35+
36+
impl<'a> JpegDecoder<'a> {
37+
pub const fn new(data: &'a [u8]) -> Self {
38+
Self { cursor: 0, data }
39+
}
40+
41+
pub fn decode(&mut self) -> Result<Jpeg> {
42+
let _jfif = self.parse_jfif()?;
43+
44+
todo!();
45+
}
46+
47+
fn parse_jfif(&mut self) -> Result<JFIF> {
48+
ensure!(self.read_marker()? == 0xFFD8);
49+
50+
let mut quantization_tables = Vec::with_capacity(4);
51+
let mut huffman_tables = Vec::new();
52+
let mut start_of_frame = None;
53+
let mut start_of_scan = None;
54+
let mut image_data = None;
55+
56+
loop {
57+
match self.read_marker()? {
58+
0xFFE0 => {
59+
self.parse_application_header()?;
60+
}
61+
0xFFDB => {
62+
quantization_tables.push(self.parse_quantization_table()?);
63+
}
64+
0xFFC4 => {
65+
huffman_tables.push(self.parse_huffman_table()?);
66+
}
67+
0xFFDA => {
68+
ensure!(start_of_scan.is_none() && image_data.is_none());
69+
start_of_scan = Some(self.parse_start_of_scan()?);
70+
image_data = Some(self.parse_image_data()?);
71+
72+
break;
73+
}
74+
start_of_frame_marker if (start_of_frame_marker as u8 & 0xF0) == 0xC0 => {
75+
ensure!(start_of_frame.is_none());
76+
start_of_frame = Some(self.parse_start_of_frame(start_of_frame_marker as u8)?);
77+
}
78+
foreign => unimplemented!("{:X}", foreign),
79+
};
80+
}
81+
82+
ensure!(self.read_marker()? == 0xFFD9);
83+
84+
Ok(JFIF {
85+
quantization_tables,
86+
huffman_tables,
87+
start_of_frame: start_of_frame.ok_or_else(|| anyhow!("expected start of frame"))?,
88+
start_of_scan: start_of_scan.ok_or_else(|| anyhow!("expected start of scan"))?,
89+
image_data: image_data.ok_or_else(|| anyhow!("expected image data"))?,
90+
})
91+
}
92+
93+
fn parse_application_header(&mut self) -> Result<()> {
94+
let offset = self.cursor;
95+
let length = self.read_u16()?;
96+
97+
ensure!(self.read_fixed::<5>()? == b"JFIF\0");
98+
99+
self.cursor = offset + length as usize;
100+
101+
Ok(())
102+
}
103+
104+
fn parse_quantization_table(&mut self) -> Result<QuantizationTable> {
105+
let offset = self.cursor;
106+
let length = self.read_u16()? as usize;
107+
108+
let flag = self.read_u8()?;
109+
110+
let quantization_table = QuantizationTable {
111+
flag,
112+
element_range: Range {
113+
start: self.cursor,
114+
end: offset + length,
115+
},
116+
};
117+
118+
self.cursor = offset + length;
119+
120+
Ok(quantization_table)
121+
}
122+
123+
fn parse_start_of_frame(&mut self, start_of_frame: u8) -> Result<StartOfFrame> {
124+
let encoding_process = EncodingProcess::try_from(start_of_frame & 0b1111)?;
125+
126+
let offset = self.cursor;
127+
let length = self.read_u16()?;
128+
129+
let start_of_frame = StartOfFrame {
130+
encoding_process,
131+
sample_precision: self.read_u8()?,
132+
lines: self.read_u16()?,
133+
samples_per_line: self.read_u16()?,
134+
components: {
135+
let number_of_image_components = self.read_u8()?;
136+
self.read_vec(number_of_image_components as usize, Self::parse_component)?
137+
},
138+
};
139+
140+
ensure!(self.cursor == offset + length as usize);
141+
142+
Ok(start_of_frame)
143+
}
144+
145+
fn parse_component(&mut self) -> Result<Component> {
146+
Ok(Component {
147+
identifier: self.read_u8()?,
148+
sampling_factor: self.read_u8()?,
149+
quantization_table_destination_selector: self.read_u8()?,
150+
})
151+
}
152+
153+
fn parse_huffman_table(&mut self) -> Result<HuffmanTable> {
154+
let offset = self.cursor;
155+
let length = self.read_u16()? as usize;
156+
157+
let flag = self.read_u8()?;
158+
159+
let huffman_table = HuffmanTable {
160+
flag,
161+
element_range: Range {
162+
start: self.cursor,
163+
end: offset + length,
164+
},
165+
};
166+
167+
self.cursor = offset + length;
168+
169+
Ok(huffman_table)
170+
}
171+
172+
fn parse_start_of_scan(&mut self) -> Result<StartOfScan> {
173+
let offset = self.cursor;
174+
let length = self.read_u16()?;
175+
176+
let number_of_image_components = self.read_u8()?;
177+
let components = self.read_vec(number_of_image_components as usize, |this| {
178+
Ok((this.read_u8()?, this.read_u8()?))
179+
})?;
180+
181+
let start_of_scan = StartOfScan {
182+
components,
183+
spectral_select: RangeInclusive::new(self.read_u8()?, self.read_u8()?),
184+
approximation: self.read_u8()?,
185+
};
186+
187+
ensure!(self.cursor == offset + length as usize);
188+
189+
Ok(start_of_scan)
190+
}
191+
192+
fn parse_image_data(&mut self) -> Result<Range<usize>> {
193+
let range = Range {
194+
start: self.cursor,
195+
end: {
196+
while self.data[self.cursor..self.cursor + U16_BYTES] != [0xFF, 0xD9] {
197+
self.cursor += 1;
198+
}
199+
200+
self.cursor
201+
},
202+
};
203+
204+
Ok(range)
205+
}
206+
207+
eof!();
208+
read!(read_u8, u8, U8_BYTES);
209+
read!(read_u16, u16, U16_BYTES);
210+
read!(read_marker, Marker, U16_BYTES);
211+
212+
fn read_fixed<const N: usize>(&mut self) -> Result<&'a [u8; N]> {
213+
self.eof(N)?;
214+
let bs = &self.data[self.cursor..self.cursor + N];
215+
self.cursor += N;
216+
217+
Ok(bs.try_into()?)
218+
}
219+
220+
fn read_vec<T>(
221+
&mut self,
222+
capacity: usize,
223+
read_fn: impl Fn(&mut Self) -> Result<T>,
224+
) -> Result<Vec<T>> {
225+
let mut list = Vec::with_capacity(capacity);
226+
227+
for _ in 0..capacity {
228+
list.push(read_fn(self)?)
229+
}
230+
231+
Ok(list)
232+
}
233+
}

src/jpeg/grammar.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use crate::image::grammar::{
2+
ColorType,
3+
ImageExt,
4+
};
5+
use anyhow::bail;
6+
use std::{
7+
borrow::Cow,
8+
ops::{
9+
Range,
10+
RangeInclusive,
11+
},
12+
};
13+
14+
pub type Marker = u16;
15+
16+
#[derive(Debug)]
17+
pub enum Precision {
18+
Eight,
19+
Sixteen,
20+
}
21+
22+
#[derive(Debug)]
23+
pub struct QuantizationTable {
24+
pub flag: u8,
25+
pub element_range: Range<usize>,
26+
}
27+
28+
impl QuantizationTable {
29+
pub const fn precision(&self) -> Precision {
30+
match self.flag >> 4 == 1 {
31+
false => Precision::Eight,
32+
true => Precision::Sixteen,
33+
}
34+
}
35+
36+
pub const fn table_identifier(&self) -> u8 {
37+
self.flag & 0b1111
38+
}
39+
}
40+
41+
#[derive(Debug)]
42+
pub struct HuffmanTable {
43+
pub flag: u8,
44+
pub element_range: Range<usize>,
45+
}
46+
47+
#[derive(Debug)]
48+
pub enum EncodingProcess {
49+
BaselineDCT = 0,
50+
HuffmanExtendedSequentialDCT = 1,
51+
HuffmanProgressiveDCT = 2,
52+
HuffmanLossless = 3,
53+
ArithmeticExtendedSequentialDCT = 9,
54+
ArithmeticProgressiveDCT = 10,
55+
ArithmeticLossless = 11,
56+
}
57+
58+
impl TryFrom<u8> for EncodingProcess {
59+
type Error = anyhow::Error;
60+
61+
fn try_from(value: u8) -> Result<Self, Self::Error> {
62+
let res = match value {
63+
0 => Self::BaselineDCT,
64+
1 => Self::HuffmanExtendedSequentialDCT,
65+
2 => Self::HuffmanProgressiveDCT,
66+
3 => Self::HuffmanLossless,
67+
9 => Self::ArithmeticExtendedSequentialDCT,
68+
10 => Self::ArithmeticProgressiveDCT,
69+
11 => Self::ArithmeticLossless,
70+
foreign => bail!("Encountered foreign encoding process: {foreign}"),
71+
};
72+
73+
Ok(res)
74+
}
75+
}
76+
77+
#[derive(Debug)]
78+
pub struct StartOfFrame {
79+
pub encoding_process: EncodingProcess,
80+
pub sample_precision: u8,
81+
pub lines: u16,
82+
pub samples_per_line: u16,
83+
pub components: Vec<Component>,
84+
}
85+
86+
#[derive(Debug)]
87+
pub struct Component {
88+
pub identifier: u8,
89+
pub sampling_factor: u8,
90+
pub quantization_table_destination_selector: u8,
91+
}
92+
93+
#[derive(Debug)]
94+
pub struct StartOfScan {
95+
pub components: Vec<(u8, u8)>,
96+
pub spectral_select: RangeInclusive<u8>,
97+
pub approximation: u8,
98+
}
99+
100+
#[derive(Debug)]
101+
pub struct JFIF {
102+
pub quantization_tables: Vec<QuantizationTable>,
103+
pub huffman_tables: Vec<HuffmanTable>,
104+
pub start_of_frame: StartOfFrame,
105+
pub start_of_scan: StartOfScan,
106+
pub image_data: Range<usize>,
107+
}
108+
109+
#[derive(Debug)]
110+
pub struct Jpeg {}
111+
112+
impl ImageExt for Jpeg {
113+
fn width(&self) -> u32 {
114+
todo!()
115+
}
116+
117+
fn height(&self) -> u32 {
118+
todo!()
119+
}
120+
121+
fn gamma(&self) -> u32 {
122+
todo!()
123+
}
124+
125+
fn color_type(&self) -> ColorType {
126+
todo!()
127+
}
128+
129+
fn rgb8(&self) -> Cow<'_, [u8]> {
130+
todo!()
131+
}
132+
133+
fn rgba8(&self) -> Cow<'_, [u8]> {
134+
todo!()
135+
}
136+
137+
fn bitmap(&self) -> Cow<'_, [u32]> {
138+
todo!()
139+
}
140+
}

0 commit comments

Comments
 (0)