diff --git a/benches/reductions.rs b/benches/reductions.rs index e0b3f265..251c165c 100644 --- a/benches/reductions.rs +++ b/benches/reductions.rs @@ -257,7 +257,7 @@ fn reductions_palette_8_to_grayscale_8(b: &mut Bencher) { )); let png = PngData::new(&input, &Options::default()).unwrap(); - b.iter(|| color::indexed_to_channels(&png.raw, true)); + b.iter(|| color::indexed_to_channels(&png.raw, true, false)); } #[bench] diff --git a/src/reduction/color.rs b/src/reduction/color.rs index 8992d1c7..fe11d048 100644 --- a/src/reduction/color.rs +++ b/src/reduction/color.rs @@ -138,15 +138,30 @@ pub fn reduced_rgb_to_grayscale(png: &PngImage) -> Option { /// Attempt to convert indexed to a different color type, returning the resulting image if successful #[must_use] -pub fn indexed_to_channels(png: &PngImage, allow_grayscale: bool) -> Option { +pub fn indexed_to_channels( + png: &PngImage, + allow_grayscale: bool, + optimize_alpha: bool, +) -> Option { if png.ihdr.bit_depth != BitDepth::Eight { return None; } - let palette = match &png.ihdr.color_type { - ColorType::Indexed { palette } => palette, + let mut palette = match &png.ihdr.color_type { + ColorType::Indexed { palette } => palette.clone(), _ => return None, }; + // Ensure fully transparent colors are black, which can help with grayscale conversion + if optimize_alpha { + for color in &mut palette { + if color.a == 0 { + color.r = 0; + color.g = 0; + color.b = 0; + } + } + } + // Determine which channels are required let is_gray = if allow_grayscale { palette.iter().all(|c| c.r == c.g && c.g == c.b) diff --git a/src/reduction/mod.rs b/src/reduction/mod.rs index 9b860c5f..dc6202a1 100644 --- a/src/reduction/mod.rs +++ b/src/reduction/mod.rs @@ -106,7 +106,9 @@ pub(crate) fn perform_reductions( // Attempt to convert from indexed to channels // This may give a better result due to dropping the PLTE chunk if !cheap && opts.color_type_reduction && !deadline.passed() { - if let Some(reduced) = indexed_to_channels(&png, opts.grayscale_reduction) { + if let Some(reduced) = + indexed_to_channels(&png, opts.grayscale_reduction, opts.optimize_alpha) + { // This result should not be passed on to subsequent reductions eval.try_image(Arc::new(reduced)); evaluation_added = true; diff --git a/tests/files/palette_2_should_be_grayscale_alpha_8.png b/tests/files/palette_2_should_be_grayscale_alpha_8.png new file mode 100644 index 00000000..c83f2543 Binary files /dev/null and b/tests/files/palette_2_should_be_grayscale_alpha_8.png differ diff --git a/tests/reduction.rs b/tests/reduction.rs index 88f7fc0a..4e33f7e2 100644 --- a/tests/reduction.rs +++ b/tests/reduction.rs @@ -578,6 +578,18 @@ fn palette_8_should_be_grayscale_8() { ); } +#[test] +fn palette_2_should_be_grayscale_alpha_8() { + test_it_converts( + "tests/files/palette_2_should_be_grayscale_alpha_8.png", + true, + INDEXED, + BitDepth::Two, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} + #[test] fn palette_8_should_be_rgb() { test_it_converts(