use std::io::BufRead;
use std::fmt;
use byteorder::{ReadBytesExt, BigEndian};
use types::{Result, Dimensions};
use traits::LoadableMetadata;
use utils::BufReadExt;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CodingProcess {
DctSequential,
DctProgressive,
Lossless
}
impl fmt::Display for CodingProcess {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
CodingProcess::DctSequential => "Sequential DCT",
CodingProcess::DctProgressive => "Progressive DCT",
CodingProcess::Lossless => "Lossless",
})
}
}
impl CodingProcess {
fn from_marker(marker: u8) -> Option<CodingProcess> {
match marker {
0xc0 | 0xc1 | 0xc5 | 0xc9 | 0xcd => Some(CodingProcess::DctSequential),
0xc2 | 0xc6 | 0xca | 0xce => Some(CodingProcess::DctProgressive),
0xc3 | 0xc7 | 0xcb | 0xcf => Some(CodingProcess::Lossless),
_ => None
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum EntropyCoding {
Huffman,
Arithmetic
}
impl fmt::Display for EntropyCoding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
EntropyCoding::Huffman => "Huffman",
EntropyCoding::Arithmetic => "Arithmetic",
})
}
}
impl EntropyCoding {
fn from_marker(marker: u8) -> Option<EntropyCoding> {
match marker {
0xc0 | 0xc1 | 0xc2 | 0xc3 | 0xc5 | 0xc6 | 0xc7 => Some(EntropyCoding::Huffman),
0xc9 | 0xca | 0xcb | 0xcd | 0xce | 0xcf => Some(EntropyCoding::Arithmetic),
_ => None
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Metadata {
pub dimensions: Dimensions,
pub sample_precision: u8,
pub coding_process: CodingProcess,
pub entropy_coding: EntropyCoding,
pub baseline: bool,
pub differential: bool,
}
fn find_marker<R: ?Sized, F>(r: &mut R, name: &str, mut matcher: F) -> Result<u8>
where R: BufRead, F: FnMut(u8) -> bool
{
loop {
if try!(r.skip_until(0xff)) == 0 {
return Err(unexpected_eof!("when searching for {} marker", name));
}
let marker_type = try_if_eof!(r.read_u8(), "when reading marker type");
if marker_type == 0 { continue; }
if matcher(marker_type) {
return Ok(marker_type);
}
}
}
impl LoadableMetadata for Metadata {
fn load<R: ?Sized + BufRead>(r: &mut R) -> Result<Metadata> {
try!(find_marker(r, "SOI", |m| m == 0xd8));
let marker = try!(find_marker(r, "SOF", is_sof_marker));
let size = try_if_eof!(r.read_u16::<BigEndian>(), "when reading SOF marker payload size");
if size <= 8 { return Err(invalid_format!("invalid JPEG frame header size: {}", size));
}
let sample_precision = try_if_eof!(r.read_u8(), "when reading sample precision of the frame");
let h = try_if_eof!(r.read_u16::<BigEndian>(), "when reading JPEG frame height");
let w = try_if_eof!(r.read_u16::<BigEndian>(), "when reading JPEG frame width");
let baseline = marker == 0xc0;
let differential = match marker {
0xc0 | 0xc1 | 0xc2 | 0xc3 | 0xc9 | 0xca | 0xcb => false,
0xc5 | 0xc6 | 0xc7 | 0xcd | 0xce | 0xcf => true,
_ => unreachable!(), };
let coding_process = CodingProcess::from_marker(marker).unwrap();
let entropy_coding = EntropyCoding::from_marker(marker).unwrap();
Ok(Metadata {
dimensions: (w, h).into(),
sample_precision: sample_precision,
coding_process: coding_process,
entropy_coding: entropy_coding,
baseline: baseline,
differential: differential,
})
}
}
fn is_sof_marker(value: u8) -> bool {
match value {
0xc0 | 0xc1 | 0xc2 | 0xc3 | 0xc5 | 0xc6 | 0xc7 | 0xc9 |
0xca | 0xcb | 0xcd | 0xce | 0xcf => true,
_ => false
}
}