diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fa6a0b6..8c9c7dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### Version 2.1.1 + - More fixes for alpha optimization on interlaced images ([#133](https://github.com/shssoichiro/oxipng/issues/133)) + ### Version 2.1.0 - [SEMVER_MINOR] Bump minimum Rust version to 1.27.0 - [SEMVER_MINOR] Reenable faster Cloudflare zlib compression on platforms that support it diff --git a/src/png/mod.rs b/src/png/mod.rs index 6f78441d..33803c76 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -712,9 +712,12 @@ impl PngData { let mut lines = Vec::new(); let mut scan_lines = self.scan_lines().collect::>(); scan_lines.reverse(); - let mut last_line = vec![0; scan_lines[0].data.len()]; + let mut last_line = Vec::new(); let mut current_line = Vec::with_capacity(last_line.len()); for line in scan_lines { + if line.data.len() != last_line.len() { + last_line = vec![0; line.data.len()]; + } current_line.push(line.filter); for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { @@ -735,8 +738,11 @@ impl PngData { fn reduce_alpha_to_down(&self, bpc: usize, bpp: usize) -> Vec { let mut reduced = Vec::with_capacity(self.raw_data.len()); - let mut last_line = vec![0; self.scan_lines().next().unwrap().data.len()]; + let mut last_line = Vec::new(); for line in self.scan_lines() { + if line.data.len() != last_line.len() { + last_line = vec![0; line.data.len()]; + } reduced.push(line.filter); for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { diff --git a/tests/files/issue-133.png b/tests/files/issue-133.png new file mode 100644 index 00000000..0d3eb871 Binary files /dev/null and b/tests/files/issue-133.png differ diff --git a/tests/regression.rs b/tests/regression.rs index 8ccfa6c6..b09d3bf3 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -291,14 +291,14 @@ fn issue_92_filter_5() { } #[test] -fn issue_113() { +fn issue_113_white() { let input = "tests/files/issue-113.png"; let (_, mut opts) = get_opts(Path::new(input)); opts.interlace = Some(1); opts.alphas = HashSet::new(); - opts.alphas.insert(AlphaOptim::Up); + opts.alphas.insert(AlphaOptim::Black); let output = OutFile::Path(Some( - Path::new(input).with_extension("-f5-out.png").to_owned(), + Path::new(input).with_extension("-white-out.png").to_owned(), )); test_it_converts( input, @@ -309,3 +309,217 @@ fn issue_113() { BitDepth::Eight, ); } + +#[test] +fn issue_113_black() { + let input = "tests/files/issue-113.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.interlace = Some(1); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Black); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-black-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::GrayscaleAlpha, + BitDepth::Eight, + ); +} + +#[test] +fn issue_113_right() { + let input = "tests/files/issue-113.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.interlace = Some(1); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Right); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-right-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::GrayscaleAlpha, + BitDepth::Eight, + ); +} + +#[test] +fn issue_113_left() { + let input = "tests/files/issue-113.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.interlace = Some(1); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Left); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-left-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::GrayscaleAlpha, + BitDepth::Eight, + ); +} + +#[test] +fn issue_113_up() { + let input = "tests/files/issue-113.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.interlace = Some(1); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Up); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-up-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::GrayscaleAlpha, + BitDepth::Eight, + ); +} + +#[test] +fn issue_113_down() { + let input = "tests/files/issue-113.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.interlace = Some(1); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Down); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-down-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::GrayscaleAlpha, + BitDepth::Eight, + ); +} + +#[test] +fn issue_133_black() { + let input = "tests/files/issue-133.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Black); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-black-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn issue_133_white() { + let input = "tests/files/issue-133.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::White); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-white-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn issue_133_up() { + let input = "tests/files/issue-133.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Up); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-up-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn issue_133_down() { + let input = "tests/files/issue-133.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Down); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-down-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn issue_133_right() { + let input = "tests/files/issue-133.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Right); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-right-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn issue_133_left() { + let input = "tests/files/issue-133.png"; + let (_, mut opts) = get_opts(Path::new(input)); + opts.alphas = HashSet::new(); + opts.alphas.insert(AlphaOptim::Left); + let output = OutFile::Path(Some( + Path::new(input).with_extension("-left-out.png").to_owned(), + )); + test_it_converts( + input, + Some((output, opts)), + ColorType::RGBA, + BitDepth::Eight, + ColorType::RGBA, + BitDepth::Eight, + ); +}