diff --git a/CHANGELOG.md b/CHANGELOG.md index 866a1b3e..fe57fb43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ **Version 0.8.1 (unreleased)** - Minor optimizations + - Fix issue where interlaced images with certain widths would fail to optimize **Version 0.8.0** - [SEMVER_MINOR] Add support for optimizing PNGs already loaded into memory via library function diff --git a/src/png.rs b/src/png.rs index 752d7bf2..14689f92 100644 --- a/src/png.rs +++ b/src/png.rs @@ -151,58 +151,69 @@ impl<'a> Iterator for ScanLines<'a> { pass.1 = 0; } } - let bits_per_pixel = self.png.ihdr_data.bit_depth.as_u8() as usize * - self.png.channels_per_pixel() as usize; - let mut bits_per_line = self.png.ihdr_data.width as usize * bits_per_pixel; + let bits_per_pixel = self.png.ihdr_data.bit_depth.as_u8() as u32 * + self.png.channels_per_pixel() as u32; let y_steps; + let pixels_factor; match self.pass { Some((1, _)) | Some((2, _)) => { - bits_per_line = (bits_per_line as f32 / 8f32).ceil() as usize; + pixels_factor = 8; y_steps = 8; } Some((3, _)) => { - bits_per_line = (bits_per_line as f32 / 4f32).ceil() as usize; + pixels_factor = 4; y_steps = 8; } Some((4, _)) => { - bits_per_line = (bits_per_line as f32 / 4f32).ceil() as usize; + pixels_factor = 4; y_steps = 4; } Some((5, _)) => { - bits_per_line = (bits_per_line as f32 / 2f32).ceil() as usize; + pixels_factor = 2; y_steps = 4; } Some((6, _)) => { - bits_per_line = (bits_per_line as f32 / 2f32).ceil() as usize; + pixels_factor = 2; y_steps = 2; } Some((7, _)) => { + pixels_factor = 1; y_steps = 2; } _ => unreachable!(), } - // Determine whether to trim the last (overflow) pixel for rows on this pass - let gap = bits_per_line % bits_per_pixel; - if gap != 0 { - let x_start = bits_per_pixel * - match self.pass.unwrap().0 { - 2 => 4, - 4 => 2, - 6 => 1, - _ => 0, + let mut pixels_per_line = self.png.ihdr_data.width / pixels_factor as u32; + // Determine whether to add pixels if there is a final, incomplete 8x8 block + let gap = self.png.ihdr_data.width % pixels_factor; + if gap > 0 { + match self.pass.unwrap().0 { + 1 | 3 | 5 => { + pixels_per_line += 1; + } + 2 => { + if gap >= 5 { + pixels_per_line += 1; + } + } + 4 => { + if gap >= 3 { + pixels_per_line += 1; + } + } + 6 => { + if gap >= 2 { + pixels_per_line += 1; + } + } + _ => (), }; - if gap >= x_start { - bits_per_line += bits_per_pixel - gap; - } else { - bits_per_line -= gap; - } } let current_pass = if let Some(pass) = self.pass { Some(pass.0) } else { None }; - let bytes_per_line = (bits_per_line as f32 / 8f32).ceil() as usize; + let bytes_per_line = ((pixels_per_line * bits_per_pixel) as f32 / 8f32).ceil() as usize; self.start = self.end; self.end = self.start + bytes_per_line + 1; if let Some(pass) = self.pass.as_mut() { diff --git a/tests/files/interlaced_odd_width.png b/tests/files/interlaced_odd_width.png new file mode 100644 index 00000000..82e7bc0a Binary files /dev/null and b/tests/files/interlaced_odd_width.png differ diff --git a/tests/interlaced.rs b/tests/interlaced.rs index 394d697c..686cea05 100644 --- a/tests/interlaced.rs +++ b/tests/interlaced.rs @@ -1246,3 +1246,18 @@ fn interlaced_small_files() { png::ColorType::Indexed, png::BitDepth::One); } + +#[test] +fn interlaced_odd_width() { + let input = PathBuf::from("tests/files/interlaced_odd_width.png"); + let opts = get_opts(&input); + let output = opts.out_file.clone(); + + test_it_converts(&input, + &output, + &opts, + png::ColorType::RGB, + png::BitDepth::Eight, + png::ColorType::RGB, + png::BitDepth::Eight); +}