diff --git a/benches/reductions.rs b/benches/reductions.rs index 1ce5d512..27aa1e77 100644 --- a/benches/reductions.rs +++ b/benches/reductions.rs @@ -261,8 +261,7 @@ fn reductions_alpha_black(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - let mut safe_png = png.clone(); - safe_png.reduce_alpha_channel(AlphaOptim::Black); + png.reduced_alpha_channel(AlphaOptim::Black) }); } @@ -272,8 +271,7 @@ fn reductions_alpha_white(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - let mut safe_png = png.clone(); - safe_png.reduce_alpha_channel(AlphaOptim::White); + png.reduced_alpha_channel(AlphaOptim::White) }); } @@ -283,8 +281,7 @@ fn reductions_alpha_left(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - let mut safe_png = png.clone(); - safe_png.reduce_alpha_channel(AlphaOptim::Left); + png.reduced_alpha_channel(AlphaOptim::Left) }); } @@ -294,8 +291,7 @@ fn reductions_alpha_right(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - let mut safe_png = png.clone(); - safe_png.reduce_alpha_channel(AlphaOptim::Right); + png.reduced_alpha_channel(AlphaOptim::Right) }); } @@ -305,8 +301,7 @@ fn reductions_alpha_up(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - let mut safe_png = png.clone(); - safe_png.reduce_alpha_channel(AlphaOptim::Up); + png.reduced_alpha_channel(AlphaOptim::Up) }); } @@ -316,7 +311,6 @@ fn reductions_alpha_down(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - let mut safe_png = png.clone(); - safe_png.reduce_alpha_channel(AlphaOptim::Down); + png.reduced_alpha_channel(AlphaOptim::Down) }); } diff --git a/src/deflate/mod.rs b/src/deflate/mod.rs index 71c6bc65..8746f0ce 100644 --- a/src/deflate/mod.rs +++ b/src/deflate/mod.rs @@ -15,21 +15,17 @@ pub fn inflate(data: &[u8]) -> PngResult> { } /// Compress a data stream using the DEFLATE algorithm -#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] pub fn deflate(data: &[u8], zc: u8, zs: u8, zw: u8, max_size: &AtomicMin) -> PngResult> { - if is_cfzlib_supported() { - return cfzlib_deflate(data, zc, zs, zw, max_size); + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + { + if is_cfzlib_supported() { + return cfzlib_deflate(data, zc, zs, zw, max_size); + } } miniz_stream::compress_to_vec_oxipng(data, zc, zw.into(), zs.into(), max_size) } -/// Compress a data stream using the DEFLATE algorithm -#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] -pub fn deflate(data: &[u8], zc: u8, zs: u8, zw: u8, max_size: &AtomicMin) -> PngResult> { - miniz_stream::compress_to_vec_oxipng(data, zc, zw.into(), zs.into(), max_size) -} - #[cfg(target_arch = "x86_64")] fn is_cfzlib_supported() -> bool { if is_x86_feature_detected!("sse4.2") && is_x86_feature_detected!("pclmulqdq") { diff --git a/src/interlace.rs b/src/interlace.rs index cdf20a2d..e5a59129 100644 --- a/src/interlace.rs +++ b/src/interlace.rs @@ -93,9 +93,8 @@ pub fn deinterlace_image(png: &mut PngData) { let mut current_y: usize = pass_constants.y_shift as usize; for line in png.scan_lines() { let bit_vec = BitVec::from_bytes(&line.data); - let bits_in_line = ((png.ihdr_data.width - u32::from(pass_constants.x_shift)) as f32 - / f32::from(pass_constants.x_step)).ceil() as usize - * bits_per_pixel as usize; + let bits_in_line = ((png.ihdr_data.width - u32::from(pass_constants.x_shift) + u32::from(pass_constants.x_step) - 1) + / u32::from(pass_constants.x_step)) as usize * bits_per_pixel as usize; for (i, bit) in bit_vec.iter().enumerate() { // Avoid moving padded 0's into new image if i >= bits_in_line { diff --git a/src/png/mod.rs b/src/png/mod.rs index 9dbf8b4d..f907cd49 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -16,7 +16,7 @@ use reduction::color::*; use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::{Read, Seek, SeekFrom}; -use std::iter::Iterator; +use std::iter::{Iterator, repeat}; use std::path::Path; const STD_COMPRESSION: u8 = 8; @@ -226,8 +226,7 @@ impl PngData { /// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream pub fn unfilter_image(&self) -> Vec { let mut unfiltered = Vec::with_capacity(self.raw_data.len()); - let bpp = ((f32::from(self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel())) / 8f32) - .ceil() as usize; + let bpp = ((self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize; let mut last_line: Vec = Vec::new(); let mut last_pass = 1; for line in self.scan_lines() { @@ -254,9 +253,8 @@ impl PngData { /// 5: All (heuristically pick the best filter for each line) pub fn filter_image(&self, filter: u8) -> Vec { let mut filtered = Vec::with_capacity(self.raw_data.len()); - let bpp = ((f32::from(self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel())) / 8f32) - .ceil() as usize; - let mut last_line: Vec = Vec::new(); + let bpp = ((self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize; + let mut last_line: &[u8] = &[]; let mut last_pass: Option = None; for line in self.scan_lines() { match filter { @@ -267,21 +265,21 @@ impl PngData { 0 }; filtered.push(filter); - filtered.extend_from_slice(&filter_line(filter, bpp, &line.data, &last_line)); + filtered.extend_from_slice(&filter_line(filter, bpp, &line.data, last_line)); } 5 => { // Heuristically guess best filter per line // Uses MSAD algorithm mentioned in libpng reference docs // http://www.libpng.org/pub/png/book/chapter09.html - let mut trials: HashMap> = HashMap::with_capacity(5); + let mut trials: Vec<(u8, Vec)> = Vec::with_capacity(5); // Avoid vertical filtering on first line of each interlacing pass for filter in if last_pass == line.pass { 0..5 } else { 0..2 } { - trials.insert(filter, filter_line(filter, bpp, &line.data, &last_line)); + trials.push((filter, filter_line(filter, bpp, &line.data, last_line))); } let (best_filter, best_line) = trials .iter() - .min_by_key(|x| { - x.1.iter().fold(0u64, |acc, &x| { + .min_by_key(|(_, line)| { + line.iter().fold(0u64, |acc, &x| { let signed = x as i8; acc + i16::from(signed).abs() as u64 }) @@ -291,7 +289,7 @@ impl PngData { } _ => unreachable!(), } - last_line = line.data.to_vec(); + last_line = line.data; last_pass = line.pass; } filtered @@ -352,26 +350,20 @@ impl PngData { } // A palette with RGB or RGBA slices - let palette = if let Some(ref trns) = self.transparency_palette { - self.palette - .clone() - .unwrap() - .chunks(3) - .zip(trns.iter().chain([255].iter().cycle())) - .flat_map(|(pixel, trns)| { - let mut pixel = pixel.to_owned(); - pixel.push(*trns); - pixel - }).collect() + let mut palette_tmp; + let mut indexed_palette: Vec<_> = if let Some(ref trns) = self.transparency_palette { + palette_tmp = Vec::with_capacity(1024); + for (pixel, trns) in self.palette.as_ref().unwrap().chunks(3) + .zip(trns.iter().cloned().chain(repeat(255))) { + palette_tmp.extend_from_slice(pixel); + palette_tmp.push(trns); + } + palette_tmp.chunks(4).collect() } else { - self.palette.clone().unwrap() + palette_tmp = self.palette.clone().unwrap(); + palette_tmp.chunks(3).collect() }; - let mut indexed_palette: Vec<&[u8]> = palette - .chunks(if self.transparency_palette.is_some() { - 4 - } else { - 3 - }).collect(); + // A map of old indexes to new ones, for any moved let mut index_map: HashMap = HashMap::new(); @@ -380,13 +372,13 @@ impl PngData { { // Find duplicate entries in the palette let mut seen: HashMap<&[u8], u8> = HashMap::with_capacity(indexed_palette.len()); - for (i, color) in indexed_palette.iter().enumerate() { + for (i, color) in indexed_palette.iter().cloned().enumerate() { if seen.contains_key(color) { - let index = &seen[color]; + let index = seen[color]; duplicates.push(i as u8); - index_map.insert(i as u8, *index); + index_map.insert(i as u8, index); } else { - seen.insert(*color, i as u8); + seen.insert(color, i as u8); } } }