diff --git a/benches/decoding_benchmark.rs b/benches/decoding_benchmark.rs index 6b62651d..8bf200c7 100644 --- a/benches/decoding_benchmark.rs +++ b/benches/decoding_benchmark.rs @@ -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(); -} \ No newline at end of file +} diff --git a/examples/decode.rs b/examples/decode.rs index 67934a21..49a19003 100644 --- a/examples/decode.rs +++ b/examples/decode.rs @@ -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 { diff --git a/src/decoder.rs b/src/decoder.rs index 5d956cae..2d8fc13a 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -2,27 +2,25 @@ use byteorder::ReadBytesExt; use error::{Error, Result, UnsupportedFeature}; use huffman::{fill_default_mjpeg_tables, HuffmanDecoder, HuffmanTable}; use marker::Marker; -use parser::{AdobeColorTransform, AppData, CodingProcess, Component, Dimensions, EntropyCoding, FrameInfo, - parse_app, parse_com, parse_dht, parse_dqt, parse_dri, parse_sof, parse_sos, ScanInfo}; -use upsampler::Upsampler; +use parser::{ + parse_app, parse_com, parse_dht, parse_dqt, parse_dri, parse_sof, parse_sos, + AdobeColorTransform, AppData, CodingProcess, Component, Dimensions, EntropyCoding, FrameInfo, + ScanInfo, +}; use std::cmp; use std::io::Read; use std::mem; use std::ops::Range; use std::sync::Arc; -use worker::{RowData, PlatformWorker, Worker}; +use upsampler::Upsampler; +use worker::{PlatformWorker, RowData, Worker}; pub const MAX_COMPONENTS: usize = 4; static UNZIGZAG: [u8; 64] = [ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, + 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, + 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, ]; /// An enumeration over combinations of color spaces and bit depths a pixel can have. @@ -104,7 +102,7 @@ impl Decoder { height: frame.output_size.height, pixel_format: pixel_format, }) - }, + } None => None, } } @@ -117,18 +115,24 @@ impl Decoder { } /// Configure the decoder to scale the image during decoding. - /// + /// /// This efficiently scales the image by the smallest supported scale /// factor that produces an image larger than or equal to the requested /// size in at least one axis. The currently implemented scale factors /// are 1/8, 1/4, 1/2 and 1. - /// + /// /// To generate a thumbnail of an exact size, pass the desired size and /// then scale to the final size using a traditional resampling algorithm. pub fn scale(&mut self, requested_width: u16, requested_height: u16) -> Result<(u16, u16)> { self.read_info()?; let frame = self.frame.as_mut().unwrap(); - let idct_size = crate::idct::choose_idct_size(frame.image_size, Dimensions{ width: requested_width, height: requested_height }); + let idct_size = crate::idct::choose_idct_size( + frame.image_size, + Dimensions { + width: requested_width, + height: requested_height, + }, + ); frame.update_idct_size(idct_size); Ok((frame.output_size.width, frame.output_size.height)) } @@ -142,16 +146,25 @@ impl Decoder { if stop_after_metadata && self.frame.is_some() { // The metadata has already been read. return Ok(Vec::new()); - } - else if self.frame.is_none() && (self.reader.read_u8()? != 0xFF || Marker::from_u8(self.reader.read_u8()?) != Some(Marker::SOI)) { - return Err(Error::Format("first two bytes is not a SOI marker".to_owned())); + } else if self.frame.is_none() + && (self.reader.read_u8()? != 0xFF + || Marker::from_u8(self.reader.read_u8()?) != Some(Marker::SOI)) + { + return Err(Error::Format( + "first two bytes is not a SOI marker".to_owned(), + )); } let mut previous_marker = Marker::SOI; let mut pending_marker = None; let mut worker = None; let mut scans_processed = 0; - let mut planes = vec![Vec::new(); self.frame.as_ref().map_or(0, |frame| frame.components.len())]; + let mut planes = vec![ + Vec::new(); + self.frame + .as_ref() + .map_or(0, |frame| frame.components.len()) + ]; loop { let marker = match pending_marker.take() { @@ -180,20 +193,30 @@ impl Decoder { return Err(Error::Unsupported(UnsupportedFeature::Lossless)); } if frame.entropy_coding == EntropyCoding::Arithmetic { - return Err(Error::Unsupported(UnsupportedFeature::ArithmeticEntropyCoding)); + return Err(Error::Unsupported( + UnsupportedFeature::ArithmeticEntropyCoding, + )); } if frame.precision != 8 { - return Err(Error::Unsupported(UnsupportedFeature::SamplePrecision(frame.precision))); + return Err(Error::Unsupported(UnsupportedFeature::SamplePrecision( + frame.precision, + ))); } if frame.image_size.height == 0 { return Err(Error::Unsupported(UnsupportedFeature::DNL)); } if component_count != 1 && component_count != 3 && component_count != 4 { - return Err(Error::Unsupported(UnsupportedFeature::ComponentCount(component_count as u8))); + return Err(Error::Unsupported(UnsupportedFeature::ComponentCount( + component_count as u8, + ))); } // Make sure we support the subsampling ratios used. - let _ = Upsampler::new(&frame.components, frame.image_size.width, frame.image_size.height)?; + let _ = Upsampler::new( + &frame.components, + frame.image_size.width, + frame.image_size.height, + )?; self.frame = Some(frame); @@ -202,7 +225,7 @@ impl Decoder { } planes = vec![Vec::new(); component_count]; - }, + } // Scan header Marker::SOS => { @@ -216,11 +239,18 @@ impl Decoder { let frame = self.frame.clone().unwrap(); let scan = parse_sos(&mut self.reader, &frame)?; - if frame.coding_process == CodingProcess::DctProgressive && self.coefficients.is_empty() { - self.coefficients = frame.components.iter().map(|c| { - let block_count = c.block_size.width as usize * c.block_size.height as usize; - vec![0; block_count * 64] - }).collect(); + if frame.coding_process == CodingProcess::DctProgressive + && self.coefficients.is_empty() + { + self.coefficients = frame + .components + .iter() + .map(|c| { + let block_count = + c.block_size.width as usize * c.block_size.height as usize; + vec![0; block_count * 64] + }) + .collect(); } if scan.successive_approximation_low == 0 { @@ -231,18 +261,26 @@ impl Decoder { } } - let is_final_scan = scan.component_indices.iter().all(|&i| self.coefficients_finished[i] == !0); - let (marker, data) = self.decode_scan(&frame, &scan, worker.as_mut().unwrap(), is_final_scan)?; + let is_final_scan = scan + .component_indices + .iter() + .all(|&i| self.coefficients_finished[i] == !0); + let (marker, data) = + self.decode_scan(&frame, &scan, worker.as_mut().unwrap(), is_final_scan)?; if let Some(data) = data { - for (i, plane) in data.into_iter().enumerate().filter(|&(_, ref plane)| !plane.is_empty()) { + for (i, plane) in data + .into_iter() + .enumerate() + .filter(|&(_, ref plane)| !plane.is_empty()) + { planes[i] = plane; } } pending_marker = marker; scans_processed += 1; - }, + } // Table-specification and miscellaneous markers // Quantization table-specification @@ -253,44 +291,52 @@ impl Decoder { if let Some(table) = table { let mut unzigzagged_table = [0u16; 64]; - for j in 0 .. 64 { + for j in 0..64 { unzigzagged_table[UNZIGZAG[j] as usize] = table[j]; } self.quantization_tables[i] = Some(Arc::new(unzigzagged_table)); } } - }, + } // Huffman table-specification Marker::DHT => { let is_baseline = self.frame.as_ref().map(|frame| frame.is_baseline); let (dc_tables, ac_tables) = parse_dht(&mut self.reader, is_baseline)?; let current_dc_tables = mem::replace(&mut self.dc_huffman_tables, vec![]); - self.dc_huffman_tables = dc_tables.into_iter() - .zip(current_dc_tables.into_iter()) - .map(|(a, b)| a.or(b)) - .collect(); + self.dc_huffman_tables = dc_tables + .into_iter() + .zip(current_dc_tables.into_iter()) + .map(|(a, b)| a.or(b)) + .collect(); let current_ac_tables = mem::replace(&mut self.ac_huffman_tables, vec![]); - self.ac_huffman_tables = ac_tables.into_iter() - .zip(current_ac_tables.into_iter()) - .map(|(a, b)| a.or(b)) - .collect(); - }, + self.ac_huffman_tables = ac_tables + .into_iter() + .zip(current_ac_tables.into_iter()) + .map(|(a, b)| a.or(b)) + .collect(); + } // Arithmetic conditioning table-specification - Marker::DAC => return Err(Error::Unsupported(UnsupportedFeature::ArithmeticEntropyCoding)), + Marker::DAC => { + return Err(Error::Unsupported( + UnsupportedFeature::ArithmeticEntropyCoding, + )) + } // Restart interval definition Marker::DRI => self.restart_interval = parse_dri(&mut self.reader)?, // Comment Marker::COM => { let _comment = parse_com(&mut self.reader)?; - }, + } // Application data Marker::APP(..) => { if let Some(data) = parse_app(&mut self.reader, marker)? { match data { - AppData::Adobe(color_transform) => self.color_transform = Some(color_transform), + AppData::Adobe(color_transform) => { + self.color_transform = Some(color_transform) + } AppData::Jfif => { // From the JFIF spec: // "The APP0 marker is used to identify a JPEG FIF file. @@ -304,38 +350,49 @@ impl Decoder { */ self.is_jfif = true; - }, + } AppData::Avi1 => self.is_mjpeg = true, } } - }, + } // Restart Marker::RST(..) => { // Some encoders emit a final RST marker after entropy-coded data, which // decode_scan does not take care of. So if we encounter one, we ignore it. if previous_marker != Marker::SOS { - return Err(Error::Format("RST found outside of entropy-coded data".to_owned())); + return Err(Error::Format( + "RST found outside of entropy-coded data".to_owned(), + )); } - }, + } // Define number of lines Marker::DNL => { // Section B.2.1 // "If a DNL segment (see B.2.5) is present, it shall immediately follow the first scan." if previous_marker != Marker::SOS || scans_processed != 1 { - return Err(Error::Format("DNL is only allowed immediately after the first scan".to_owned())); + return Err(Error::Format( + "DNL is only allowed immediately after the first scan".to_owned(), + )); } return Err(Error::Unsupported(UnsupportedFeature::DNL)); - }, + } // Hierarchical mode markers - Marker::DHP | Marker::EXP => return Err(Error::Unsupported(UnsupportedFeature::Hierarchical)), + Marker::DHP | Marker::EXP => { + return Err(Error::Unsupported(UnsupportedFeature::Hierarchical)) + } // End of image Marker::EOI => break, - _ => return Err(Error::Format(format!("{:?} marker found where not allowed", marker))), + _ => { + return Err(Error::Format(format!( + "{:?} marker found where not allowed", + marker + ))) + } } previous_marker = marker; @@ -346,7 +403,13 @@ impl Decoder { } let frame = self.frame.as_ref().unwrap(); - compute_image(&frame.components, planes, frame.output_size, self.is_jfif, self.color_transform) + compute_image( + &frame.components, + planes, + frame.output_size, + self.is_jfif, + self.color_transform, + ) } fn read_marker(&mut self) -> Result { @@ -369,42 +432,64 @@ impl Decoder { while byte == 0xFF { byte = self.reader.read_u8()?; } - + if byte != 0x00 && byte != 0xFF { return Ok(Marker::from_u8(byte).unwrap()); } } } - fn decode_scan(&mut self, - frame: &FrameInfo, - scan: &ScanInfo, - worker: &mut PlatformWorker, - produce_data: bool) - -> Result<(Option, Option>>)> { + fn decode_scan( + &mut self, + frame: &FrameInfo, + scan: &ScanInfo, + worker: &mut PlatformWorker, + produce_data: bool, + ) -> Result<(Option, Option>>)> { assert!(scan.component_indices.len() <= MAX_COMPONENTS); - let components: Vec = scan.component_indices.iter() - .map(|&i| frame.components[i].clone()) - .collect(); + let components: Vec = scan + .component_indices + .iter() + .map(|&i| frame.components[i].clone()) + .collect(); // Verify that all required quantization tables has been set. - if components.iter().any(|component| self.quantization_tables[component.quantization_table_index].is_none()) { + if components + .iter() + .any(|component| self.quantization_tables[component.quantization_table_index].is_none()) + { return Err(Error::Format("use of unset quantization table".to_owned())); } if self.is_mjpeg { - fill_default_mjpeg_tables(scan, &mut self.dc_huffman_tables, &mut self.ac_huffman_tables); + fill_default_mjpeg_tables( + scan, + &mut self.dc_huffman_tables, + &mut self.ac_huffman_tables, + ); } // Verify that all required huffman tables has been set. - if scan.spectral_selection.start == 0 && - scan.dc_table_indices.iter().any(|&i| self.dc_huffman_tables[i].is_none()) { - return Err(Error::Format("scan makes use of unset dc huffman table".to_owned())); + if scan.spectral_selection.start == 0 + && scan + .dc_table_indices + .iter() + .any(|&i| self.dc_huffman_tables[i].is_none()) + { + return Err(Error::Format( + "scan makes use of unset dc huffman table".to_owned(), + )); } - if scan.spectral_selection.end > 1 && - scan.ac_table_indices.iter().any(|&i| self.ac_huffman_tables[i].is_none()) { - return Err(Error::Format("scan makes use of unset ac huffman table".to_owned())); + if scan.spectral_selection.end > 1 + && scan + .ac_table_indices + .iter() + .any(|&i| self.ac_huffman_tables[i].is_none()) + { + return Err(Error::Format( + "scan makes use of unset ac huffman table".to_owned(), + )); } if produce_data { @@ -413,16 +498,20 @@ impl Decoder { let row_data = RowData { index: i, component: component.clone(), - quantization_table: self.quantization_tables[component.quantization_table_index].clone().unwrap(), + quantization_table: self.quantization_tables + [component.quantization_table_index] + .clone() + .unwrap(), }; worker.start(row_data)?; } } - let blocks_per_mcu: Vec = components.iter() - .map(|c| c.horizontal_sampling_factor as u16 * c.vertical_sampling_factor as u16) - .collect(); + let blocks_per_mcu: Vec = components + .iter() + .map(|c| c.horizontal_sampling_factor as u16 * c.vertical_sampling_factor as u16) + .collect(); let is_progressive = frame.coding_process == CodingProcess::DctProgressive; let is_interleaved = components.len() > 1; let mut dummy_block = [0i16; 64]; @@ -435,79 +524,102 @@ impl Decoder { if produce_data && !is_progressive { for component in &components { - let coefficients_per_mcu_row = component.block_size.width as usize * component.vertical_sampling_factor as usize * 64; + let coefficients_per_mcu_row = component.block_size.width as usize + * component.vertical_sampling_factor as usize + * 64; mcu_row_coefficients.push(vec![0i16; coefficients_per_mcu_row]); } } - for mcu_y in 0 .. frame.mcu_size.height { - for mcu_x in 0 .. frame.mcu_size.width { + for mcu_y in 0..frame.mcu_size.height { + for mcu_x in 0..frame.mcu_size.width { for (i, component) in components.iter().enumerate() { - for j in 0 .. blocks_per_mcu[i] { + for j in 0..blocks_per_mcu[i] { let (block_x, block_y) = if is_interleaved { // Section A.2.3 - (mcu_x * component.horizontal_sampling_factor as u16 + j % component.horizontal_sampling_factor as u16, - mcu_y * component.vertical_sampling_factor as u16 + j / component.horizontal_sampling_factor as u16) - } - else { + ( + mcu_x * component.horizontal_sampling_factor as u16 + + j % component.horizontal_sampling_factor as u16, + mcu_y * component.vertical_sampling_factor as u16 + + j / component.horizontal_sampling_factor as u16, + ) + } else { // Section A.2.2 let blocks_per_row = component.block_size.width as usize; - let block_num = (mcu_y as usize * frame.mcu_size.width as usize + - mcu_x as usize) * blocks_per_mcu[i] as usize + j as usize; + let block_num = (mcu_y as usize * frame.mcu_size.width as usize + + mcu_x as usize) + * blocks_per_mcu[i] as usize + + j as usize; let x = (block_num % blocks_per_row) as u16; let y = (block_num / blocks_per_row) as u16; - if x * component.dct_scale as u16 >= component.size.width || y * component.dct_scale as u16 >= component.size.height { + if x * component.dct_scale as u16 >= component.size.width + || y * component.dct_scale as u16 >= component.size.height + { continue; } (x, y) }; - let block_offset = (block_y as usize * component.block_size.width as usize + block_x as usize) * 64; - let mcu_row_offset = mcu_y as usize * component.block_size.width as usize * component.vertical_sampling_factor as usize * 64; + let block_offset = (block_y as usize * component.block_size.width as usize + + block_x as usize) + * 64; + let mcu_row_offset = mcu_y as usize + * component.block_size.width as usize + * component.vertical_sampling_factor as usize + * 64; let coefficients = if is_progressive { - &mut self.coefficients[scan.component_indices[i]][block_offset .. block_offset + 64] + &mut self.coefficients[scan.component_indices[i]] + [block_offset..block_offset + 64] } else if produce_data { - &mut mcu_row_coefficients[i][block_offset - mcu_row_offset .. block_offset - mcu_row_offset + 64] + &mut mcu_row_coefficients[i] + [block_offset - mcu_row_offset..block_offset - mcu_row_offset + 64] } else { &mut dummy_block[..] }; if scan.successive_approximation_high == 0 { - decode_block(&mut self.reader, - coefficients, - &mut huffman, - self.dc_huffman_tables[scan.dc_table_indices[i]].as_ref(), - self.ac_huffman_tables[scan.ac_table_indices[i]].as_ref(), - scan.spectral_selection.clone(), - scan.successive_approximation_low, - &mut eob_run, - &mut dc_predictors[i])?; - } - else { - decode_block_successive_approximation(&mut self.reader, - coefficients, - &mut huffman, - self.ac_huffman_tables[scan.ac_table_indices[i]].as_ref(), - scan.spectral_selection.clone(), - scan.successive_approximation_low, - &mut eob_run)?; + decode_block( + &mut self.reader, + coefficients, + &mut huffman, + self.dc_huffman_tables[scan.dc_table_indices[i]].as_ref(), + self.ac_huffman_tables[scan.ac_table_indices[i]].as_ref(), + scan.spectral_selection.clone(), + scan.successive_approximation_low, + &mut eob_run, + &mut dc_predictors[i], + )?; + } else { + decode_block_successive_approximation( + &mut self.reader, + coefficients, + &mut huffman, + self.ac_huffman_tables[scan.ac_table_indices[i]].as_ref(), + scan.spectral_selection.clone(), + scan.successive_approximation_low, + &mut eob_run, + )?; } } } if self.restart_interval > 0 { - let is_last_mcu = mcu_x == frame.mcu_size.width - 1 && mcu_y == frame.mcu_size.height - 1; + let is_last_mcu = + mcu_x == frame.mcu_size.width - 1 && mcu_y == frame.mcu_size.height - 1; mcus_left_until_restart -= 1; if mcus_left_until_restart == 0 && !is_last_mcu { match huffman.take_marker(&mut self.reader)? { Some(Marker::RST(n)) => { if n != expected_rst_num { - return Err(Error::Format(format!("found RST{} where RST{} was expected", n, expected_rst_num))); + return Err(Error::Format(format!( + "found RST{} where RST{} was expected", + n, expected_rst_num + ))); } huffman.reset(); @@ -518,9 +630,19 @@ impl Decoder { expected_rst_num = (expected_rst_num + 1) % 8; mcus_left_until_restart = self.restart_interval; - }, - Some(marker) => return Err(Error::Format(format!("found marker {:?} inside scan where RST{} was expected", marker, expected_rst_num))), - None => return Err(Error::Format(format!("no marker found where RST{} was expected", expected_rst_num))), + } + Some(marker) => { + return Err(Error::Format(format!( + "found marker {:?} inside scan where RST{} was expected", + marker, expected_rst_num + ))) + } + None => { + return Err(Error::Format(format!( + "no marker found where RST{} was expected", + expected_rst_num + ))) + } } } } @@ -529,13 +651,20 @@ impl Decoder { if produce_data { // Send the coefficients from this MCU row to the worker thread for dequantization and idct. for (i, component) in components.iter().enumerate() { - let coefficients_per_mcu_row = component.block_size.width as usize * component.vertical_sampling_factor as usize * 64; + let coefficients_per_mcu_row = component.block_size.width as usize + * component.vertical_sampling_factor as usize + * 64; let row_coefficients = if is_progressive { let offset = mcu_y as usize * coefficients_per_mcu_row; - self.coefficients[scan.component_indices[i]][offset .. offset + coefficients_per_mcu_row].to_vec() + self.coefficients[scan.component_indices[i]] + [offset..offset + coefficients_per_mcu_row] + .to_vec() } else { - mem::replace(&mut mcu_row_coefficients[i], vec![0i16; coefficients_per_mcu_row]) + mem::replace( + &mut mcu_row_coefficients[i], + vec![0i16; coefficients_per_mcu_row], + ) }; worker.append_row((i, row_coefficients))?; @@ -557,22 +686,23 @@ impl Decoder { } Ok((marker, Some(data))) - } - else { + } else { Ok((marker, None)) } } } -fn decode_block(reader: &mut R, - coefficients: &mut [i16], - huffman: &mut HuffmanDecoder, - dc_table: Option<&HuffmanTable>, - ac_table: Option<&HuffmanTable>, - spectral_selection: Range, - successive_approximation_low: u8, - eob_run: &mut u16, - dc_predictor: &mut i16) -> Result<()> { +fn decode_block( + reader: &mut R, + coefficients: &mut [i16], + huffman: &mut HuffmanDecoder, + dc_table: Option<&HuffmanTable>, + ac_table: Option<&HuffmanTable>, + spectral_selection: Range, + successive_approximation_low: u8, + eob_run: &mut u16, + dc_predictor: &mut i16, +) -> Result<()> { debug_assert_eq!(coefficients.len(), 64); if spectral_selection.start == 0 { @@ -585,11 +715,13 @@ fn decode_block(reader: &mut R, // Section F.1.2.1.1 // Table F.1 if value > 11 { - return Err(Error::Format("invalid DC difference magnitude category".to_owned())); + return Err(Error::Format( + "invalid DC difference magnitude category".to_owned(), + )); } huffman.receive_extend(reader, value)? - }, + } }; // Malicious JPEG files can cause this add to overflow, therefore we use wrapping_add. @@ -616,8 +748,7 @@ fn decode_block(reader: &mut R, coefficients[UNZIGZAG[index as usize] as usize] = value << successive_approximation_low; index += 1; - } - else { + } else { let byte = huffman.decode(reader, ac_table.unwrap())?; let r = byte >> 4; let s = byte & 0x0f; @@ -625,7 +756,7 @@ fn decode_block(reader: &mut R, if s == 0 { match r { 15 => index += 16, // Run length of 16 zero coefficients. - _ => { + _ => { *eob_run = (1 << r) - 1; if r > 0 { @@ -633,17 +764,17 @@ fn decode_block(reader: &mut R, } break; - }, + } } - } - else { + } else { index += r; if index >= spectral_selection.end { break; } - coefficients[UNZIGZAG[index as usize] as usize] = huffman.receive_extend(reader, s)? << successive_approximation_low; + coefficients[UNZIGZAG[index as usize] as usize] = + huffman.receive_extend(reader, s)? << successive_approximation_low; index += 1; } } @@ -652,13 +783,15 @@ fn decode_block(reader: &mut R, Ok(()) } -fn decode_block_successive_approximation(reader: &mut R, - coefficients: &mut [i16], - huffman: &mut HuffmanDecoder, - ac_table: Option<&HuffmanTable>, - spectral_selection: Range, - successive_approximation_low: u8, - eob_run: &mut u16) -> Result<()> { +fn decode_block_successive_approximation( + reader: &mut R, + coefficients: &mut [i16], + huffman: &mut HuffmanDecoder, + ac_table: Option<&HuffmanTable>, + spectral_selection: Range, + successive_approximation_low: u8, + eob_run: &mut u16, +) -> Result<()> { debug_assert_eq!(coefficients.len(), 64); let bit = 1 << successive_approximation_low; @@ -669,8 +802,7 @@ fn decode_block_successive_approximation(reader: &mut R, if huffman.get_bits(reader, 1)? == 1 { coefficients[0] |= bit; } - } - else { + } else { // Section G.1.2.3 if *eob_run > 0 { @@ -697,7 +829,7 @@ fn decode_block_successive_approximation(reader: &mut R, // We don't need to do anything special here, zero_run_length is 15 // and then value (which is zero) gets written, resulting in 16 // zero coefficients. - }, + } _ => { *eob_run = (1 << r) - 1; @@ -707,17 +839,16 @@ fn decode_block_successive_approximation(reader: &mut R, // Force end of block. zero_run_length = 64; - }, + } } - }, + } 1 => { if huffman.get_bits(reader, 1)? == 1 { value = bit; - } - else { + } else { value = -bit; } - }, + } _ => return Err(Error::Format("unexpected huffman code".to_owned())), } @@ -738,12 +869,14 @@ fn decode_block_successive_approximation(reader: &mut R, Ok(()) } -fn refine_non_zeroes(reader: &mut R, - coefficients: &mut [i16], - huffman: &mut HuffmanDecoder, - range: Range, - zrl: u8, - bit: i16) -> Result { +fn refine_non_zeroes( + reader: &mut R, + coefficients: &mut [i16], + huffman: &mut HuffmanDecoder, + range: Range, + zrl: u8, + bit: i16, +) -> Result { debug_assert_eq!(coefficients.len(), 64); let last = range.end - 1; @@ -758,12 +891,10 @@ fn refine_non_zeroes(reader: &mut R, } zero_run_length -= 1; - } - else if huffman.get_bits(reader, 1)? == 1 && coefficients[index] & bit == 0 { + } else if huffman.get_bits(reader, 1)? == 1 && coefficients[index] & bit == 0 { if coefficients[index] > 0 { coefficients[index] += bit; - } - else { + } else { coefficients[index] -= bit; } } @@ -772,11 +903,13 @@ fn refine_non_zeroes(reader: &mut R, Ok(last) } -fn compute_image(components: &[Component], - mut data: Vec>, - output_size: Dimensions, - is_jfif: bool, - color_transform: Option) -> Result> { +fn compute_image( + components: &[Component], + mut data: Vec>, + output_size: Dimensions, + is_jfif: bool, + color_transform: Option, +) -> Result> { if data.iter().any(|data| data.is_empty()) { return Err(Error::Format("not all components has data".to_owned())); } @@ -806,18 +939,19 @@ fn compute_image(components: &[Component], } decoded.resize(size, 0); Ok(decoded) - } - else { + } else { compute_image_parallel(components, data, output_size, is_jfif, color_transform) } } -#[cfg(feature="rayon")] -fn compute_image_parallel(components: &[Component], - data: Vec>, - output_size: Dimensions, - is_jfif: bool, - color_transform: Option) -> Result> { +#[cfg(feature = "rayon")] +fn compute_image_parallel( + components: &[Component], + data: Vec>, + output_size: Dimensions, + is_jfif: bool, + color_transform: Option, +) -> Result> { use rayon::prelude::*; let color_convert_func = choose_color_convert_func(components.len(), is_jfif, color_transform)?; @@ -825,83 +959,86 @@ fn compute_image_parallel(components: &[Component], let line_size = output_size.width as usize * components.len(); let mut image = vec![0u8; line_size * output_size.height as usize]; - image.par_chunks_mut(line_size) - .with_max_len(1) - .enumerate() - .for_each(|(row, line)| { - upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line); - color_convert_func(line, output_size.width as usize); - }); + image + .par_chunks_mut(line_size) + .with_max_len(1) + .enumerate() + .for_each(|(row, line)| { + upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line); + color_convert_func(line, output_size.width as usize); + }); Ok(image) - } - -#[cfg(not(feature="rayon"))] -fn compute_image_parallel(components: &[Component], - data: Vec>, - output_size: Dimensions, - is_jfif: bool, - color_transform: Option) -> Result> { +} + +#[cfg(not(feature = "rayon"))] +fn compute_image_parallel( + components: &[Component], + data: Vec>, + output_size: Dimensions, + is_jfif: bool, + color_transform: Option, +) -> Result> { let color_convert_func = choose_color_convert_func(components.len(), is_jfif, color_transform)?; let upsampler = Upsampler::new(components, output_size.width, output_size.height)?; let line_size = output_size.width as usize * components.len(); let mut image = vec![0u8; line_size * output_size.height as usize]; - for (row, line) in image.chunks_mut(line_size) - .enumerate() { - upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line); - color_convert_func(line, output_size.width as usize); - } + for (row, line) in image.chunks_mut(line_size).enumerate() { + upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line); + color_convert_func(line, output_size.width as usize); + } Ok(image) } -fn choose_color_convert_func(component_count: usize, - _is_jfif: bool, - color_transform: Option) - -> Result { +fn choose_color_convert_func( + component_count: usize, + _is_jfif: bool, + color_transform: Option, +) -> Result { match component_count { 3 => { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe // Unknown means the data is RGB, so we don't need to perform any color conversion on it. if color_transform == Some(AdobeColorTransform::Unknown) { Ok(color_convert_line_null) - } - else { + } else { Ok(color_convert_line_ycbcr) } - }, + } 4 => { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe match color_transform { Some(AdobeColorTransform::Unknown) => Ok(color_convert_line_cmyk), Some(_) => Ok(color_convert_line_ycck), - None => Err(Error::Format("4 components without Adobe APP14 metadata to tell color space".to_owned())), + None => Err(Error::Format( + "4 components without Adobe APP14 metadata to tell color space".to_owned(), + )), } - }, + } _ => panic!(), } } -fn color_convert_line_null(_data: &mut [u8], _width: usize) { -} +fn color_convert_line_null(_data: &mut [u8], _width: usize) {} fn color_convert_line_ycbcr(data: &mut [u8], width: usize) { - for i in 0 .. width { + for i in 0..width { let (r, g, b) = ycbcr_to_rgb(data[i * 3], data[i * 3 + 1], data[i * 3 + 2]); - data[i * 3] = r; + data[i * 3] = r; data[i * 3 + 1] = g; data[i * 3 + 2] = b; } } fn color_convert_line_ycck(data: &mut [u8], width: usize) { - for i in 0 .. width { + for i in 0..width { let (r, g, b) = ycbcr_to_rgb(data[i * 4], data[i * 4 + 1], data[i * 4 + 2]); let k = data[i * 4 + 3]; - data[i * 4] = r; + data[i * 4] = r; data[i * 4 + 1] = g; data[i * 4 + 2] = b; data[i * 4 + 3] = 255 - k; @@ -909,8 +1046,8 @@ fn color_convert_line_ycck(data: &mut [u8], width: usize) { } fn color_convert_line_cmyk(data: &mut [u8], width: usize) { - for i in 0 .. width { - data[i * 4] = 255 - data[i * 4]; + for i in 0..width { + data[i * 4] = 255 - data[i * 4]; data[i * 4 + 1] = 255 - data[i * 4 + 1]; data[i * 4 + 2] = 255 - data[i * 4 + 2]; data[i * 4 + 3] = 255 - data[i * 4 + 3]; @@ -923,17 +1060,23 @@ fn ycbcr_to_rgb(y: u8, cb: u8, cr: u8) -> (u8, u8, u8) { let cb = cb as f32 - 128.0; let cr = cr as f32 - 128.0; - let r = y + 1.40200 * cr; + let r = y + 1.40200 * cr; let g = y - 0.34414 * cb - 0.71414 * cr; let b = y + 1.77200 * cb; - (clamp((r + 0.5) as i32, 0, 255) as u8, - clamp((g + 0.5) as i32, 0, 255) as u8, - clamp((b + 0.5) as i32, 0, 255) as u8) + ( + clamp((r + 0.5) as i32, 0, 255) as u8, + clamp((g + 0.5) as i32, 0, 255) as u8, + clamp((b + 0.5) as i32, 0, 255) as u8, + ) } fn clamp(value: T, min: T, max: T) -> T { - if value < min { return min; } - if value > max { return max; } + if value < min { + return min; + } + if value > max { + return max; + } value } diff --git a/src/error.rs b/src/error.rs index fac73200..f04ea6b5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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), } } } diff --git a/src/huffman.rs b/src/huffman.rs index 75028690..9013fba1 100644 --- a/src/huffman.rs +++ b/src/huffman.rs @@ -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(&mut self, reader: &mut R, table: &HuffmanTable) -> Result> { + pub fn decode_fast_ac( + &mut self, + reader: &mut R, + table: &HuffmanTable, + ) -> Result> { 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,7 +238,9 @@ 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)); @@ -235,7 +248,7 @@ impl HuffmanTable { } Some(table) - }, + } }; Ok(HuffmanTable { @@ -251,13 +264,14 @@ impl HuffmanTable { // Section C.2 fn derive_huffman_codes(bits: &[u8; 16]) -> Result<(Vec, Vec)> { // Figure C.1 - let huffsize = bits.iter() - .enumerate() - .fold(Vec::new(), |mut acc, (i, &value)| { - let mut repeated_size: Vec = 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 = 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, Vec)> { // 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], - ac_huffman_tables: &mut[Option]) { +pub fn fill_default_mjpeg_tables( + scan: &ScanInfo, + dc_huffman_tables: &mut [Option], + ac_huffman_tables: &mut [Option], +) { // 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(), + ); } } diff --git a/src/idct.rs b/src/idct.rs index a5de0828..13752394 100644 --- a/src/idct.rs +++ b/src/idct.rs @@ -5,10 +5,14 @@ use crate::parser::Dimensions; use std::num::Wrapping; pub(crate) fn choose_idct_size(full_size: Dimensions, requested_size: Dimensions) -> usize { - fn scaled(len: u16, scale: usize) -> u16 { ((len as u32 * scale as u32 - 1) / 8 + 1) as u16 } + fn scaled(len: u16, scale: usize) -> u16 { + ((len as u32 * scale as u32 - 1) / 8 + 1) as u16 + } for &scale in &[1, 2, 4] { - if scaled(full_size.width, scale) >= requested_size.width || scaled(full_size.height, scale) >= requested_size.height { + if scaled(full_size.width, scale) >= requested_size.width + || scaled(full_size.height, scale) >= requested_size.height + { return scale; } } @@ -18,56 +22,247 @@ pub(crate) fn choose_idct_size(full_size: Dimensions, requested_size: Dimensions #[test] fn test_choose_idct_size() { - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 200, height: 200}), 1); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 500, height: 500}), 1); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 684, height: 456}), 1); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 999, height: 456}), 1); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 684, height: 999}), 1); - assert_eq!(choose_idct_size(Dimensions{width: 500, height: 333}, Dimensions{width: 63, height: 42}), 1); - - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 685, height: 999}), 2); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 1000, height: 1000}), 2); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 1400, height: 1400}), 4); - - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 5472, height: 3648}), 8); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 16384, height: 16384}), 8); - assert_eq!(choose_idct_size(Dimensions{width: 1, height: 1}, Dimensions{width: 65535, height: 65535}), 8); - assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 16384, height: 16384}), 8); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 200, + height: 200 + } + ), + 1 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 500, + height: 500 + } + ), + 1 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 684, + height: 456 + } + ), + 1 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 999, + height: 456 + } + ), + 1 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 684, + height: 999 + } + ), + 1 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 500, + height: 333 + }, + Dimensions { + width: 63, + height: 42 + } + ), + 1 + ); + + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 685, + height: 999 + } + ), + 2 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 1000, + height: 1000 + } + ), + 2 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 1400, + height: 1400 + } + ), + 4 + ); + + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 5472, + height: 3648 + } + ), + 8 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 16384, + height: 16384 + } + ), + 8 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 1, + height: 1 + }, + Dimensions { + width: 65535, + height: 65535 + } + ), + 8 + ); + assert_eq!( + choose_idct_size( + Dimensions { + width: 5472, + height: 3648 + }, + Dimensions { + width: 16384, + height: 16384 + } + ), + 8 + ); } -pub(crate) fn dequantize_and_idct_block(scale: usize, coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) { +pub(crate) fn dequantize_and_idct_block( + scale: usize, + coefficients: &[i16], + quantization_table: &[u16; 64], + output_linestride: usize, + output: &mut [u8], +) { match scale { - 8 => dequantize_and_idct_block_8x8(coefficients, quantization_table, output_linestride, output), - 4 => dequantize_and_idct_block_4x4(coefficients, quantization_table, output_linestride, output), - 2 => dequantize_and_idct_block_2x2(coefficients, quantization_table, output_linestride, output), - 1 => dequantize_and_idct_block_1x1(coefficients, quantization_table, output_linestride, output), + 8 => dequantize_and_idct_block_8x8( + coefficients, + quantization_table, + output_linestride, + output, + ), + 4 => dequantize_and_idct_block_4x4( + coefficients, + quantization_table, + output_linestride, + output, + ), + 2 => dequantize_and_idct_block_2x2( + coefficients, + quantization_table, + output_linestride, + output, + ), + 1 => dequantize_and_idct_block_1x1( + coefficients, + quantization_table, + output_linestride, + output, + ), _ => panic!("Unsupported IDCT scale {}/8", scale), } } // This is based on stb_image's 'stbi__idct_block'. -fn dequantize_and_idct_block_8x8(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) { +fn dequantize_and_idct_block_8x8( + coefficients: &[i16], + quantization_table: &[u16; 64], + output_linestride: usize, + output: &mut [u8], +) { debug_assert_eq!(coefficients.len(), 64); let mut temp = [Wrapping(0i32); 64]; // columns - for i in 0 .. 8 { + for i in 0..8 { // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if coefficients[i + 8] == 0 && coefficients[i + 16] == 0 && coefficients[i + 24] == 0 && - coefficients[i + 32] == 0 && coefficients[i + 40] == 0 && coefficients[i + 48] == 0 && - coefficients[i + 56] == 0 { + if coefficients[i + 8] == 0 + && coefficients[i + 16] == 0 + && coefficients[i + 24] == 0 + && coefficients[i + 32] == 0 + && coefficients[i + 40] == 0 + && coefficients[i + 48] == 0 + && coefficients[i + 56] == 0 + { let dcterm = Wrapping(coefficients[i] as i32 * quantization_table[i] as i32) << 2; - temp[i] = dcterm; - temp[i + 8] = dcterm; + temp[i] = dcterm; + temp[i + 8] = dcterm; temp[i + 16] = dcterm; temp[i + 24] = dcterm; temp[i + 32] = dcterm; temp[i + 40] = dcterm; temp[i + 48] = dcterm; temp[i + 56] = dcterm; - } - else { + } else { let s0 = Wrapping(coefficients[i] as i32 * quantization_table[i] as i32); let s1 = Wrapping(coefficients[i + 8] as i32 * quantization_table[i + 8] as i32); let s2 = Wrapping(coefficients[i + 16] as i32 * quantization_table[i + 16] as i32); @@ -130,7 +325,7 @@ fn dequantize_and_idct_block_8x8(coefficients: &[i16], quantization_table: &[u16 } } - for i in 0 .. 8 { + for i in 0..8 { // no fast case since the first 1D IDCT spread components out let s0 = temp[i * 8]; let s1 = temp[i * 8 + 1]; @@ -200,7 +395,12 @@ fn dequantize_and_idct_block_8x8(coefficients: &[i16], quantization_table: &[u16 // 4x4 and 2x2 IDCT based on Rakesh Dugad and Narendra Ahuja: "A Fast Scheme for Image Size Change in the Compressed Domain" (2001). // http://sylvana.net/jpegcrop/jidctred/ -fn dequantize_and_idct_block_4x4(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) { +fn dequantize_and_idct_block_4x4( + coefficients: &[i16], + quantization_table: &[u16; 64], + output_linestride: usize, + output: &mut [u8], +) { debug_assert_eq!(coefficients.len(), 64); let mut temp = [Wrapping(0i32); 4 * 4]; @@ -228,7 +428,7 @@ fn dequantize_and_idct_block_4x4(coefficients: &[i16], quantization_table: &[u16 temp[i + 4 * 2] = x2 - t0; } - for i in 0 .. 4 { + for i in 0..4 { let s0 = temp[i * 4 + 0]; let s1 = temp[i * 4 + 1]; let s2 = temp[i * 4 + 2]; @@ -257,7 +457,12 @@ fn dequantize_and_idct_block_4x4(coefficients: &[i16], quantization_table: &[u16 } } -fn dequantize_and_idct_block_2x2(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) { +fn dequantize_and_idct_block_2x2( + coefficients: &[i16], + quantization_table: &[u16; 64], + output_linestride: usize, + output: &mut [u8], +) { debug_assert_eq!(coefficients.len(), 64); const SCALE_BITS: usize = 3; @@ -288,16 +493,21 @@ fn dequantize_and_idct_block_2x2(coefficients: &[i16], quantization_table: &[u16 output[output_linestride + 1] = stbi_clamp((x2 - x3) >> SCALE_BITS); } -fn dequantize_and_idct_block_1x1(coefficients: &[i16], quantization_table: &[u16; 64], _output_linestride: usize, output: &mut [u8]) { +fn dequantize_and_idct_block_1x1( + coefficients: &[i16], + quantization_table: &[u16; 64], + _output_linestride: usize, + output: &mut [u8], +) { debug_assert_eq!(coefficients.len(), 64); - let s0 = (Wrapping(coefficients[0] as i32 * quantization_table[0] as i32) + Wrapping(128 * 8)) / Wrapping(8); + let s0 = (Wrapping(coefficients[0] as i32 * quantization_table[0] as i32) + Wrapping(128 * 8)) + / Wrapping(8); output[0] = stbi_clamp(s0); } // take a -128..127 value and stbi__clamp it and convert to 0..255 -fn stbi_clamp(x: Wrapping) -> u8 -{ +fn stbi_clamp(x: Wrapping) -> u8 { x.0.max(0).min(255) as u8 } @@ -312,70 +522,53 @@ fn stbi_fsh(x: Wrapping) -> Wrapping { #[test] fn test_dequantize_and_idct_block_8x8() { let coefficients: [i16; 8 * 8] = [ - -14, -39, 58, -2, 3, 3, 0, 1, - 11, 27, 4, -3, 3, 0, 1, 0, - -6, -13, -9, -1, -2, -1, 0, 0, - -4, 0, -1, -2, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, 0, 0, - -3, -2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0]; + -14, -39, 58, -2, 3, 3, 0, 1, 11, 27, 4, -3, 3, 0, 1, 0, -6, -13, -9, -1, -2, -1, 0, 0, -4, + 0, -1, -2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, -3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; let quantization_table: [u16; 8 * 8] = [ - 8, 6, 5, 8, 12, 20, 26, 31, - 6, 6, 7, 10, 13, 29, 30, 28, - 7, 7, 8, 12, 20, 29, 35, 28, - 7, 9, 11, 15, 26, 44, 40, 31, - 9, 11, 19, 28, 34, 55, 52, 39, - 12, 18, 28, 32, 41, 52, 57, 46, - 25, 32, 39, 44, 52, 61, 60, 51, - 36, 46, 48, 49, 56, 50, 52, 50]; + 8, 6, 5, 8, 12, 20, 26, 31, 6, 6, 7, 10, 13, 29, 30, 28, 7, 7, 8, 12, 20, 29, 35, 28, 7, 9, + 11, 15, 26, 44, 40, 31, 9, 11, 19, 28, 34, 55, 52, 39, 12, 18, 28, 32, 41, 52, 57, 46, 25, + 32, 39, 44, 52, 61, 60, 51, 36, 46, 48, 49, 56, 50, 52, 50, + ]; let output_linestride: usize = 8; let mut output = [0u8; 8 * 8]; dequantize_and_idct_block_8x8( &coefficients, &quantization_table, output_linestride, - &mut output); + &mut output, + ); let expected_output = [ - 118, 92, 110, 83, 77, 93, 144, 198, - 172, 116, 114, 87, 78, 93, 146, 191, - 194, 107, 91, 76, 71, 93, 160, 198, - 196, 100, 80, 74, 67, 92, 174, 209, - 182, 104, 88, 81, 68, 89, 178, 206, - 105, 64, 59, 59, 63, 94, 183, 201, - 35, 27, 28, 37, 72, 121, 203, 204, - 37, 45, 41, 47, 98, 154, 223, 208]; + 118, 92, 110, 83, 77, 93, 144, 198, 172, 116, 114, 87, 78, 93, 146, 191, 194, 107, 91, 76, + 71, 93, 160, 198, 196, 100, 80, 74, 67, 92, 174, 209, 182, 104, 88, 81, 68, 89, 178, 206, + 105, 64, 59, 59, 63, 94, 183, 201, 35, 27, 28, 37, 72, 121, 203, 204, 37, 45, 41, 47, 98, + 154, 223, 208, + ]; assert_eq!(&output[..], &expected_output[..]); } #[test] fn test_dequantize_and_idct_block_8x8_all_zero() { let mut output = [0u8; 8 * 8]; - dequantize_and_idct_block_8x8( - &[0; 8*8], - &[666; 8*8], - 8, - &mut output); - assert_eq!(&output[..], &[128; 8*8][..]); + dequantize_and_idct_block_8x8(&[0; 8 * 8], &[666; 8 * 8], 8, &mut output); + assert_eq!(&output[..], &[128; 8 * 8][..]); } #[test] fn test_dequantize_and_idct_block_8x8_saturated() { let mut output = [0u8; 8 * 8]; dequantize_and_idct_block_8x8( - &[std::i16::MAX; 8*8], - &[std::u16::MAX; 8*8], + &[std::i16::MAX; 8 * 8], + &[std::u16::MAX; 8 * 8], 8, - &mut output); + &mut output, + ); let expected = [ - 0, 0, 0, 255, 255, 0, 0, 255, - 0, 0, 215, 0, 0, 255, 255, 0, - 255, 255, 255, 255, 255, 0, 0, 255, - 0, 0, 255, 0, 255, 0, 255, 255, - 0, 0, 255, 255, 0, 255, 0, 0, - 255, 255, 0, 255, 255, 255, 170, 0, - 0, 255, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 255, 0, 255, 0, 0]; + 0, 0, 0, 255, 255, 0, 0, 255, 0, 0, 215, 0, 0, 255, 255, 0, 255, 255, 255, 255, 255, 0, 0, + 255, 0, 0, 255, 0, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 0, 0, 255, 255, 0, 255, 255, + 255, 170, 0, 0, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 0, 255, 0, 0, + ]; assert_eq!(&output[..], &expected[..]); } diff --git a/src/lib.rs b/src/lib.rs index f79c1d90..15df1f86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; diff --git a/src/marker.rs b/src/marker.rs index f7b93f83..29f9e57a 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -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)), diff --git a/src/parser.rs b/src/parser.rs index 823778eb..d81c1fd8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -92,7 +92,7 @@ impl FrameInfo { self.output_size = Dimensions { width: (self.image_size.width as f32 * idct_size as f32 / 8.0).ceil() as u16, - height: (self.image_size.height as f32 * idct_size as f32 / 8.0).ceil() as u16 + height: (self.image_size.height as f32 * idct_size as f32 / 8.0).ceil() as u16, }; } } @@ -104,7 +104,10 @@ fn read_length(reader: &mut R, marker: Marker) -> Result { let length = reader.read_u16::()? as usize; if length < 2 { - return Err(Error::Format(format!("encountered {:?} with invalid length {}", marker, length))); + return Err(Error::Format(format!( + "encountered {:?} with invalid length {}", + marker, length + ))); } Ok(length - 2) @@ -131,36 +134,41 @@ pub fn parse_sof(reader: &mut R, marker: Marker) -> Result { let is_baseline = marker == SOF(0); let is_differential = match marker { - SOF(0 ..= 3) | SOF(9 ..= 11) => false, - SOF(5 ..= 7) | SOF(13 ..= 15) => true, + SOF(0..=3) | SOF(9..=11) => false, + SOF(5..=7) | SOF(13..=15) => true, _ => panic!(), }; let coding_process = match marker { SOF(0) | SOF(1) | SOF(5) | SOF(9) | SOF(13) => CodingProcess::DctSequential, - SOF(2) | SOF(6) | SOF(10) | SOF(14) => CodingProcess::DctProgressive, - SOF(3) | SOF(7) | SOF(11) | SOF(15) => CodingProcess::Lossless, + SOF(2) | SOF(6) | SOF(10) | SOF(14) => CodingProcess::DctProgressive, + SOF(3) | SOF(7) | SOF(11) | SOF(15) => CodingProcess::Lossless, _ => panic!(), }; let entropy_coding = match marker { - SOF(0 ..= 3) | SOF(5 ..= 7) => EntropyCoding::Huffman, - SOF(9 ..= 11) | SOF(13 ..= 15) => EntropyCoding::Arithmetic, + SOF(0..=3) | SOF(5..=7) => EntropyCoding::Huffman, + SOF(9..=11) | SOF(13..=15) => EntropyCoding::Arithmetic, _ => panic!(), }; let precision = reader.read_u8()?; match precision { - 8 => {}, + 8 => {} 12 => { if is_baseline { - return Err(Error::Format("12 bit sample precision is not allowed in baseline".to_owned())); + return Err(Error::Format( + "12 bit sample precision is not allowed in baseline".to_owned(), + )); } - }, + } _ => { if coding_process != CodingProcess::Lossless { - return Err(Error::Format(format!("invalid precision {} in frame header", precision))) + return Err(Error::Format(format!( + "invalid precision {} in frame header", + precision + ))); } - }, + } } let height = reader.read_u16::()?; @@ -177,10 +185,14 @@ pub fn parse_sof(reader: &mut R, marker: Marker) -> Result { let component_count = reader.read_u8()?; if component_count == 0 { - return Err(Error::Format("zero component count in frame header".to_owned())); + return Err(Error::Format( + "zero component count in frame header".to_owned(), + )); } if coding_process == CodingProcess::DctProgressive && component_count > 4 { - return Err(Error::Format("progressive frame with more than 4 components".to_owned())); + return Err(Error::Format( + "progressive frame with more than 4 components".to_owned(), + )); } if length != 6 + 3 * component_count as usize { @@ -189,12 +201,15 @@ pub fn parse_sof(reader: &mut R, marker: Marker) -> Result { let mut components: Vec = Vec::with_capacity(component_count as usize); - for _ in 0 .. component_count { + for _ in 0..component_count { let identifier = reader.read_u8()?; // Each component's identifier must be unique. if components.iter().any(|c| c.identifier == identifier) { - return Err(Error::Format(format!("duplicate frame component identifier {}", identifier))); + return Err(Error::Format(format!( + "duplicate frame component identifier {}", + identifier + ))); } let byte = reader.read_u8()?; @@ -202,16 +217,27 @@ pub fn parse_sof(reader: &mut R, marker: Marker) -> Result { let vertical_sampling_factor = byte & 0x0f; if horizontal_sampling_factor == 0 || horizontal_sampling_factor > 4 { - return Err(Error::Format(format!("invalid horizontal sampling factor {}", horizontal_sampling_factor))); + return Err(Error::Format(format!( + "invalid horizontal sampling factor {}", + horizontal_sampling_factor + ))); } if vertical_sampling_factor == 0 || vertical_sampling_factor > 4 { - return Err(Error::Format(format!("invalid vertical sampling factor {}", vertical_sampling_factor))); + return Err(Error::Format(format!( + "invalid vertical sampling factor {}", + vertical_sampling_factor + ))); } let quantization_table_index = reader.read_u8()?; - if quantization_table_index > 3 || (coding_process == CodingProcess::Lossless && quantization_table_index != 0) { - return Err(Error::Format(format!("invalid quantization table index {}", quantization_table_index))); + if quantization_table_index > 3 + || (coding_process == CodingProcess::Lossless && quantization_table_index != 0) + { + return Err(Error::Format(format!( + "invalid quantization table index {}", + quantization_table_index + ))); } components.push(Component { @@ -220,8 +246,14 @@ pub fn parse_sof(reader: &mut R, marker: Marker) -> Result { vertical_sampling_factor: vertical_sampling_factor, quantization_table_index: quantization_table_index as usize, dct_scale: 8, - size: Dimensions {width: 0, height: 0}, - block_size: Dimensions {width: 0, height: 0}, + size: Dimensions { + width: 0, + height: 0, + }, + block_size: Dimensions { + width: 0, + height: 0, + }, }); } @@ -242,13 +274,21 @@ pub fn parse_sof(reader: &mut R, marker: Marker) -> Result { /// Returns ceil(x/y), requires x>0 fn ceil_div(x: u32, y: u32) -> u16 { - assert!(x>0 && y>0, "invalid dimensions"); + assert!(x > 0 && y > 0, "invalid dimensions"); (1 + ((x - 1) / y)) as u16 } fn update_component_sizes(size: Dimensions, components: &mut [Component]) -> Dimensions { - let h_max = components.iter().map(|c| c.horizontal_sampling_factor).max().unwrap() as u32; - let v_max = components.iter().map(|c| c.vertical_sampling_factor).max().unwrap() as u32; + let h_max = components + .iter() + .map(|c| c.horizontal_sampling_factor) + .max() + .unwrap() as u32; + let v_max = components + .iter() + .map(|c| c.vertical_sampling_factor) + .max() + .unwrap() as u32; let mcu_size = Dimensions { width: ceil_div(size.width as u32, h_max * 8), @@ -256,8 +296,18 @@ fn update_component_sizes(size: Dimensions, components: &mut [Component]) -> Dim }; for component in components { - component.size.width = ceil_div(size.width as u32 * component.horizontal_sampling_factor as u32 * component.dct_scale as u32, h_max * 8); - component.size.height = ceil_div(size.height as u32 * component.vertical_sampling_factor as u32 * component.dct_scale as u32, v_max * 8); + component.size.width = ceil_div( + size.width as u32 + * component.horizontal_sampling_factor as u32 + * component.dct_scale as u32, + h_max * 8, + ); + component.size.height = ceil_div( + size.height as u32 + * component.vertical_sampling_factor as u32 + * component.dct_scale as u32, + v_max * 8, + ); component.block_size.width = mcu_size.width * component.horizontal_sampling_factor as u16; component.block_size.height = mcu_size.height * component.vertical_sampling_factor as u16; @@ -274,15 +324,43 @@ fn test_update_component_sizes() { vertical_sampling_factor: 2, quantization_table_index: 0, dct_scale: 8, - size: Dimensions { width: 0, height: 0 }, - block_size: Dimensions { width: 0, height: 0 }, + size: Dimensions { + width: 0, + height: 0, + }, + block_size: Dimensions { + width: 0, + height: 0, + }, }]; let mcu = update_component_sizes( - Dimensions { width: 800, height: 280 }, - &mut components); - assert_eq!(mcu, Dimensions { width: 50, height: 18 }); - assert_eq!(components[0].block_size, Dimensions { width: 100, height: 36 }); - assert_eq!(components[0].size, Dimensions { width: 800, height: 280 }); + Dimensions { + width: 800, + height: 280, + }, + &mut components, + ); + assert_eq!( + mcu, + Dimensions { + width: 50, + height: 18 + } + ); + assert_eq!( + components[0].block_size, + Dimensions { + width: 100, + height: 36 + } + ); + assert_eq!( + components[0].size, + Dimensions { + width: 800, + height: 280 + } + ); } // Section B.2.3 @@ -295,7 +373,10 @@ pub fn parse_sos(reader: &mut R, frame: &FrameInfo) -> Result let component_count = reader.read_u8()?; if component_count == 0 || component_count > 4 { - return Err(Error::Format(format!("invalid component count {} in scan header", component_count))); + return Err(Error::Format(format!( + "invalid component count {} in scan header", + component_count + ))); } if length != 4 + 2 * component_count as usize { @@ -306,7 +387,7 @@ pub fn parse_sos(reader: &mut R, frame: &FrameInfo) -> Result let mut dc_table_indices = Vec::with_capacity(component_count as usize); let mut ac_table_indices = Vec::with_capacity(component_count as usize); - for _ in 0 .. component_count { + for _ in 0..component_count { let identifier = reader.read_u8()?; let component_index = match frame.components.iter().position(|c| c.identifier == identifier) { @@ -316,12 +397,17 @@ pub fn parse_sos(reader: &mut R, frame: &FrameInfo) -> Result // Each of the scan's components must be unique. if component_indices.contains(&component_index) { - return Err(Error::Format(format!("duplicate scan component identifier {}", identifier))); + return Err(Error::Format(format!( + "duplicate scan component identifier {}", + identifier + ))); } // "... the ordering in the scan header shall follow the ordering in the frame header." if component_index < *component_indices.iter().max().unwrap_or(&0) { - return Err(Error::Format("the scan component order does not follow the order in the frame header".to_owned())); + return Err(Error::Format( + "the scan component order does not follow the order in the frame header".to_owned(), + )); } let byte = reader.read_u8()?; @@ -329,10 +415,16 @@ pub fn parse_sos(reader: &mut R, frame: &FrameInfo) -> Result let ac_table_index = byte & 0x0f; if dc_table_index > 3 || (frame.is_baseline && dc_table_index > 1) { - return Err(Error::Format(format!("invalid dc table index {}", dc_table_index))); + return Err(Error::Format(format!( + "invalid dc table index {}", + dc_table_index + ))); } if ac_table_index > 3 || (frame.is_baseline && ac_table_index > 1) { - return Err(Error::Format(format!("invalid ac table index {}", ac_table_index))); + return Err(Error::Format(format!( + "invalid ac table index {}", + ac_table_index + ))); } component_indices.push(component_index); @@ -340,12 +432,18 @@ pub fn parse_sos(reader: &mut R, frame: &FrameInfo) -> Result ac_table_indices.push(ac_table_index as usize); } - let blocks_per_mcu = component_indices.iter().map(|&i| { - frame.components[i].horizontal_sampling_factor as u32 * frame.components[i].vertical_sampling_factor as u32 - }).fold(0, ::std::ops::Add::add); + let blocks_per_mcu = component_indices + .iter() + .map(|&i| { + frame.components[i].horizontal_sampling_factor as u32 + * frame.components[i].vertical_sampling_factor as u32 + }) + .fold(0, ::std::ops::Add::add); if component_count > 1 && blocks_per_mcu > 10 { - return Err(Error::Format("scan with more than one component and more than 10 blocks per MCU".to_owned())); + return Err(Error::Format( + "scan with more than one component and more than 10 blocks per MCU".to_owned(), + )); } let spectral_selection_start = reader.read_u8()?; @@ -356,31 +454,49 @@ pub fn parse_sos(reader: &mut R, frame: &FrameInfo) -> Result let successive_approximation_low = byte & 0x0f; if frame.coding_process == CodingProcess::DctProgressive { - if spectral_selection_end > 63 || spectral_selection_start > spectral_selection_end || - (spectral_selection_start == 0 && spectral_selection_end != 0) { - return Err(Error::Format(format!("invalid spectral selection parameters: ss={}, se={}", spectral_selection_start, spectral_selection_end))); + if spectral_selection_end > 63 + || spectral_selection_start > spectral_selection_end + || (spectral_selection_start == 0 && spectral_selection_end != 0) + { + return Err(Error::Format(format!( + "invalid spectral selection parameters: ss={}, se={}", + spectral_selection_start, spectral_selection_end + ))); } if spectral_selection_start != 0 && component_count != 1 { - return Err(Error::Format("spectral selection scan with AC coefficients can't have more than one component".to_owned())); + return Err(Error::Format( + "spectral selection scan with AC coefficients can't have more than one component" + .to_owned(), + )); } if successive_approximation_high > 13 || successive_approximation_low > 13 { - return Err(Error::Format(format!("invalid successive approximation parameters: ah={}, al={}", successive_approximation_high, successive_approximation_low))); + return Err(Error::Format(format!( + "invalid successive approximation parameters: ah={}, al={}", + successive_approximation_high, successive_approximation_low + ))); } // Section G.1.1.1.2 // "Each scan which follows the first scan for a given band progressively improves // the precision of the coefficients by one bit, until full precision is reached." - if successive_approximation_high != 0 && successive_approximation_high != successive_approximation_low + 1 { - return Err(Error::Format("successive approximation scan with more than one bit of improvement".to_owned())); + if successive_approximation_high != 0 + && successive_approximation_high != successive_approximation_low + 1 + { + return Err(Error::Format( + "successive approximation scan with more than one bit of improvement".to_owned(), + )); } - } - else { + } else { if spectral_selection_start != 0 || spectral_selection_end != 63 { - return Err(Error::Format("spectral selection is not allowed in non-progressive scan".to_owned())); + return Err(Error::Format( + "spectral selection is not allowed in non-progressive scan".to_owned(), + )); } if successive_approximation_high != 0 || successive_approximation_low != 0 { - return Err(Error::Format("successive approximation is not allowed in non-progressive scan".to_owned())); + return Err(Error::Format( + "successive approximation is not allowed in non-progressive scan".to_owned(), + )); } } @@ -417,10 +533,16 @@ pub fn parse_dqt(reader: &mut R) -> Result<[Option<[u16; 64]>; 4]> { // libjpeg allows this behavior though, and there are images in the wild using it. So to // match libjpeg's behavior we are deviating from the JPEG spec here. if precision > 1 { - return Err(Error::Format(format!("invalid precision {} in DQT", precision))); + return Err(Error::Format(format!( + "invalid precision {} in DQT", + precision + ))); } if index > 3 { - return Err(Error::Format(format!("invalid destination identifier {} in DQT", index))); + return Err(Error::Format(format!( + "invalid destination identifier {} in DQT", + index + ))); } if length < 65 + 64 * precision { return Err(Error::Format("invalid length in DQT".to_owned())); @@ -428,7 +550,7 @@ pub fn parse_dqt(reader: &mut R) -> Result<[Option<[u16; 64]>; 4]> { let mut table = [0u16; 64]; - for i in 0 .. 64 { + for i in 0..64 { table[i] = match precision { 0 => reader.read_u8()? as u16, 1 => reader.read_u16::()?, @@ -437,7 +559,9 @@ pub fn parse_dqt(reader: &mut R) -> Result<[Option<[u16; 64]>; 4]> { } if table.iter().any(|&val| val == 0) { - return Err(Error::Format("quantization table contains element with a zero value".to_owned())); + return Err(Error::Format( + "quantization table contains element with a zero value".to_owned(), + )); } tables[index] = Some(table); @@ -448,7 +572,10 @@ pub fn parse_dqt(reader: &mut R) -> Result<[Option<[u16; 64]>; 4]> { } // Section B.2.4.2 -pub fn parse_dht(reader: &mut R, is_baseline: Option) -> Result<(Vec>, Vec>)> { +pub fn parse_dht( + reader: &mut R, + is_baseline: Option, +) -> Result<(Vec>, Vec>)> { let mut length = read_length(reader, DHT)?; let mut dc_tables = vec![None, None, None, None]; let mut ac_tables = vec![None, None, None, None]; @@ -463,24 +590,34 @@ pub fn parse_dht(reader: &mut R, is_baseline: Option) -> Result<( return Err(Error::Format(format!("invalid class {} in DHT", class))); } if is_baseline == Some(true) && index > 1 { - return Err(Error::Format("a maximum of two huffman tables per class are allowed in baseline".to_owned())); + return Err(Error::Format( + "a maximum of two huffman tables per class are allowed in baseline".to_owned(), + )); } if index > 3 { - return Err(Error::Format(format!("invalid destination identifier {} in DHT", index))); + return Err(Error::Format(format!( + "invalid destination identifier {} in DHT", + index + ))); } let mut counts = [0u8; 16]; reader.read_exact(&mut counts)?; - let size = counts.iter().map(|&val| val as usize).fold(0, ::std::ops::Add::add); + let size = counts + .iter() + .map(|&val| val as usize) + .fold(0, ::std::ops::Add::add); if size == 0 { - return Err(Error::Format("encountered table with zero length in DHT".to_owned())); - } - else if size > 256 { - return Err(Error::Format("encountered table with excessive length in DHT".to_owned())); - } - else if size > length - 17 { + return Err(Error::Format( + "encountered table with zero length in DHT".to_owned(), + )); + } else if size > 256 { + return Err(Error::Format( + "encountered table with excessive length in DHT".to_owned(), + )); + } else if size > length - 17 { return Err(Error::Format("invalid length in DHT".to_owned())); } @@ -488,8 +625,12 @@ pub fn parse_dht(reader: &mut R, is_baseline: Option) -> Result<( reader.read_exact(&mut values)?; match class { - 0 => dc_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::DC)?), - 1 => ac_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::AC)?), + 0 => { + dc_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::DC)?) + } + 1 => { + ac_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::AC)?) + } _ => unreachable!(), } @@ -538,14 +679,14 @@ pub fn parse_app(reader: &mut R, marker: Marker) -> Result { if length >= 12 { let mut buffer = [0u8; 12]; @@ -553,19 +694,23 @@ pub fn parse_app(reader: &mut R, marker: Marker) -> Result AdobeColorTransform::Unknown, 1 => AdobeColorTransform::YCbCr, 2 => AdobeColorTransform::YCCK, - _ => return Err(Error::Format("invalid color transform in adobe app segment".to_owned())), + _ => { + return Err(Error::Format( + "invalid color transform in adobe app segment".to_owned(), + )) + } }; result = Some(AppData::Adobe(color_transform)); } } - }, - _ => {}, + } + _ => {} } skip_bytes(reader, length - bytes_read)?; diff --git a/src/upsampler.rs b/src/upsampler.rs index 31224a5c..16188feb 100644 --- a/src/upsampler.rs +++ b/src/upsampler.rs @@ -3,7 +3,7 @@ use parser::Component; pub struct Upsampler { components: Vec, - 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 { - 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 { + 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], row: usize, output_width: usize, output: &mut [u8]) { + pub fn upsample_and_interleave_row( + &self, + component_data: &[Vec], + 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,13 +94,15 @@ 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> { +fn choose_upsampler( + sampling_factors: (u8, u8), + max_sampling_factors: (u8, u8), + output_width: u16, + output_height: u16, +) -> Result> { 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; @@ -83,67 +110,72 @@ fn choose_upsampler(sampling_factors: (u8, u8), 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)]; diff --git a/src/worker/immediate.rs b/src/worker/immediate.rs index 7e512d2e..d2a1ec59 100644 --- a/src/worker/immediate.rs +++ b/src/worker/immediate.rs @@ -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; diff --git a/src/worker/mod.rs b/src/worker/mod.rs index f6fc019b..04105c14 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -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; diff --git a/src/worker/threaded.rs b/src/worker/threaded.rs index c2bd0577..d20186d3 100644 --- a/src/worker/threaded.rs +++ b/src/worker/threaded.rs @@ -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,13 +26,13 @@ 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)); - }, + } } } })?; @@ -40,14 +40,22 @@ impl Worker for ThreadedWorker { 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)) -> 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> { 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")) } } diff --git a/tests/lib.rs b/tests/lib.rs index ec2220dc..83725cb9 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -2,8 +2,8 @@ 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; @@ -11,7 +11,11 @@ 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(); diff --git a/tests/reftest/mod.rs b/tests/reftest/mod.rs index aa5d6e2e..0cb4232d 100644 --- a/tests/reftest/mod.rs +++ b/tests/reftest/mod.rs @@ -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(mut decoder: jpeg::Decoder, 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(mut decoder: jpeg::Decoder, 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(mut decoder: jpeg::Decoder, path: &Path, assert_eq!(data.len(), ref_data.len()); let mut max_diff = 0; - let pixels: Vec = 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 = 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 + ); } }