From 9267f040af56b9ce41cb1017dd3e7dc9598beaab Mon Sep 17 00:00:00 2001 From: Joshua Holmer Date: Tue, 12 Jan 2016 11:50:39 -0500 Subject: [PATCH] Implement bit depth reduction --- src/lib.rs | 46 +++++++++++++++++++++++++++++++++++----------- src/png.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 77aa4fb1..cfa23297 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,20 +74,43 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { println!(" IDAT size = {} bytes", idat_original_size); println!(" File size = {} bytes", file_original_size); } - // TODO: Color space reduction - // TODO: 16-bit Bit depth reduction - // TODO: Palette bit depth reduction - // - // TODO: Apply interlacing changes - // - // TODO: Force reencoding if interlacing was changed - if opts.idat_recoding { + + let mut something_changed = false; + + if opts.color_type_reduction { + if let Some(data) = png.reduce_color_type() { + png.raw_data = data; + something_changed = true; + }; + } + + if opts.bit_depth_reduction { + if let Some(data) = png.reduce_bit_depth() { + png.raw_data = data; + something_changed = true; + }; + } + + if opts.palette_reduction { + if let Some(data) = png.reduce_palette() { + png.raw_data = data; + something_changed = true; + }; + } + + if let Some(interlacing) = opts.interlace { + if let Some(data) = png.change_interlacing(interlacing) { + png.raw_data = data; + something_changed = true; + }; + } + + if opts.idat_recoding || something_changed { // Go through selected permutations and determine the best let mut best: Option<(u8, u8, u8, u8)> = None; let combinations = opts.filter.len() * opts.compression.len() * opts.memory.len() * opts.strategies.len(); println!("Trying: {} combinations", combinations); - // TODO: Force reencoding if interlacing was changed // TODO: Multithreading for f in &opts.filter { let filtered = png.filter_image(*f); @@ -102,7 +125,8 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { Ok(x) => x, Err(x) => return Err(x), }; - if new_idat.len() < png.idat_data.len() { + if new_idat.len() < png.idat_data.len() || + (best.is_none() && something_changed) { best = Some((*f, *zc, *zm, *zs)); png.idat_data = new_idat.clone(); } @@ -136,7 +160,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { } let output_data = png.output(); - if file_original_size <= output_data.len() && !opts.force { + if file_original_size <= output_data.len() && !opts.force && opts.interlace.is_none() { println!("File already optimized"); return Ok(()); } diff --git a/src/png.rs b/src/png.rs index 33b5308f..5c260740 100644 --- a/src/png.rs +++ b/src/png.rs @@ -518,6 +518,47 @@ impl PngData { } filtered } + pub fn reduce_bit_depth(&self) -> Option> { + // TODO: Implement + if self.ihdr_data.bit_depth != BitDepth::Sixteen { + // Can't reduce here--palette reduction is handled elsewhere + return None; + } + + // It's difficult to estimate without knowing the number of ScanLines + // So we overallocate to prioritize speed over memory efficiency + let mut reduced = Vec::with_capacity(self.raw_data.len()); + + for line in self.scan_lines() { + reduced.push(line.filter); + for (i, byte) in line.data.iter().enumerate() { + if i % 2 == 0 { + // High byte + if *byte != 0 { + // Can't reduce, exit early + return None; + } + } else { + // Low byte + reduced.push(*byte); + } + } + } + + Some(reduced) + } + pub fn reduce_palette(&self) -> Option> { + // TODO: Implement + None + } + pub fn reduce_color_type(&self) -> Option> { + // TODO: Implement + None + } + pub fn change_interlacing(&self, interlace: u8) -> Option> { + // TODO: Implement + None + } } fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {