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();
|
||||
|
||||
b.iter(|| color::indexed_to_channels(&png.raw, true));
|
||||
b.iter(|| color::indexed_to_channels(&png.raw, true, false));
|
||||
}
|
||||
|
||||
#[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
|
||||
#[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 {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
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]
|
||||
fn palette_8_should_be_rgb() {
|
||||
test_it_converts(
|
||||
|
|
|
|||
Loading…
Reference in a new issue