diff --git a/benches/reductions.rs b/benches/reductions.rs index 602b963d..a0ecc38e 100644 --- a/benches/reductions.rs +++ b/benches/reductions.rs @@ -258,9 +258,7 @@ fn reductions_palette_full_reduction(b: &mut Bencher) { #[bench] fn reductions_alpha_black(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/rgba_8_reduce_alpha_black.png", - )); + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_black.png")); let png = png::PngData::new(&input, false).unwrap(); b.iter(|| { @@ -271,9 +269,7 @@ fn reductions_alpha_black(b: &mut Bencher) { #[bench] fn reductions_alpha_white(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/rgba_8_reduce_alpha_white.png", - )); + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_white.png")); let png = png::PngData::new(&input, false).unwrap(); b.iter(|| { @@ -284,9 +280,7 @@ fn reductions_alpha_white(b: &mut Bencher) { #[bench] fn reductions_alpha_left(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/rgba_8_reduce_alpha_left.png", - )); + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_left.png")); let png = png::PngData::new(&input, false).unwrap(); b.iter(|| { @@ -297,9 +291,7 @@ fn reductions_alpha_left(b: &mut Bencher) { #[bench] fn reductions_alpha_right(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/rgba_8_reduce_alpha_right.png", - )); + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_right.png")); let png = png::PngData::new(&input, false).unwrap(); b.iter(|| { @@ -310,9 +302,7 @@ fn reductions_alpha_right(b: &mut Bencher) { #[bench] fn reductions_alpha_up(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/rgba_8_reduce_alpha_up.png", - )); + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_up.png")); let png = png::PngData::new(&input, false).unwrap(); b.iter(|| { @@ -323,9 +313,7 @@ fn reductions_alpha_up(b: &mut Bencher) { #[bench] fn reductions_alpha_down(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/rgba_8_reduce_alpha_down.png", - )); + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_down.png")); let png = png::PngData::new(&input, false).unwrap(); b.iter(|| { diff --git a/src/png.rs b/src/png.rs index 884dc4a3..c7cf4f5d 100644 --- a/src/png.rs +++ b/src/png.rs @@ -774,143 +774,167 @@ impl PngData { pub fn reduce_alpha_channel(&mut self, optim: AlphaOptim) -> bool { let (bpc, bpp) = match self.ihdr_data.color_type { - ColorType::RGBA => { - match self.ihdr_data.bit_depth { - BitDepth::Sixteen => (2, 8), - BitDepth::Eight => (1, 4), - _ => unreachable!(), - } - } + ColorType::RGBA | ColorType::GrayscaleAlpha => { - match self.ihdr_data.bit_depth { - BitDepth::Sixteen => (2, 4), - BitDepth::Eight => (1, 2), - _ => unreachable!(), - } + let cpp = self.channels_per_pixel(); + let bpc = self.ihdr_data.bit_depth.as_u8() / 8; + (bpc as usize, (bpc * cpp) as usize) } _ => { return false; } }; - let mut reduced = Vec::with_capacity(self.raw_data.len()); match optim { AlphaOptim::NoOp => { return false; } AlphaOptim::Black => { - for line in self.scan_lines() { - reduced.push(line.filter); - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - for _ in 0..bpp { - reduced.push(0); - } - } else { - reduced.extend_from_slice(pixel); - } - } - } + self.raw_data = self.reduce_alpha_to_black(bpc, bpp); } AlphaOptim::White => { - for line in self.scan_lines() { - reduced.push(line.filter); - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - for _ in 0..(bpp - bpc) { - reduced.push(255); - } - for _ in 0..bpc { - reduced.push(0); - } - } else { - reduced.extend_from_slice(pixel); - } - } - } + self.raw_data = self.reduce_alpha_to_white(bpc, bpp); } AlphaOptim::Up => { - let mut lines = Vec::new(); - let scan_lines = self.scan_lines().collect::>(); - let mut last_line = vec![0; scan_lines[0].data.len()]; - let mut current_line = Vec::with_capacity(last_line.len()); - for line in scan_lines.into_iter().rev() { - current_line.push(line.filter); - for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - current_line.extend_from_slice(&last_pixel[0..(bpp - bpc)]); - for _ in 0..bpc { - current_line.push(0); - } - } else { - current_line.extend_from_slice(pixel); - } - } - last_line = current_line.clone(); - lines.push(current_line.clone()); - current_line.clear(); - } - reduced.extend(lines.into_iter().rev().flatten()); + self.raw_data = self.reduce_alpha_to_up(bpc, bpp); } AlphaOptim::Down => { - let mut last_line = vec![0; self.scan_lines().next().unwrap().data.len()]; - for line in self.scan_lines() { - reduced.push(line.filter); - for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]); - for _ in 0..bpc { - reduced.push(0); - } - } else { - reduced.extend_from_slice(pixel); - } - } - last_line = reduced.clone(); - } + self.raw_data = self.reduce_alpha_to_down(bpc, bpp); } AlphaOptim::Left => { - for line in self.scan_lines() { - let mut line_bytes = Vec::with_capacity(line.data.len()); - let mut last_pixel = vec![0; bpp]; - for pixel in line.data.chunks(bpp).rev() { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - line_bytes.extend_from_slice(&last_pixel[0..(bpp - bpc)]); - for _ in 0..bpc { - line_bytes.push(0); - } - } else { - line_bytes.extend_from_slice(pixel); - } - last_pixel = pixel.to_owned(); - } - reduced.push(line.filter); - reduced.extend(line_bytes.chunks(bpp).rev().flatten()); - } + self.raw_data = self.reduce_alpha_to_left(bpc, bpp); } AlphaOptim::Right => { - for line in self.scan_lines() { - reduced.push(line.filter); - let mut last_pixel = vec![0; bpp]; - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]); - for _ in 0..bpc { - reduced.push(0); - } - } else { - reduced.extend_from_slice(pixel); - } - last_pixel = pixel.to_owned(); - } - } + self.raw_data = self.reduce_alpha_to_right(bpc, bpp); } } - self.raw_data = reduced; true } + fn reduce_alpha_to_black(&self, bpc: usize, bpp: usize) -> Vec { + let mut reduced = Vec::with_capacity(self.raw_data.len()); + for line in self.scan_lines() { + reduced.push(line.filter); + for pixel in line.data.chunks(bpp) { + if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + for _ in 0..bpp { + reduced.push(0); + } + } else { + reduced.extend_from_slice(pixel); + } + } + } + reduced + } + + fn reduce_alpha_to_white(&self, bpc: usize, bpp: usize) -> Vec { + let mut reduced = Vec::with_capacity(self.raw_data.len()); + for line in self.scan_lines() { + reduced.push(line.filter); + for pixel in line.data.chunks(bpp) { + if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + for _ in 0..(bpp - bpc) { + reduced.push(255); + } + for _ in 0..bpc { + reduced.push(0); + } + } else { + reduced.extend_from_slice(pixel); + } + } + } + reduced + } + + fn reduce_alpha_to_up(&self, bpc: usize, bpp: usize) -> Vec { + let mut lines = Vec::new(); + let scan_lines = self.scan_lines().collect::>(); + let mut last_line = vec![0; scan_lines[0].data.len()]; + let mut current_line = Vec::with_capacity(last_line.len()); + for line in scan_lines.into_iter().rev() { + current_line.push(line.filter); + for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { + if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + current_line.extend_from_slice(&last_pixel[0..(bpp - bpc)]); + for _ in 0..bpc { + current_line.push(0); + } + } else { + current_line.extend_from_slice(pixel); + } + } + last_line = current_line.clone(); + lines.push(current_line.clone()); + current_line.clear(); + } + lines.into_iter().rev().flatten().collect() + } + + fn reduce_alpha_to_down(&self, bpc: usize, bpp: usize) -> Vec { + let mut reduced = Vec::with_capacity(self.raw_data.len()); + let mut last_line = vec![0; self.scan_lines().next().unwrap().data.len()]; + for line in self.scan_lines() { + reduced.push(line.filter); + for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { + if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]); + for _ in 0..bpc { + reduced.push(0); + } + } else { + reduced.extend_from_slice(pixel); + } + } + last_line = reduced.clone(); + } + reduced + } + + fn reduce_alpha_to_left(&self, bpc: usize, bpp: usize) -> Vec { + let mut reduced = Vec::with_capacity(self.raw_data.len()); + for line in self.scan_lines() { + let mut line_bytes = Vec::with_capacity(line.data.len()); + let mut last_pixel = vec![0; bpp]; + for pixel in line.data.chunks(bpp).rev() { + if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + line_bytes.extend_from_slice(&last_pixel[0..(bpp - bpc)]); + for _ in 0..bpc { + line_bytes.push(0); + } + } else { + line_bytes.extend_from_slice(pixel); + } + last_pixel = pixel.to_owned(); + } + reduced.push(line.filter); + reduced.extend(line_bytes.chunks(bpp).rev().flatten()); + } + reduced + } + + fn reduce_alpha_to_right(&self, bpc: usize, bpp: usize) -> Vec { + let mut reduced = Vec::with_capacity(self.raw_data.len()); + for line in self.scan_lines() { + reduced.push(line.filter); + let mut last_pixel = vec![0; bpp]; + for pixel in line.data.chunks(bpp) { + if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]); + for _ in 0..bpc { + reduced.push(0); + } + } else { + reduced.extend_from_slice(pixel); + } + last_pixel = pixel.to_owned(); + } + } + reduced + } + /// Convert the image to the specified interlacing type /// Returns true if the interlacing was changed, false otherwise /// The `interlace` parameter specifies the *new* interlacing mode @@ -932,7 +956,6 @@ impl PngData { } } -#[inline] fn write_png_block(key: &[u8], header: &[u8], output: &mut Vec) { let mut header_data = Vec::with_capacity(header.len() + 4); header_data.extend_from_slice(key);