From e0e90a7da7713c1b4e75bd5719a716f4fcaca588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kornel=20Lesi=C5=84ski?= Date: Tue, 13 Nov 2018 20:42:56 +0000 Subject: [PATCH 1/3] Compress alpha with strategy sensitive to repetitions --- src/png/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/png/mod.rs b/src/png/mod.rs index 56c682e1..50665e42 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -20,7 +20,8 @@ use std::iter::Iterator; use std::path::Path; const STD_COMPRESSION: u8 = 8; -const STD_STRATEGY: u8 = 2; // Huffman only +/// Must use normal compression, as faster ones (Huffman/RLE-only) are not representative +const STD_STRATEGY: u8 = 0; const STD_WINDOW: u8 = 15; const STD_FILTERS: [u8; 2] = [0, 5]; From 345232d30a6dad95c821e269a400c466e3bbd362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kornel=20Lesi=C5=84ski?= Date: Tue, 13 Nov 2018 20:44:47 +0000 Subject: [PATCH 2/3] Update reductions flag when alpha changed --- src/lib.rs | 4 +++- src/png/mod.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6a9d43f6..f4b49097 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -768,7 +768,9 @@ fn perform_reductions(png: &mut PngData, opts: &Options, deadline: &Deadline) -> return reduction_occurred; } - png.try_alpha_reduction(&opts.alphas); + if png.try_alpha_reduction(&opts.alphas) { + reduction_occurred = true; + } reduction_occurred } diff --git a/src/png/mod.rs b/src/png/mod.rs index 50665e42..c4597350 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -587,7 +587,7 @@ impl PngData { changed } - pub fn try_alpha_reduction(&mut self, alphas: &HashSet) { + pub fn try_alpha_reduction(&mut self, alphas: &HashSet) -> bool { assert!(!alphas.is_empty()); let alphas = alphas.iter().collect::>(); let best_size = AtomicMin::new(None); @@ -623,7 +623,9 @@ impl PngData { if let Some(best) = best { self.raw_data = best.1.raw_data; + return true; } + false } pub fn reduce_alpha_channel(&mut self, optim: AlphaOptim) -> bool { From 2eb057102fb4619ccb1e320440c2e9e34f9bf151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kornel=20Lesi=C5=84ski?= Date: Tue, 13 Nov 2018 20:53:57 +0000 Subject: [PATCH 3/3] Avoid needlessly cloning out-of-date idat_data and temp raw_data --- src/png/mod.rs | 66 ++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/png/mod.rs b/src/png/mod.rs index c4597350..9dbf8b4d 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -597,8 +597,10 @@ impl PngData { let alphas_iter = alphas.iter(); let best = alphas_iter .filter_map(|&alpha| { - let mut image = self.clone(); - image.reduce_alpha_channel(*alpha); + let image = match self.reduced_alpha_channel(*alpha) { + Some(image) => image, + None => return None, + }; #[cfg(feature = "parallel")] let filters_iter = STD_FILTERS.par_iter().with_max_len(1); #[cfg(not(feature = "parallel"))] @@ -628,7 +630,9 @@ impl PngData { false } - pub fn reduce_alpha_channel(&mut self, optim: AlphaOptim) -> bool { + /// It doesn't recompress `idat_data`, so this field is out of date + /// after the reduction. + pub fn reduced_alpha_channel(&self, optim: AlphaOptim) -> Option { let (bpc, bpp) = match self.ihdr_data.color_type { ColorType::RGBA | ColorType::GrayscaleAlpha => { let cpp = self.channels_per_pixel(); @@ -636,38 +640,32 @@ impl PngData { (bpc as usize, (bpc * cpp) as usize) } _ => { - return false; + return None; } }; - match optim { - AlphaOptim::NoOp => { - return false; - } - AlphaOptim::Black => { - self.raw_data = self.reduce_alpha_to_black(bpc, bpp); - } - AlphaOptim::White => { - self.raw_data = self.reduce_alpha_to_white(bpc, bpp); - } - AlphaOptim::Up => { - self.raw_data = self.reduce_alpha_to_up(bpc, bpp); - } - AlphaOptim::Down => { - self.raw_data = self.reduce_alpha_to_down(bpc, bpp); - } - AlphaOptim::Left => { - self.raw_data = self.reduce_alpha_to_left(bpc, bpp); - } - AlphaOptim::Right => { - self.raw_data = self.reduce_alpha_to_right(bpc, bpp); - } - } + let raw_data = match optim { + AlphaOptim::NoOp => return None, + AlphaOptim::Black => self.reduced_alpha_to_black(bpc, bpp), + AlphaOptim::White => self.reduced_alpha_to_white(bpc, bpp), + AlphaOptim::Up => self.reduced_alpha_to_up(bpc, bpp), + AlphaOptim::Down => self.reduced_alpha_to_down(bpc, bpp), + AlphaOptim::Left => self.reduced_alpha_to_left(bpc, bpp), + AlphaOptim::Right => self.reduced_alpha_to_right(bpc, bpp), + }; - true + Some(Self { + raw_data, + idat_data: vec![], + ihdr_data: self.ihdr_data, + palette: self.palette.clone(), + transparency_pixel: self.transparency_pixel.clone(), + transparency_palette: self.transparency_palette.clone(), + aux_headers: self.aux_headers.clone(), + }) } - fn reduce_alpha_to_black(&self, bpc: usize, bpp: usize) -> Vec { + fn reduced_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); @@ -684,7 +682,7 @@ impl PngData { reduced } - fn reduce_alpha_to_white(&self, bpc: usize, bpp: usize) -> Vec { + fn reduced_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); @@ -704,7 +702,7 @@ impl PngData { reduced } - fn reduce_alpha_to_up(&self, bpc: usize, bpp: usize) -> Vec { + fn reduced_alpha_to_up(&self, bpc: usize, bpp: usize) -> Vec { let mut lines = Vec::new(); let mut scan_lines = self.scan_lines().collect::>(); scan_lines.reverse(); @@ -732,7 +730,7 @@ impl PngData { flatten(lines.into_iter().rev()).collect() } - fn reduce_alpha_to_down(&self, bpc: usize, bpp: usize) -> Vec { + fn reduced_alpha_to_down(&self, bpc: usize, bpp: usize) -> Vec { let mut reduced = Vec::with_capacity(self.raw_data.len()); let mut last_line = Vec::new(); for line in self.scan_lines() { @@ -755,7 +753,7 @@ impl PngData { reduced } - fn reduce_alpha_to_left(&self, bpc: usize, bpp: usize) -> Vec { + fn reduced_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()); @@ -777,7 +775,7 @@ impl PngData { reduced } - fn reduce_alpha_to_right(&self, bpc: usize, bpp: usize) -> Vec { + fn reduced_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);