Skip to content

Commit 46b3b47

Browse files
Parse jpeg files
1 parent d427235 commit 46b3b47

7 files changed

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

src/jpeg/grammar.rs

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

src/jpeg/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub use decoder::*;
2+
3+
pub mod grammar;
4+
5+
mod decoder;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use wasm_bindgen::prelude::*;
55

66
pub mod font;
77
pub mod image;
8+
pub mod jpeg;
89
pub mod png;
910
pub mod renderer;
1011
pub mod util;

0 commit comments

Comments
 (0)