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!(" 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(());
}

View file

@ -518,6 +518,47 @@ impl PngData {
}
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 {