Implement bit depth reduction

This commit is contained in:
Joshua Holmer 2016-01-12 11:50:39 -05:00
parent 939d886932
commit 9267f040af
2 changed files with 76 additions and 11 deletions

View file

@ -74,20 +74,43 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
println!(" IDAT size = {} bytes", idat_original_size); println!(" IDAT size = {} bytes", idat_original_size);
println!(" File size = {} bytes", file_original_size); println!(" File size = {} bytes", file_original_size);
} }
// TODO: Color space reduction
// TODO: 16-bit Bit depth reduction let mut something_changed = false;
// TODO: Palette bit depth reduction
// if opts.color_type_reduction {
// TODO: Apply interlacing changes if let Some(data) = png.reduce_color_type() {
// png.raw_data = data;
// TODO: Force reencoding if interlacing was changed something_changed = true;
if opts.idat_recoding { };
}
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 // Go through selected permutations and determine the best
let mut best: Option<(u8, u8, u8, u8)> = None; let mut best: Option<(u8, u8, u8, u8)> = None;
let combinations = opts.filter.len() * opts.compression.len() * opts.memory.len() * let combinations = opts.filter.len() * opts.compression.len() * opts.memory.len() *
opts.strategies.len(); opts.strategies.len();
println!("Trying: {} combinations", combinations); println!("Trying: {} combinations", combinations);
// TODO: Force reencoding if interlacing was changed
// TODO: Multithreading // TODO: Multithreading
for f in &opts.filter { for f in &opts.filter {
let filtered = png.filter_image(*f); let filtered = png.filter_image(*f);
@ -102,7 +125,8 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
Ok(x) => x, Ok(x) => x,
Err(x) => return Err(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)); best = Some((*f, *zc, *zm, *zs));
png.idat_data = new_idat.clone(); png.idat_data = new_idat.clone();
} }
@ -136,7 +160,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
} }
let output_data = png.output(); 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"); println!("File already optimized");
return Ok(()); return Ok(());
} }

View file

@ -518,6 +518,47 @@ impl PngData {
} }
filtered filtered
} }
pub fn reduce_bit_depth(&self) -> Option<Vec<u8>> {
// 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<Vec<u8>> {
// TODO: Implement
None
}
pub fn reduce_color_type(&self) -> Option<Vec<u8>> {
// TODO: Implement
None
}
pub fn change_interlacing(&self, interlace: u8) -> Option<Vec<u8>> {
// TODO: Implement
None
}
} }
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 { fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {