Skip to content

Format code using 'cargo fmt' #154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions benches/decoding_benchmark.rs
Original file line number Diff line number Diff line change
@@ -18,20 +18,20 @@ fn read_metadata(image: &[u8]) -> ImageInfo {

fn main() {
let mut c = Criterion::default().configure_from_args();
c.bench_function("decode a 512x512 JPEG", |b| b.iter(|| {
read_image(include_bytes!("tower.jpg"))
}));
c.bench_function("decode a 512x512 JPEG", |b| {
b.iter(|| read_image(include_bytes!("tower.jpg")))
});

c.bench_function("decode a 512x512 progressive JPEG", |b| b.iter(|| {
read_image(include_bytes!("tower_progressive.jpg"))
}));
c.bench_function("decode a 512x512 progressive JPEG", |b| {
b.iter(|| read_image(include_bytes!("tower_progressive.jpg")))
});

c.bench_function("decode a 512x512 grayscale JPEG", |b| b.iter(|| {
read_image(include_bytes!("tower_grayscale.jpg"))
}));
c.bench_function("decode a 512x512 grayscale JPEG", |b| {
b.iter(|| read_image(include_bytes!("tower_grayscale.jpg")))
});

c.bench_function("extract metadata from an image", |b| b.iter(|| {
read_metadata(include_bytes!("tower.jpg"))
}));
c.bench_function("extract metadata from an image", |b| {
b.iter(|| read_metadata(include_bytes!("tower.jpg")))
});
c.final_summary();
}
}
15 changes: 8 additions & 7 deletions examples/decode.rs
Original file line number Diff line number Diff line change
@@ -26,18 +26,19 @@ fn main() {
encoder.set_depth(png::BitDepth::Eight);

match info.pixel_format {
jpeg::PixelFormat::L8 => encoder.set_color(png::ColorType::Grayscale),
jpeg::PixelFormat::RGB24 => encoder.set_color(png::ColorType::RGB),
jpeg::PixelFormat::L8 => encoder.set_color(png::ColorType::Grayscale),
jpeg::PixelFormat::RGB24 => encoder.set_color(png::ColorType::RGB),
jpeg::PixelFormat::CMYK32 => {
data = cmyk_to_rgb(&mut data);
encoder.set_color(png::ColorType::RGB)
},
}
};

encoder.write_header()
.expect("writing png header failed")
.write_image_data(&data)
.expect("png encoding failed");
encoder
.write_header()
.expect("writing png header failed")
.write_image_data(&data)
.expect("png encoding failed");
}

fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
585 changes: 364 additions & 221 deletions src/decoder.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -45,10 +45,10 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Format(ref desc) => write!(f, "invalid JPEG format: {}", desc),
Error::Format(ref desc) => write!(f, "invalid JPEG format: {}", desc),
Error::Unsupported(ref feat) => write!(f, "unsupported JPEG feature: {:?}", feat),
Error::Io(ref err) => err.fmt(f),
Error::Internal(ref err) => err.fmt(f),
Error::Io(ref err) => err.fmt(f),
Error::Internal(ref err) => err.fmt(f),
}
}
}
168 changes: 113 additions & 55 deletions src/huffman.rs
Original file line number Diff line number Diff line change
@@ -35,11 +35,10 @@ impl HuffmanDecoder {
if size > 0 {
self.consume_bits(size);
Ok(value)
}
else {
} else {
let bits = self.peek_bits(16);

for i in LUT_BITS .. 16 {
for i in LUT_BITS..16 {
let code = (bits >> (15 - i)) as i32;

if code <= table.maxcode[i as usize] {
@@ -54,7 +53,11 @@ impl HuffmanDecoder {
}
}

pub fn decode_fast_ac<R: Read>(&mut self, reader: &mut R, table: &HuffmanTable) -> Result<Option<(i16, u8)>> {
pub fn decode_fast_ac<R: Read>(
&mut self,
reader: &mut R,
table: &HuffmanTable,
) -> Result<Option<(i16, u8)>> {
if let Some(ref ac_lut) = table.ac_lut {
if self.num_bits < LUT_BITS {
self.read_bits(reader)?;
@@ -141,8 +144,12 @@ impl HuffmanDecoder {
}

match next_byte {
0x00 => return Err(Error::Format("FF 00 found where marker was expected".to_owned())),
_ => self.marker = Some(Marker::from_u8(next_byte).unwrap()),
0x00 => {
return Err(Error::Format(
"FF 00 found where marker was expected".to_owned(),
))
}
_ => self.marker = Some(Marker::from_u8(next_byte).unwrap()),
}

continue;
@@ -195,7 +202,7 @@ impl HuffmanTable {
let mut maxcode = [-1i32; 16];
let mut j = 0;

for i in 0 .. 16 {
for i in 0..16 {
if bits[i] != 0 {
delta[i] = j as i32 - huffcode[j] as i32;
j += bits[i] as usize;
@@ -206,11 +213,15 @@ impl HuffmanTable {
// Build a lookup table for faster decoding.
let mut lut = [(0u8, 0u8); 1 << LUT_BITS];

for (i, &size) in huffsize.iter().enumerate().filter(|&(_, &size)| size <= LUT_BITS) {
for (i, &size) in huffsize
.iter()
.enumerate()
.filter(|&(_, &size)| size <= LUT_BITS)
{
let bits_remaining = LUT_BITS - size;
let start = (huffcode[i] << bits_remaining) as usize;

for j in 0 .. 1 << bits_remaining {
for j in 0..1 << bits_remaining {
lut[start + j] = (values[i], size);
}
}
@@ -227,15 +238,17 @@ impl HuffmanTable {
let magnitude_category = value & 0x0f;

if magnitude_category > 0 && size + magnitude_category <= LUT_BITS {
let unextended_ac_value = (((i << size) & ((1 << LUT_BITS) - 1)) >> (LUT_BITS - magnitude_category)) as u16;
let unextended_ac_value = (((i << size) & ((1 << LUT_BITS) - 1))
>> (LUT_BITS - magnitude_category))
as u16;
let ac_value = extend(unextended_ac_value, magnitude_category);

table[i] = (ac_value, (run_length << 4) | (size + magnitude_category));
}
}

Some(table)
},
}
};

Ok(HuffmanTable {
@@ -251,13 +264,14 @@ impl HuffmanTable {
// Section C.2
fn derive_huffman_codes(bits: &[u8; 16]) -> Result<(Vec<u16>, Vec<u8>)> {
// Figure C.1
let huffsize = bits.iter()
.enumerate()
.fold(Vec::new(), |mut acc, (i, &value)| {
let mut repeated_size: Vec<u8> = repeat((i + 1) as u8).take(value as usize).collect();
acc.append(&mut repeated_size);
acc
});
let huffsize = bits
.iter()
.enumerate()
.fold(Vec::new(), |mut acc, (i, &value)| {
let mut repeated_size: Vec<u8> = repeat((i + 1) as u8).take(value as usize).collect();
acc.append(&mut repeated_size);
acc
});

// Figure C.2
let mut huffcode = vec![0u16; huffsize.len()];
@@ -289,55 +303,99 @@ fn derive_huffman_codes(bits: &[u8; 16]) -> Result<(Vec<u16>, Vec<u8>)> {
// MJPEG frames and decode them with a regular JPEG decoder, but you have to prepend the DHT
// segment to them, or else the decoder won't have any idea how to decompress the data.
// The exact table necessary is given in the OpenDML spec.""
pub fn fill_default_mjpeg_tables(scan: &ScanInfo,
dc_huffman_tables: &mut[Option<HuffmanTable>],
ac_huffman_tables: &mut[Option<HuffmanTable>]) {
pub fn fill_default_mjpeg_tables(
scan: &ScanInfo,
dc_huffman_tables: &mut [Option<HuffmanTable>],
ac_huffman_tables: &mut [Option<HuffmanTable>],
) {
// Section K.3.3

if dc_huffman_tables[0].is_none() && scan.dc_table_indices.iter().any(|&i| i == 0) {
// Table K.3
dc_huffman_tables[0] = Some(HuffmanTable::new(
&[0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B], HuffmanTableClass::DC).unwrap());
dc_huffman_tables[0] = Some(
HuffmanTable::new(
&[
0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
],
&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
],
HuffmanTableClass::DC,
)
.unwrap(),
);
}
if dc_huffman_tables[1].is_none() && scan.dc_table_indices.iter().any(|&i| i == 1) {
// Table K.4
dc_huffman_tables[1] = Some(HuffmanTable::new(
&[0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00],
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B], HuffmanTableClass::DC).unwrap());
dc_huffman_tables[1] = Some(
HuffmanTable::new(
&[
0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00,
],
&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
],
HuffmanTableClass::DC,
)
.unwrap(),
);
}
if ac_huffman_tables[0].is_none() && scan.ac_table_indices.iter().any(|&i| i == 0) {
// Table K.5
ac_huffman_tables[0] = Some(HuffmanTable::new(
&[0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D],
&[0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
], HuffmanTableClass::AC).unwrap());
ac_huffman_tables[0] = Some(
HuffmanTable::new(
&[
0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
0x00, 0x01, 0x7D,
],
&[
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42,
0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A,
0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35,
0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84,
0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1,
0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
],
HuffmanTableClass::AC,
)
.unwrap(),
);
}
if ac_huffman_tables[1].is_none() && scan.ac_table_indices.iter().any(|&i| i == 1) {
// Table K.6
ac_huffman_tables[1] = Some(HuffmanTable::new(
&[0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77],
&[0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
], HuffmanTableClass::AC).unwrap());
ac_huffman_tables[1] = Some(
HuffmanTable::new(
&[
0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
0x01, 0x02, 0x77,
],
&[
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1,
0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24,
0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82,
0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA,
0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4,
0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
],
HuffmanTableClass::AC,
)
.unwrap(),
);
}
}
355 changes: 274 additions & 81 deletions src/idct.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@
#![forbid(unsafe_code)]

extern crate byteorder;
#[cfg(feature="rayon")]
#[cfg(feature = "rayon")]
extern crate rayon;

pub use decoder::{Decoder, ImageInfo, PixelFormat};
2 changes: 1 addition & 1 deletion src/marker.rs
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ impl Marker {
match n {
0x00 => None, // Byte stuffing
0x01 => Some(TEM),
0x02 ..= 0xBF => Some(RES),
0x02..=0xBF => Some(RES),
0xC0 => Some(SOF(0)),
0xC1 => Some(SOF(1)),
0xC2 => Some(SOF(2)),
303 changes: 224 additions & 79 deletions src/parser.rs

Large diffs are not rendered by default.

239 changes: 139 additions & 100 deletions src/upsampler.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ use parser::Component;

pub struct Upsampler {
components: Vec<UpsamplerComponent>,
line_buffer_size: usize
line_buffer_size: usize,
}

struct UpsamplerComponent {
@@ -14,17 +14,33 @@ struct UpsamplerComponent {
}

impl Upsampler {
pub fn new(components: &[Component], output_width: u16, output_height: u16) -> Result<Upsampler> {
let h_max = components.iter().map(|c| c.horizontal_sampling_factor).max().unwrap();
let v_max = components.iter().map(|c| c.vertical_sampling_factor).max().unwrap();
pub fn new(
components: &[Component],
output_width: u16,
output_height: u16,
) -> Result<Upsampler> {
let h_max = components
.iter()
.map(|c| c.horizontal_sampling_factor)
.max()
.unwrap();
let v_max = components
.iter()
.map(|c| c.vertical_sampling_factor)
.max()
.unwrap();
let mut upsampler_components = Vec::with_capacity(components.len());

for component in components {
let upsampler = choose_upsampler((component.horizontal_sampling_factor,
component.vertical_sampling_factor),
(h_max, v_max),
output_width,
output_height)?;
let upsampler = choose_upsampler(
(
component.horizontal_sampling_factor,
component.vertical_sampling_factor,
),
(h_max, v_max),
output_width,
output_height,
)?;
upsampler_components.push(UpsamplerComponent {
upsampler: upsampler,
width: component.size.width as usize,
@@ -33,29 +49,38 @@ impl Upsampler {
});
}

let buffer_size = components.iter().map(|c| c.size.width).max().unwrap() as usize * h_max as usize;
let buffer_size =
components.iter().map(|c| c.size.width).max().unwrap() as usize * h_max as usize;

Ok(Upsampler {
components: upsampler_components,
line_buffer_size: buffer_size
line_buffer_size: buffer_size,
})
}

pub fn upsample_and_interleave_row(&self, component_data: &[Vec<u8>], row: usize, output_width: usize, output: &mut [u8]) {
pub fn upsample_and_interleave_row(
&self,
component_data: &[Vec<u8>],
row: usize,
output_width: usize,
output: &mut [u8],
) {
let component_count = component_data.len();
let mut line_buffer = vec![0u8; self.line_buffer_size];

debug_assert_eq!(component_count, self.components.len());

for (i, component) in self.components.iter().enumerate() {
component.upsampler.upsample_row(&component_data[i],
component.width,
component.height,
component.row_stride,
row,
output_width,
&mut line_buffer);
for x in 0 .. output_width {
component.upsampler.upsample_row(
&component_data[i],
component.width,
component.height,
component.row_stride,
row,
output_width,
&mut line_buffer,
);
for x in 0..output_width {
output[x * component_count + i] = line_buffer[x];
}
}
@@ -69,81 +94,88 @@ struct UpsamplerH2V2;

struct UpsamplerGeneric {
horizontal_scaling_factor: u8,
vertical_scaling_factor: u8
vertical_scaling_factor: u8,
}

fn choose_upsampler(sampling_factors: (u8, u8),
max_sampling_factors: (u8, u8),
output_width: u16,
output_height: u16) -> Result<Box<dyn Upsample + Sync>> {
fn choose_upsampler(
sampling_factors: (u8, u8),
max_sampling_factors: (u8, u8),
output_width: u16,
output_height: u16,
) -> Result<Box<dyn Upsample + Sync>> {
let h1 = sampling_factors.0 == max_sampling_factors.0 || output_width == 1;
let v1 = sampling_factors.1 == max_sampling_factors.1 || output_height == 1;
let h2 = sampling_factors.0 * 2 == max_sampling_factors.0;
let v2 = sampling_factors.1 * 2 == max_sampling_factors.1;

if h1 && v1 {
Ok(Box::new(UpsamplerH1V1))
}
else if h2 && v1 {
} else if h2 && v1 {
Ok(Box::new(UpsamplerH2V1))
}
else if h1 && v2 {
} else if h1 && v2 {
Ok(Box::new(UpsamplerH1V2))
}
else if h2 && v2 {
} else if h2 && v2 {
Ok(Box::new(UpsamplerH2V2))
}
else {
if max_sampling_factors.0 % sampling_factors.0 != 0 || max_sampling_factors.1 % sampling_factors.1 != 0 {
Err(Error::Unsupported(UnsupportedFeature::NonIntegerSubsamplingRatio))
}
else {
} else {
if max_sampling_factors.0 % sampling_factors.0 != 0
|| max_sampling_factors.1 % sampling_factors.1 != 0
{
Err(Error::Unsupported(
UnsupportedFeature::NonIntegerSubsamplingRatio,
))
} else {
Ok(Box::new(UpsamplerGeneric {
horizontal_scaling_factor: max_sampling_factors.0 / sampling_factors.0,
vertical_scaling_factor: max_sampling_factors.1 / sampling_factors.1
vertical_scaling_factor: max_sampling_factors.1 / sampling_factors.1,
}))
}
}
}

trait Upsample {
fn upsample_row(&self,
input: &[u8],
input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8]);
fn upsample_row(
&self,
input: &[u8],
input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8],
);
}

impl Upsample for UpsamplerH1V1 {
fn upsample_row(&self,
input: &[u8],
_input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8]) {
let input = &input[row * row_stride ..];

for i in 0 .. output_width {
fn upsample_row(
&self,
input: &[u8],
_input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8],
) {
let input = &input[row * row_stride..];

for i in 0..output_width {
output[i] = input[i];
}
}
}

impl Upsample for UpsamplerH2V1 {
fn upsample_row(&self,
input: &[u8],
input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8]) {
let input = &input[row * row_stride ..];
fn upsample_row(
&self,
input: &[u8],
input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8],
) {
let input = &input[row * row_stride..];

if input_width == 1 {
output[0] = input[0];
@@ -154,56 +186,61 @@ impl Upsample for UpsamplerH2V1 {
output[0] = input[0];
output[1] = ((input[0] as u32 * 3 + input[1] as u32 + 2) >> 2) as u8;

for i in 1 .. input_width - 1 {
for i in 1..input_width - 1 {
let sample = 3 * input[i] as u32 + 2;
output[i * 2] = ((sample + input[i - 1] as u32) >> 2) as u8;
output[i * 2] = ((sample + input[i - 1] as u32) >> 2) as u8;
output[i * 2 + 1] = ((sample + input[i + 1] as u32) >> 2) as u8;
}

output[(input_width - 1) * 2] = ((input[input_width - 1] as u32 * 3 + input[input_width - 2] as u32 + 2) >> 2) as u8;
output[(input_width - 1) * 2] =
((input[input_width - 1] as u32 * 3 + input[input_width - 2] as u32 + 2) >> 2) as u8;
output[(input_width - 1) * 2 + 1] = input[input_width - 1];
}
}

impl Upsample for UpsamplerH1V2 {
fn upsample_row(&self,
input: &[u8],
_input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8]) {
fn upsample_row(
&self,
input: &[u8],
_input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8],
) {
let row_near = row as f32 / 2.0;
// If row_near's fractional is 0.0 we want row_far to be the previous row and if it's 0.5 we
// want it to be the next row.
let row_far = (row_near + row_near.fract() * 3.0 - 0.25).min((input_height - 1) as f32);

let input_near = &input[row_near as usize * row_stride ..];
let input_far = &input[row_far as usize * row_stride ..];
let input_near = &input[row_near as usize * row_stride..];
let input_far = &input[row_far as usize * row_stride..];

for i in 0 .. output_width {
for i in 0..output_width {
output[i] = ((3 * input_near[i] as u32 + input_far[i] as u32 + 2) >> 2) as u8;
}
}
}

impl Upsample for UpsamplerH2V2 {
fn upsample_row(&self,
input: &[u8],
input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8]) {
fn upsample_row(
&self,
input: &[u8],
input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8],
) {
let row_near = row as f32 / 2.0;
// If row_near's fractional is 0.0 we want row_far to be the previous row and if it's 0.5 we
// want it to be the next row.
let row_far = (row_near + row_near.fract() * 3.0 - 0.25).min((input_height - 1) as f32);

let input_near = &input[row_near as usize * row_stride ..];
let input_far = &input[row_far as usize * row_stride ..];
let input_near = &input[row_near as usize * row_stride..];
let input_far = &input[row_far as usize * row_stride..];

if input_width == 1 {
let value = ((3 * input_near[0] as u32 + input_far[0] as u32 + 2) >> 2) as u8;
@@ -215,12 +252,12 @@ impl Upsample for UpsamplerH2V2 {
let mut t1 = 3 * input_near[0] as u32 + input_far[0] as u32;
output[0] = ((t1 + 2) >> 2) as u8;

for i in 1 .. input_width {
for i in 1..input_width {
let t0 = t1;
t1 = 3 * input_near[i] as u32 + input_far[i] as u32;

output[i * 2 - 1] = ((3 * t0 + t1 + 8) >> 4) as u8;
output[i * 2] = ((3 * t1 + t0 + 8) >> 4) as u8;
output[i * 2] = ((3 * t1 + t0 + 8) >> 4) as u8;
}

output[input_width * 2 - 1] = ((t1 + 2) >> 2) as u8;
@@ -229,14 +266,16 @@ impl Upsample for UpsamplerH2V2 {

impl Upsample for UpsamplerGeneric {
// Uses nearest neighbor sampling
fn upsample_row(&self,
input: &[u8],
input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8]) {
fn upsample_row(
&self,
input: &[u8],
input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8],
) {
let mut index = 0;
let start = (row / self.vertical_scaling_factor as usize) * row_stride;
let input = &input[start..(start + input_width)];
23 changes: 18 additions & 5 deletions src/worker/immediate.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::{RowData, Worker};
use decoder::MAX_COMPONENTS;
use error::Result;
use idct::dequantize_and_idct_block;
use parser::Component;
use std::mem;
use std::sync::Arc;
use parser::Component;
use super::{RowData, Worker};

pub struct ImmediateWorker {
offsets: [usize; MAX_COMPONENTS],
@@ -26,7 +26,13 @@ impl ImmediateWorker {
assert!(self.results[data.index].is_empty());

self.offsets[data.index] = 0;
self.results[data.index].resize(data.component.block_size.width as usize * data.component.block_size.height as usize * data.component.dct_scale * data.component.dct_scale, 0u8);
self.results[data.index].resize(
data.component.block_size.width as usize
* data.component.block_size.height as usize
* data.component.dct_scale
* data.component.dct_scale,
0u8,
);
self.components[data.index] = Some(data.component);
self.quantization_tables[data.index] = Some(data.quantization_table);
}
@@ -35,7 +41,8 @@ impl ImmediateWorker {

let component = self.components[index].as_ref().unwrap();
let quantization_table = self.quantization_tables[index].as_ref().unwrap();
let block_count = component.block_size.width as usize * component.vertical_sampling_factor as usize;
let block_count =
component.block_size.width as usize * component.vertical_sampling_factor as usize;
let line_stride = component.block_size.width as usize * component.dct_scale;

assert_eq!(data.len(), block_count * 64);
@@ -47,7 +54,13 @@ impl ImmediateWorker {
let coefficients = &data[i * 64..(i + 1) * 64];
let output = &mut self.results[index][self.offsets[index] + y * line_stride + x..];

dequantize_and_idct_block(component.dct_scale, coefficients, quantization_table, line_stride, output);
dequantize_and_idct_block(
component.dct_scale,
coefficients,
quantization_table,
line_stride,
output,
);
}

self.offsets[index] += block_count * component.dct_scale * component.dct_scale;
6 changes: 3 additions & 3 deletions src/worker/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
mod threaded;
mod immediate;
mod threaded;

#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
pub use self::threaded::ThreadedWorker as PlatformWorker;
#[cfg(any(target_arch = "asmjs", target_arch = "wasm32"))]
pub use self::immediate::ImmediateWorker as PlatformWorker;
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
pub use self::threaded::ThreadedWorker as PlatformWorker;

use error::Result;
use parser::Component;
24 changes: 16 additions & 8 deletions src/worker/threaded.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::immediate::ImmediateWorker;
use super::{RowData, Worker};
use error::Result;
use std::sync::mpsc::{self, Sender};
use std::thread;
use super::{RowData, Worker};
use super::immediate::ImmediateWorker;

enum WorkerMsg {
Start(RowData),
@@ -26,28 +26,36 @@ impl Worker for ThreadedWorker {
match message {
WorkerMsg::Start(data) => {
worker.start_immediate(data);
},
}
WorkerMsg::AppendRow(row) => {
worker.append_row_immediate(row);
},
}
WorkerMsg::GetResult((index, chan)) => {
let _ = chan.send(worker.get_result_immediate(index));
},
}
}
}
})?;

Ok(ThreadedWorker { sender: tx })
}
fn start(&mut self, row_data: RowData) -> Result<()> {
Ok(self.sender.send(WorkerMsg::Start(row_data)).expect("jpeg-decoder worker thread error"))
Ok(self
.sender
.send(WorkerMsg::Start(row_data))
.expect("jpeg-decoder worker thread error"))
}
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
Ok(self.sender.send(WorkerMsg::AppendRow(row)).expect("jpeg-decoder worker thread error"))
Ok(self
.sender
.send(WorkerMsg::AppendRow(row))
.expect("jpeg-decoder worker thread error"))
}
fn get_result(&mut self, index: usize) -> Result<Vec<u8>> {
let (tx, rx) = mpsc::channel();
self.sender.send(WorkerMsg::GetResult((index, tx))).expect("jpeg-decoder worker thread error");
self.sender
.send(WorkerMsg::GetResult((index, tx)))
.expect("jpeg-decoder worker thread error");
Ok(rx.recv().expect("jpeg-decoder worker thread error"))
}
}
8 changes: 6 additions & 2 deletions tests/lib.rs
Original file line number Diff line number Diff line change
@@ -2,16 +2,20 @@ extern crate jpeg_decoder as jpeg;
extern crate png;
extern crate walkdir;

use std::path::Path;
use std::fs::File;
use std::path::Path;

mod common;
mod crashtest;
mod reftest;

#[test]
fn read_info() {
let path = Path::new("tests").join("reftest").join("images").join("mozilla").join("jpg-progressive.jpg");
let path = Path::new("tests")
.join("reftest")
.join("images")
.join("mozilla")
.join("jpg-progressive.jpg");

let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
let ref_data = decoder.decode().unwrap();
77 changes: 51 additions & 26 deletions tests/reftest/mod.rs
Original file line number Diff line number Diff line change
@@ -19,9 +19,14 @@ fn reftest() {
fn reftest_scaled() {
let base = &Path::new("tests").join("reftest").join("images");
reftest_scaled_file(&base.join("rgb.jpg"), 500, 333, &base.join("rgb.png"));
reftest_scaled_file(&base.join("rgb.jpg"), 250, 167, &base.join("rgb_250x167.png"));
reftest_scaled_file(&base.join("rgb.jpg"), 125, 84, &base.join("rgb_125x84.png"));
reftest_scaled_file(&base.join("rgb.jpg"), 63, 42, &base.join("rgb_63x42.png"));
reftest_scaled_file(
&base.join("rgb.jpg"),
250,
167,
&base.join("rgb_250x167.png"),
);
reftest_scaled_file(&base.join("rgb.jpg"), 125, 84, &base.join("rgb_125x84.png"));
reftest_scaled_file(&base.join("rgb.jpg"), 63, 42, &base.join("rgb_63x42.png"));
}

fn reftest_file(path: &Path) {
@@ -39,7 +44,9 @@ fn reftest_scaled_file(path: &Path, width: u16, height: u16, ref_path: &Path) {
}

fn reftest_decoder<T: std::io::Read>(mut decoder: jpeg::Decoder<T>, path: &Path, ref_path: &Path) {
let mut data = decoder.decode().expect(&format!("failed to decode file: {:?}", path));
let mut data = decoder
.decode()
.expect(&format!("failed to decode file: {:?}", path));
let info = decoder.info().unwrap();
let mut pixel_format = info.pixel_format;

@@ -49,14 +56,18 @@ fn reftest_decoder<T: std::io::Read>(mut decoder: jpeg::Decoder<T>, path: &Path,
}

let ref_file = File::open(ref_path).unwrap();
let (ref_info, mut ref_reader) = png::Decoder::new(ref_file).read_info().expect("png failed to read info");
let (ref_info, mut ref_reader) = png::Decoder::new(ref_file)
.read_info()
.expect("png failed to read info");

assert_eq!(ref_info.width, info.width as u32);
assert_eq!(ref_info.height, info.height as u32);
assert_eq!(ref_info.bit_depth, png::BitDepth::Eight);

let mut ref_data = vec![0; ref_info.buffer_size()];
ref_reader.next_frame(&mut ref_data).expect("png decode failed");
ref_reader
.next_frame(&mut ref_data)
.expect("png decode failed");
let mut ref_pixel_format = ref_info.color_type;

if ref_pixel_format == png::ColorType::RGBA {
@@ -73,32 +84,46 @@ fn reftest_decoder<T: std::io::Read>(mut decoder: jpeg::Decoder<T>, path: &Path,
assert_eq!(data.len(), ref_data.len());

let mut max_diff = 0;
let pixels: Vec<u8> = data.iter().zip(ref_data.iter()).map(|(&a, &b)| {
let diff = (a as i16 - b as i16).abs();
max_diff = cmp::max(diff, max_diff);

// FIXME: Only a diff of 1 should be allowed?
if diff <= 2 {
// White for correct
0xFF
} else {
// "1100" in the RGBA channel with an error for an incorrect value
// This results in some number of C0 and FFs, which is much more
// readable (and distinguishable) than the previous difference-wise
// scaling but does not require reconstructing the actual RGBA pixel.
0xC0
}
}).collect();
let pixels: Vec<u8> = data
.iter()
.zip(ref_data.iter())
.map(|(&a, &b)| {
let diff = (a as i16 - b as i16).abs();
max_diff = cmp::max(diff, max_diff);

// FIXME: Only a diff of 1 should be allowed?
if diff <= 2 {
// White for correct
0xFF
} else {
// "1100" in the RGBA channel with an error for an incorrect value
// This results in some number of C0 and FFs, which is much more
// readable (and distinguishable) than the previous difference-wise
// scaling but does not require reconstructing the actual RGBA pixel.
0xC0
}
})
.collect();

if pixels.iter().any(|&a| a < 255) {
let output_path = path.with_file_name(format!("{}-diff.png", path.file_stem().unwrap().to_str().unwrap()));
let output_path = path.with_file_name(format!(
"{}-diff.png",
path.file_stem().unwrap().to_str().unwrap()
));
let output = File::create(&output_path).unwrap();
let mut encoder = png::Encoder::new(output, info.width as u32, info.height as u32);
encoder.set_depth(png::BitDepth::Eight);
encoder.set_color(ref_pixel_format);
encoder.write_header().expect("png failed to write header").write_image_data(&pixels).expect("png failed to write data");

panic!("decoding difference: {:?}, maximum difference was {}", output_path, max_diff);
encoder
.write_header()
.expect("png failed to write header")
.write_image_data(&pixels)
.expect("png failed to write data");

panic!(
"decoding difference: {:?}, maximum difference was {}",
output_path, max_diff
);
}
}