Improve indexed to grayscale conversion (#674)
If no palette reduction occurred (or palette reductions were off), a fully transparent palette entry may not have been zeroed to black. By ensuring this gets cleaned in the `indexed_to_channels` transformation, we may able to achieve a grayscale conversion that would otherwise have been RGB. This is the final piece of the puzzle in #649 to achieve better file sizes on the first run and avoid further changes on a second run. Results from the images in #649 (combining this PR and #673): 9.1.3 1st run: 2,191,773 9.1.3 2nd run: 2,191,629 PR 1st run: 2,191,380 PR 2nd run: 2,191,380
This commit is contained in:
parent
e6f84264e5
commit
174f36ad8f
5 changed files with 34 additions and 5 deletions
|
|
@ -257,7 +257,7 @@ fn reductions_palette_8_to_grayscale_8(b: &mut Bencher) {
|
||||||
));
|
));
|
||||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
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]
|
#[bench]
|
||||||
|
|
|
||||||
|
|
@ -138,15 +138,30 @@ pub fn reduced_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
|
||||||
|
|
||||||
/// Attempt to convert indexed to a different color type, returning the resulting image if successful
|
/// Attempt to convert indexed to a different color type, returning the resulting image if successful
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn indexed_to_channels(png: &PngImage, allow_grayscale: bool) -> Option<PngImage> {
|
pub fn indexed_to_channels(
|
||||||
|
png: &PngImage,
|
||||||
|
allow_grayscale: bool,
|
||||||
|
optimize_alpha: bool,
|
||||||
|
) -> Option<PngImage> {
|
||||||
if png.ihdr.bit_depth != BitDepth::Eight {
|
if png.ihdr.bit_depth != BitDepth::Eight {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let palette = match &png.ihdr.color_type {
|
let mut palette = match &png.ihdr.color_type {
|
||||||
ColorType::Indexed { palette } => palette,
|
ColorType::Indexed { palette } => palette.clone(),
|
||||||
_ => return None,
|
_ => 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
|
// Determine which channels are required
|
||||||
let is_gray = if allow_grayscale {
|
let is_gray = if allow_grayscale {
|
||||||
palette.iter().all(|c| c.r == c.g && c.g == c.b)
|
palette.iter().all(|c| c.r == c.g && c.g == c.b)
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,9 @@ pub(crate) fn perform_reductions(
|
||||||
// Attempt to convert from indexed to channels
|
// Attempt to convert from indexed to channels
|
||||||
// This may give a better result due to dropping the PLTE chunk
|
// This may give a better result due to dropping the PLTE chunk
|
||||||
if !cheap && opts.color_type_reduction && !deadline.passed() {
|
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
|
// This result should not be passed on to subsequent reductions
|
||||||
eval.try_image(Arc::new(reduced));
|
eval.try_image(Arc::new(reduced));
|
||||||
evaluation_added = true;
|
evaluation_added = true;
|
||||||
|
|
|
||||||
BIN
tests/files/palette_2_should_be_grayscale_alpha_8.png
Normal file
BIN
tests/files/palette_2_should_be_grayscale_alpha_8.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 B |
|
|
@ -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]
|
#[test]
|
||||||
fn palette_8_should_be_rgb() {
|
fn palette_8_should_be_rgb() {
|
||||||
test_it_converts(
|
test_it_converts(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue