Implement bit depth reduction
This commit is contained in:
parent
939d886932
commit
9267f040af
2 changed files with 76 additions and 11 deletions
46
src/lib.rs
46
src/lib.rs
|
|
@ -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(());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
src/png.rs
41
src/png.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue