Fix a bug in palette reduction at less than 8 bits per pixel

Closes #82
This commit is contained in:
Josh Holmer 2017-09-11 18:27:28 -04:00
parent 6d1005d199
commit e17f911334
4 changed files with 33 additions and 15 deletions

View file

@ -7,7 +7,8 @@
trials for optimizing the alpha channel, using the previously mentioned fast heuristic. trials for optimizing the alpha channel, using the previously mentioned fast heuristic.
This option will make optimization of images with transparency somewhat slower, This option will make optimization of images with transparency somewhat slower,
but may improve compression. but may improve compression.
- Fixed a bug in reducing palettes for images with bit depth of two([#80](https://github.com/shssoichiro/oxipng/issues/80)) - Fixed a bug in reducing palettes for images with bit depth of two ([#80](https://github.com/shssoichiro/oxipng/issues/80))
- Fixed another bug in reducing palettes for images with bit depth less than eight ([#82](https://github.com/shssoichiro/oxipng/issues/82))
- Code cleanup - Code cleanup
- Update dependencies - Update dependencies

View file

@ -95,15 +95,15 @@ impl<'a> Iterator for ScanLines<'a> {
1 | 3 | 5 => { 1 | 3 | 5 => {
pixels_per_line += 1; pixels_per_line += 1;
} }
2 => if gap >= 5 { 2 if gap >= 5 => {
pixels_per_line += 1; pixels_per_line += 1;
}, }
4 => if gap >= 3 { 4 if gap >= 3 => {
pixels_per_line += 1; pixels_per_line += 1;
}, }
6 => if gap >= 2 { 6 if gap >= 2 => {
pixels_per_line += 1; pixels_per_line += 1;
}, }
_ => (), _ => (),
}; };
} }
@ -112,7 +112,7 @@ impl<'a> Iterator for ScanLines<'a> {
} else { } else {
None None
}; };
let bytes_per_line = ((pixels_per_line * bits_per_pixel) as f32 / 8f32).ceil() as usize; let bytes_per_line = ((pixels_per_line * bits_per_pixel + 7) / 8) as usize;
self.start = self.end; self.start = self.end;
self.end = self.start + bytes_per_line + 1; self.end = self.start + bytes_per_line + 1;
if let Some(pass) = self.pass.as_mut() { if let Some(pass) = self.pass.as_mut() {
@ -138,7 +138,7 @@ impl<'a> Iterator for ScanLines<'a> {
let bits_per_line = self.png.ihdr_data.width as usize * let bits_per_line = self.png.ihdr_data.width as usize *
self.png.ihdr_data.bit_depth.as_u8() as usize * self.png.ihdr_data.bit_depth.as_u8() as usize *
self.png.channels_per_pixel() as usize; self.png.channels_per_pixel() as usize;
let bytes_per_line = (bits_per_line as f32 / 8f32).ceil() as usize; let bytes_per_line = (bits_per_line + 7) / 8 as usize;
self.start = self.end; self.start = self.end;
self.end = self.start + bytes_per_line + 1; self.end = self.start + bytes_per_line + 1;
Some(ScanLine { Some(ScanLine {
@ -588,13 +588,13 @@ impl PngData {
fn do_palette_reduction( fn do_palette_reduction(
&mut self, &mut self,
indices: &[u8], indices_to_remove: &[u8],
index_map: &mut HashMap<u8, u8>, index_map: &mut HashMap<u8, u8>,
indexed_palette: &mut Vec<&[u8]>, indexed_palette: &mut Vec<&[u8]>,
) { ) {
let mut new_data = Vec::with_capacity(self.raw_data.len()); let mut new_data = Vec::with_capacity(self.raw_data.len());
let original_len = indexed_palette.len(); let original_len = indexed_palette.len();
for idx in indices.iter().sorted_by(|a, b| b.cmp(a)) { for idx in indices_to_remove.iter().sorted_by(|a, b| b.cmp(a)) {
for i in (*idx as usize + 1)..original_len { for i in (*idx as usize + 1)..original_len {
let existing = index_map.entry(i as u8).or_insert(i as u8); let existing = index_map.entry(i as u8).or_insert(i as u8);
if *existing >= *idx { if *existing >= *idx {
@ -628,7 +628,7 @@ impl PngData {
let upper = *byte & 0b1111_0000; let upper = *byte & 0b1111_0000;
let lower = *byte & 0b0000_1111; let lower = *byte & 0b0000_1111;
let mut new_byte = 0u8; let mut new_byte = 0u8;
new_byte |= if let Some(new_idx) = index_map.get(&upper) { new_byte |= if let Some(new_idx) = index_map.get(&(upper >> 4)) {
*new_idx << 4 *new_idx << 4
} else { } else {
upper upper
@ -646,17 +646,17 @@ impl PngData {
let three = *byte & 0b0000_1100; let three = *byte & 0b0000_1100;
let four = *byte & 0b0000_0011; let four = *byte & 0b0000_0011;
let mut new_byte = 0u8; let mut new_byte = 0u8;
new_byte |= if let Some(new_idx) = index_map.get(&one) { new_byte |= if let Some(new_idx) = index_map.get(&(one >> 6)) {
*new_idx << 6 *new_idx << 6
} else { } else {
one one
}; };
new_byte |= if let Some(new_idx) = index_map.get(&two) { new_byte |= if let Some(new_idx) = index_map.get(&(two >> 4)) {
*new_idx << 4 *new_idx << 4
} else { } else {
two two
}; };
new_byte |= if let Some(new_idx) = index_map.get(&three) { new_byte |= if let Some(new_idx) = index_map.get(&(three >> 2)) {
*new_idx << 2 *new_idx << 2
} else { } else {
three three

BIN
tests/files/issue-82.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

View file

@ -291,3 +291,20 @@ fn issue_80() {
BitDepth::One, BitDepth::One,
); );
} }
#[test]
fn issue_82() {
let input = PathBuf::from("tests/files/issue-82.png");
let opts = get_opts(&input);
let output = opts.out_file.clone();
test_it_converts(
&input,
&output,
&opts,
ColorType::Indexed,
BitDepth::Four,
ColorType::Indexed,
BitDepth::Four,
);
}