diff --git a/benches/deflate.rs b/benches/deflate.rs index d83e3230..de9e0318 100644 --- a/benches/deflate.rs +++ b/benches/deflate.rs @@ -9,7 +9,7 @@ use test::Bencher; use oxipng::internal_tests::*; #[bench] -fn deflate_16_bits_strategy_0(b: &mut Bencher) { +fn deflate_16_bits(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png")); let png = PngData::new(&input, false).unwrap(); @@ -20,7 +20,7 @@ fn deflate_16_bits_strategy_0(b: &mut Bencher) { } #[bench] -fn deflate_8_bits_strategy_0(b: &mut Bencher) { +fn deflate_8_bits(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png")); let png = PngData::new(&input, false).unwrap(); @@ -31,7 +31,7 @@ fn deflate_8_bits_strategy_0(b: &mut Bencher) { } #[bench] -fn deflate_4_bits_strategy_0(b: &mut Bencher) { +fn deflate_4_bits(b: &mut Bencher) { let input = test::black_box(PathBuf::from( "tests/files/palette_4_should_be_palette_4.png", )); @@ -44,7 +44,7 @@ fn deflate_4_bits_strategy_0(b: &mut Bencher) { } #[bench] -fn deflate_2_bits_strategy_0(b: &mut Bencher) { +fn deflate_2_bits(b: &mut Bencher) { let input = test::black_box(PathBuf::from( "tests/files/palette_2_should_be_palette_2.png", )); @@ -57,190 +57,7 @@ fn deflate_2_bits_strategy_0(b: &mut Bencher) { } #[bench] -fn deflate_1_bits_strategy_0(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_1_should_be_palette_1.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_16_bits_strategy_1(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_8_bits_strategy_1(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_4_bits_strategy_1(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_4_should_be_palette_4.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_2_bits_strategy_1(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_2_should_be_palette_2.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_1_bits_strategy_1(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_1_should_be_palette_1.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_16_bits_strategy_2(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_8_bits_strategy_2(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_4_bits_strategy_2(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_4_should_be_palette_4.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_2_bits_strategy_2(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_2_should_be_palette_2.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_1_bits_strategy_2(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_1_should_be_palette_1.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_16_bits_strategy_3(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_8_bits_strategy_3(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_4_bits_strategy_3(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_4_should_be_palette_4.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_2_bits_strategy_3(b: &mut Bencher) { - let input = test::black_box(PathBuf::from( - "tests/files/palette_2_should_be_palette_2.png", - )); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| { - let min = AtomicMin::new(None); - deflate(png.raw.data.as_ref(), 12, &min) - }); -} - -#[bench] -fn deflate_1_bits_strategy_3(b: &mut Bencher) { +fn deflate_1_bits(b: &mut Bencher) { let input = test::black_box(PathBuf::from( "tests/files/palette_1_should_be_palette_1.png", )); diff --git a/benches/filters.rs b/benches/filters.rs index 687f9709..b1ce453a 100644 --- a/benches/filters.rs +++ b/benches/filters.rs @@ -13,7 +13,7 @@ fn filters_16_bits_filter_0(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::None); + png.raw.filter_image(RowFilter::None, false); }); } @@ -23,7 +23,7 @@ fn filters_8_bits_filter_0(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::None); + png.raw.filter_image(RowFilter::None, false); }); } @@ -35,7 +35,7 @@ fn filters_4_bits_filter_0(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::None); + png.raw.filter_image(RowFilter::None, false); }); } @@ -47,7 +47,7 @@ fn filters_2_bits_filter_0(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::None); + png.raw.filter_image(RowFilter::None, false); }); } @@ -59,7 +59,7 @@ fn filters_1_bits_filter_0(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::None); + png.raw.filter_image(RowFilter::None, false); }); } @@ -69,7 +69,7 @@ fn filters_16_bits_filter_1(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Sub); + png.raw.filter_image(RowFilter::Sub, false); }); } @@ -79,7 +79,7 @@ fn filters_8_bits_filter_1(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Sub); + png.raw.filter_image(RowFilter::Sub, false); }); } @@ -91,7 +91,7 @@ fn filters_4_bits_filter_1(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Sub); + png.raw.filter_image(RowFilter::Sub, false); }); } @@ -103,7 +103,7 @@ fn filters_2_bits_filter_1(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Sub); + png.raw.filter_image(RowFilter::Sub, false); }); } @@ -115,7 +115,7 @@ fn filters_1_bits_filter_1(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Sub); + png.raw.filter_image(RowFilter::Sub, false); }); } @@ -125,7 +125,7 @@ fn filters_16_bits_filter_2(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Up); + png.raw.filter_image(RowFilter::Up, false); }); } @@ -135,7 +135,7 @@ fn filters_8_bits_filter_2(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Up); + png.raw.filter_image(RowFilter::Up, false); }); } @@ -147,7 +147,7 @@ fn filters_4_bits_filter_2(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Up); + png.raw.filter_image(RowFilter::Up, false); }); } @@ -159,7 +159,7 @@ fn filters_2_bits_filter_2(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Up); + png.raw.filter_image(RowFilter::Up, false); }); } @@ -171,7 +171,7 @@ fn filters_1_bits_filter_2(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Up); + png.raw.filter_image(RowFilter::Up, false); }); } @@ -181,7 +181,7 @@ fn filters_16_bits_filter_3(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Average); + png.raw.filter_image(RowFilter::Average, false); }); } @@ -191,7 +191,7 @@ fn filters_8_bits_filter_3(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Average); + png.raw.filter_image(RowFilter::Average, false); }); } @@ -203,7 +203,7 @@ fn filters_4_bits_filter_3(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Average); + png.raw.filter_image(RowFilter::Average, false); }); } @@ -215,7 +215,7 @@ fn filters_2_bits_filter_3(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Average); + png.raw.filter_image(RowFilter::Average, false); }); } @@ -227,7 +227,7 @@ fn filters_1_bits_filter_3(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Average); + png.raw.filter_image(RowFilter::Average, false); }); } @@ -237,7 +237,7 @@ fn filters_16_bits_filter_4(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Paeth); + png.raw.filter_image(RowFilter::Paeth, false); }); } @@ -247,7 +247,7 @@ fn filters_8_bits_filter_4(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Paeth); + png.raw.filter_image(RowFilter::Paeth, false); }); } @@ -259,7 +259,7 @@ fn filters_4_bits_filter_4(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Paeth); + png.raw.filter_image(RowFilter::Paeth, false); }); } @@ -271,7 +271,7 @@ fn filters_2_bits_filter_4(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Paeth); + png.raw.filter_image(RowFilter::Paeth, false); }); } @@ -283,7 +283,7 @@ fn filters_1_bits_filter_4(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Paeth); + png.raw.filter_image(RowFilter::Paeth, false); }); } @@ -293,7 +293,7 @@ fn filters_16_bits_filter_5(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::MinSum); + png.raw.filter_image(RowFilter::MinSum, false); }); } @@ -303,7 +303,7 @@ fn filters_8_bits_filter_5(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::MinSum); + png.raw.filter_image(RowFilter::MinSum, false); }); } @@ -315,7 +315,7 @@ fn filters_4_bits_filter_5(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::MinSum); + png.raw.filter_image(RowFilter::MinSum, false); }); } @@ -327,7 +327,7 @@ fn filters_2_bits_filter_5(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::MinSum); + png.raw.filter_image(RowFilter::MinSum, false); }); } @@ -339,6 +339,6 @@ fn filters_1_bits_filter_5(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::MinSum); + png.raw.filter_image(RowFilter::MinSum, false); }); } diff --git a/benches/reductions.rs b/benches/reductions.rs index 30eebe17..774c346a 100644 --- a/benches/reductions.rs +++ b/benches/reductions.rs @@ -80,7 +80,7 @@ fn reductions_rgba_to_rgb_16(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgba_16_should_be_rgb_16.png")); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -88,7 +88,7 @@ fn reductions_rgba_to_rgb_8(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgba_8_should_be_rgb_8.png")); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -98,7 +98,7 @@ fn reductions_rgba_to_grayscale_alpha_16(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -108,7 +108,7 @@ fn reductions_rgba_to_grayscale_alpha_8(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -118,7 +118,7 @@ fn reductions_rgba_to_grayscale_16(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -128,7 +128,7 @@ fn reductions_rgba_to_grayscale_8(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -138,7 +138,7 @@ fn reductions_rgb_to_grayscale_16(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -146,7 +146,7 @@ fn reductions_rgb_to_grayscale_8(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_grayscale_8.png")); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -154,7 +154,7 @@ fn reductions_rgba_to_palette_8(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgba_8_should_be_palette_8.png")); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -162,7 +162,7 @@ fn reductions_rgb_to_palette_8(b: &mut Bencher) { let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_palette_8.png")); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduce_color_type(&png.raw, true)); + b.iter(|| reduce_color_type(&png.raw, true, false)); } #[bench] @@ -172,7 +172,7 @@ fn reductions_palette_duplicate_reduction(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduced_palette(&png.raw)); + b.iter(|| reduced_palette(&png.raw, false)); } #[bench] @@ -182,7 +182,7 @@ fn reductions_palette_unused_reduction(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduced_palette(&png.raw)); + b.iter(|| reduced_palette(&png.raw, false)); } #[bench] @@ -192,53 +192,13 @@ fn reductions_palette_full_reduction(b: &mut Bencher) { )); let png = PngData::new(&input, false).unwrap(); - b.iter(|| reduced_palette(&png.raw)); + b.iter(|| reduced_palette(&png.raw, false)); } #[bench] -fn reductions_alpha_black(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_black.png")); +fn reductions_alpha(b: &mut Bencher) { + let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha.png")); let png = PngData::new(&input, false).unwrap(); - b.iter(|| alpha::filtered_alpha_channel(&png.raw, AlphaOptim::Black)); -} - -#[bench] -fn reductions_alpha_white(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_white.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| alpha::filtered_alpha_channel(&png.raw, AlphaOptim::White)); -} - -#[bench] -fn reductions_alpha_left(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_left.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| alpha::filtered_alpha_channel(&png.raw, AlphaOptim::Left)); -} - -#[bench] -fn reductions_alpha_right(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_right.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| alpha::filtered_alpha_channel(&png.raw, AlphaOptim::Right)); -} - -#[bench] -fn reductions_alpha_up(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_up.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| alpha::filtered_alpha_channel(&png.raw, AlphaOptim::Up)); -} - -#[bench] -fn reductions_alpha_down(b: &mut Bencher) { - let input = test::black_box(PathBuf::from("tests/files/rgba_8_reduce_alpha_down.png")); - let png = PngData::new(&input, false).unwrap(); - - b.iter(|| alpha::filtered_alpha_channel(&png.raw, AlphaOptim::Down)); + b.iter(|| alpha::cleaned_alpha_channel(&png.raw)); } diff --git a/benches/strategies.rs b/benches/strategies.rs index a8b6c091..de5a009a 100644 --- a/benches/strategies.rs +++ b/benches/strategies.rs @@ -13,7 +13,7 @@ fn filters_minsum(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::MinSum); + png.raw.filter_image(RowFilter::MinSum, false); }); } @@ -23,7 +23,7 @@ fn filters_entropy(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Entropy); + png.raw.filter_image(RowFilter::Entropy, false); }); } @@ -33,7 +33,7 @@ fn filters_bigrams(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Bigrams); + png.raw.filter_image(RowFilter::Bigrams, false); }); } @@ -43,7 +43,7 @@ fn filters_bigent(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::BigEnt); + png.raw.filter_image(RowFilter::BigEnt, false); }); } @@ -53,6 +53,6 @@ fn filters_brute(b: &mut Bencher) { let png = PngData::new(&input, false).unwrap(); b.iter(|| { - png.raw.filter_image(RowFilter::Brute); + png.raw.filter_image(RowFilter::Brute, false); }); } diff --git a/src/colors.rs b/src/colors.rs index 350f4424..e339092b 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -117,33 +117,3 @@ impl BitDepth { } } } - -#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)] -/// Potential optimization methods for alpha channel -pub enum AlphaOptim { - NoOp, - Black, - White, - Up, - Right, - Down, - Left, -} - -impl fmt::Display for AlphaOptim { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match *self { - AlphaOptim::NoOp => "_", - AlphaOptim::Black => "B", - AlphaOptim::White => "W", - AlphaOptim::Up => "U", - AlphaOptim::Right => "R", - AlphaOptim::Down => "D", - AlphaOptim::Left => "L", - } - ) - } -} diff --git a/src/evaluate.rs b/src/evaluate.rs index 13cc0473..23cc9832 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -44,6 +44,7 @@ pub(crate) struct Evaluator { deadline: Arc, filters: IndexSet, compression: u8, + optimize_alpha: bool, nth: AtomicUsize, best_candidate_size: Arc, /// images are sent to the caller thread for evaluation @@ -55,13 +56,19 @@ pub(crate) struct Evaluator { } impl Evaluator { - pub fn new(deadline: Arc, filters: IndexSet, compression: u8) -> Self { + pub fn new( + deadline: Arc, + filters: IndexSet, + compression: u8, + optimize_alpha: bool, + ) -> Self { #[cfg(feature = "parallel")] let eval_channel = unbounded(); Self { deadline, filters, compression, + optimize_alpha, best_candidate_size: Arc::new(AtomicMin::new(None)), nth: AtomicUsize::new(0), #[cfg(feature = "parallel")] @@ -106,6 +113,7 @@ impl Evaluator { let deadline = self.deadline.clone(); let filters = self.filters.clone(); let compression = self.compression; + let optimize_alpha = self.optimize_alpha; let best_candidate_size = self.best_candidate_size.clone(); // sends it off asynchronously for compression, // but results will be collected via the message queue @@ -122,7 +130,7 @@ impl Evaluator { if deadline.passed() { return; } - let filtered = image.filter_image(filter); + let filtered = image.filter_image(filter, optimize_alpha); if let Ok(idat_data) = deflate::deflate(&filtered, compression, &best_candidate_size) { diff --git a/src/filters.rs b/src/filters.rs index ec62e8fe..d43c2833 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -56,9 +56,21 @@ impl RowFilter { pub const STANDARD: [Self; 5] = [Self::None, Self::Sub, Self::Up, Self::Average, Self::Paeth]; pub const SINGLE_LINE: [Self; 2] = [Self::None, Self::Sub]; - pub fn filter_line(self, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec) { + pub fn filter_line( + self, + bpp: usize, + data: &mut [u8], + prev_line: &[u8], + buf: &mut Vec, + alpha_bytes: usize, + ) { assert!(data.len() >= bpp); - assert!(last_line.is_empty() || data.len() == last_line.len()); + assert_eq!(data.len(), prev_line.len()); + + if alpha_bytes != 0 { + self.optimize_alpha(bpp, data, prev_line, bpp - alpha_bytes); + } + buf.clear(); buf.reserve(data.len() + 1); buf.push(self as u8); @@ -76,68 +88,105 @@ impl RowFilter { ); } Self::Up => { - if last_line.is_empty() { - buf.extend_from_slice(data); - } else { - assert_eq!(data.len(), last_line.len()); - buf.extend( - data.iter() - .zip(last_line.iter()) - .map(|(cur, last)| cur.wrapping_sub(*last)), - ); - }; + buf.extend( + data.iter() + .zip(prev_line.iter()) + .map(|(cur, last)| cur.wrapping_sub(*last)), + ); } Self::Average => { for (i, byte) in data.iter().enumerate() { - if last_line.is_empty() { - buf.push(match i.checked_sub(bpp) { - Some(x) => byte.wrapping_sub(data[x] >> 1), - None => *byte, - }); - } else { - buf.push(match i.checked_sub(bpp) { - Some(x) => byte.wrapping_sub( - ((u16::from(data[x]) + u16::from(last_line[i])) >> 1) as u8, - ), - None => byte.wrapping_sub(last_line[i] >> 1), - }); - }; + buf.push(match i.checked_sub(bpp) { + Some(x) => byte.wrapping_sub( + ((u16::from(data[x]) + u16::from(prev_line[i])) >> 1) as u8, + ), + None => byte.wrapping_sub(prev_line[i] >> 1), + }); } } Self::Paeth => { for (i, byte) in data.iter().enumerate() { - if last_line.is_empty() { - buf.push(match i.checked_sub(bpp) { - Some(x) => byte.wrapping_sub(data[x]), - None => *byte, - }); - } else { - buf.push(match i.checked_sub(bpp) { - Some(x) => byte.wrapping_sub(paeth_predictor( - data[x], - last_line[i], - last_line[x], - )), - None => byte.wrapping_sub(last_line[i]), - }); - }; + buf.push(match i.checked_sub(bpp) { + Some(x) => { + byte.wrapping_sub(paeth_predictor(data[x], prev_line[i], prev_line[x])) + } + None => byte.wrapping_sub(prev_line[i]), + }); } } _ => unreachable!(), } } + // Optimize fully transparent pixels of a scanline such that they will be zeroed when filtered + fn optimize_alpha(self, bpp: usize, data: &mut [u8], prev_line: &[u8], color_bytes: usize) { + if self == Self::None { + // Assume transparent pixels already set to 0 + return; + } + + let mut pixels: Vec<_> = data.chunks_mut(bpp).collect(); + let prev_pixels: Vec<_> = prev_line.chunks(bpp).collect(); + for i in 0..pixels.len() { + if pixels[i].iter().skip(color_bytes).all(|b| *b == 0) { + // If the first pixel in the row is transparent, find the next non-transparent pixel and pretend + // it is the previous one. This can help improve effectiveness of the Sub and Paeth filters. + let prev = match i { + 0 => pixels + .iter() + .position(|px| px.iter().skip(color_bytes).any(|b| *b != 0)) + .unwrap_or(i), + _ => i - 1, + }; + match self { + Self::Sub => { + for j in 0..color_bytes { + pixels[i][j] = pixels[prev][j]; + } + } + Self::Up => { + pixels[i][0..color_bytes].copy_from_slice(&prev_pixels[i][0..color_bytes]); + } + Self::Average => { + for j in 0..color_bytes { + pixels[i][j] = match i { + 0 => prev_pixels[i][j] >> 1, + _ => { + ((u16::from(pixels[i - 1][j]) + u16::from(prev_pixels[i][j])) + >> 1) as u8 + } + }; + } + } + Self::Paeth => { + for j in 0..color_bytes { + pixels[i][j] = match i { + 0 => pixels[prev][j].min(prev_pixels[i][j]), + _ => paeth_predictor( + pixels[i - 1][j], + prev_pixels[i][j], + prev_pixels[i - 1][j], + ), + }; + } + } + _ => unreachable!(), + } + } + } + } + pub fn unfilter_line( self, bpp: usize, data: &[u8], - last_line: &[u8], + prev_line: &[u8], buf: &mut Vec, ) -> Result<(), PngError> { buf.clear(); buf.reserve(data.len()); assert!(data.len() >= bpp); - assert_eq!(data.len(), last_line.len()); + assert_eq!(data.len(), prev_line.len()); match self { Self::None => { buf.extend_from_slice(data); @@ -154,12 +203,12 @@ impl RowFilter { Self::Up => { buf.extend( data.iter() - .zip(last_line) + .zip(prev_line) .map(|(&cur, &last)| cur.wrapping_add(last)), ); } Self::Average => { - for (i, (&cur, &last)) in data.iter().zip(last_line).enumerate() { + for (i, (&cur, &last)) in data.iter().zip(prev_line).enumerate() { let prev_byte = i.checked_sub(bpp).and_then(|x| buf.get(x).copied()); buf.push(match prev_byte { Some(b) => cur.wrapping_add(((u16::from(b) + u16::from(last)) >> 1) as u8), @@ -168,11 +217,11 @@ impl RowFilter { } } Self::Paeth => { - for (i, (&cur, &up)) in data.iter().zip(last_line).enumerate() { + for (i, (&cur, &up)) in data.iter().zip(prev_line).enumerate() { buf.push( match i .checked_sub(bpp) - .map(|x| (buf.get(x).copied(), last_line.get(x).copied())) + .map(|x| (buf.get(x).copied(), prev_line.get(x).copied())) { Some((Some(left), Some(left_up))) => { cur.wrapping_add(paeth_predictor(left, up, left_up)) diff --git a/src/lib.rs b/src/lib.rs index cd74186a..46891753 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; -pub use crate::colors::AlphaOptim; pub use crate::deflate::Deflaters; pub use crate::error::PngError; pub use crate::filters::RowFilter; @@ -159,8 +158,8 @@ pub struct Options { /// /// Default: `None` pub interlace: Option, - /// Alpha filtering strategies to use - pub alphas: IndexSet, + /// Whether to allow transparent pixels to be altered to improve compression. + pub optimize_alpha: bool, /// Whether to attempt bit depth reduction /// /// Default: `true` @@ -295,7 +294,7 @@ impl Default for Options { preserve_attrs: false, filter: indexset! {RowFilter::None, RowFilter::Sub, RowFilter::Entropy, RowFilter::Bigrams}, interlace: None, - alphas: IndexSet::new(), + optimize_alpha: false, bit_depth_reduction: true, color_type_reduction: true, palette_reduction: true, @@ -491,13 +490,25 @@ fn optimize_png( perform_strip(png, opts); let stripped_png = png.clone(); + // If alpha optimization is enabled, first perform a black alpha reduction + // This can allow reductions from alpha to indexed which may not have been possible otherwise + if opts.optimize_alpha { + if let Some(reduced) = cleaned_alpha_channel(&png.raw) { + png.raw = Arc::new(reduced); + } + } + // Must use normal (lazy) compression, as faster ones (greedy) are not representative - // Alpha reductions can benefit from higher compression but otherwise it's not beneficial let eval_compression = 5; // None and Bigrams work well together, especially for alpha reductions let eval_filters = indexset! {RowFilter::None, RowFilter::Bigrams}; // This will collect all versions of images and pick one that compresses best - let eval = Evaluator::new(deadline.clone(), eval_filters.clone(), eval_compression); + let eval = Evaluator::new( + deadline.clone(), + eval_filters.clone(), + eval_compression, + false, + ); perform_reductions(png.raw.clone(), opts, &deadline, &eval); let (reduction_occurred, mut eval_filter) = if let Some(result) = eval.get_best_candidate() { *png = result.image; @@ -519,7 +530,7 @@ fn optimize_png( if !filters.is_empty() { debug!("Evaluating: {} filters", filters.len()); - let eval = Evaluator::new(deadline, filters, eval_compression); + let eval = Evaluator::new(deadline, filters, eval_compression, opts.optimize_alpha); if eval_filter.is_some() { eval.set_best_size(png.idat_data.len()); } @@ -585,7 +596,7 @@ fn optimize_png( if deadline.passed() { return None; } - let filtered = &png.raw.filter_image(trial.filter); + let filtered = &png.raw.filter_image(trial.filter, opts.optimize_alpha); perform_trial(filtered, opts, trial, &best_size) }); best.reduce_with(|i, j| { @@ -606,7 +617,7 @@ fn optimize_png( opts.filter, png.idat_data.len() ); - } else if eval_filter.is_some() { + } else { *png = stripped_png; } } else if png.idat_data.len() >= idat_original_size { @@ -692,7 +703,7 @@ fn perform_reductions( } if opts.palette_reduction { - if let Some(reduced) = reduced_palette(&png) { + if let Some(reduced) = reduced_palette(&png, opts.optimize_alpha) { png = Arc::new(reduced); eval.try_image(png.clone()); report_reduction(&png); @@ -726,14 +737,9 @@ fn perform_reductions( } if opts.color_type_reduction { - // Perform a black alpha reduction before color type reductions - // This can allow reductions from alpha to indexed which may not have been possible otherwise - if !opts.alphas.is_empty() { - if let Some(reduced) = filtered_alpha_channel(&png, AlphaOptim::Black) { - png = Arc::new(reduced); - } - } - if let Some(reduced) = reduce_color_type(&png, opts.grayscale_reduction) { + if let Some(reduced) = + reduce_color_type(&png, opts.grayscale_reduction, opts.optimize_alpha) + { png = Arc::new(reduced); eval.try_image(png.clone()); report_reduction(&png); @@ -744,10 +750,6 @@ fn perform_reductions( } } - if try_alpha_reductions(png, &opts.alphas, eval) { - reduction_occurred = true; - } - if let Some(baseline) = baseline { if reduction_occurred { eval.set_baseline(baseline); diff --git a/src/main.rs b/src/main.rs index 61eaed71..1b52eada 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ use clap::{AppSettings, Arg, ArgMatches, Command}; use indexmap::IndexSet; use log::{error, warn}; -use oxipng::AlphaOptim; use oxipng::Deflaters; use oxipng::Headers; use oxipng::Options; @@ -438,10 +437,7 @@ fn parse_opts_into_struct( }; if matches.is_present("alpha") { - opts.alphas.insert(AlphaOptim::Black); - opts.alphas.insert(AlphaOptim::White); - opts.alphas.insert(AlphaOptim::Up); - opts.alphas.insert(AlphaOptim::Left); + opts.optimize_alpha = true; } if matches.is_present("fast") { diff --git a/src/png/mod.rs b/src/png/mod.rs index 3ef33846..4f479431 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -314,31 +314,43 @@ impl PngImage { } /// Apply the specified filter type to all rows in the image - pub fn filter_image(&self, filter: RowFilter) -> Vec { + pub fn filter_image(&self, filter: RowFilter, optimize_alpha: bool) -> Vec { let mut filtered = Vec::with_capacity(self.data.len()); let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize; - let mut last_line: &[u8] = &[]; - let mut last_pass: Option = None; + // If alpha optimization is enabled, determine how many bytes of alpha there are per pixel + let alpha_bytes = match self.ihdr.color_type { + ColorType::RGBA | ColorType::GrayscaleAlpha if optimize_alpha => { + (self.ihdr.bit_depth.as_u8() / 8) as usize + } + _ => 0, + }; + + let mut prev_line = Vec::new(); + let mut prev_pass: Option = None; let mut f_buf = Vec::new(); for line in self.scan_lines() { - if last_pass != line.pass { - last_line = &[]; + if prev_pass != line.pass || line.data.len() != prev_line.len() { + prev_line = vec![0; line.data.len()]; } + // Alpha optimisation may alter the line data, so we need a mutable copy of it + let mut line_data = line.data.to_vec(); if filter <= RowFilter::Paeth { // Standard filters - let filter = if last_pass == line.pass || filter <= RowFilter::Sub { + let filter = if prev_pass == line.pass || filter <= RowFilter::Sub { filter } else { RowFilter::None }; - filter.filter_line(bpp, line.data, last_line, &mut f_buf); + filter.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); filtered.extend_from_slice(&f_buf); + prev_line = line_data; } else { // Heuristic filter selection strategies let mut best_line = Vec::new(); + let mut best_line_raw = Vec::new(); // Avoid vertical filtering on first line of each interlacing pass - let try_filters = if last_pass == line.pass { + let try_filters = if prev_pass == line.pass { RowFilter::STANDARD.iter() } else { RowFilter::SINGLE_LINE.iter() @@ -348,8 +360,8 @@ impl PngImage { // MSAD algorithm mentioned in libpng reference docs // http://www.libpng.org/pub/png/book/chapter09.html let mut best_size = usize::MAX; - for try_filter in try_filters { - try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + for f in try_filters { + f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); let size = f_buf.iter().fold(0, |acc, &x| { let signed = x as i8; acc + signed.unsigned_abs() as usize @@ -357,6 +369,7 @@ impl PngImage { if size < best_size { best_size = size; std::mem::swap(&mut best_line, &mut f_buf); + best_line_raw = line_data.clone(); } } } @@ -364,8 +377,8 @@ impl PngImage { // Shannon entropy algorithm, from LodePNG // https://github.com/lvandeve/lodepng let mut best_size = i32::MIN; - for try_filter in try_filters { - try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + for f in try_filters { + f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); let mut counts = vec![0; 0x100]; for &i in f_buf.iter() { counts[i as usize] += 1; @@ -379,6 +392,7 @@ impl PngImage { if size > best_size { best_size = size; std::mem::swap(&mut best_line, &mut f_buf); + best_line_raw = line_data.clone(); } } } @@ -386,8 +400,8 @@ impl PngImage { // Count distinct bigrams, from pngwolf // https://bjoern.hoehrmann.de/pngwolf/ let mut best_size = usize::MAX; - for try_filter in try_filters { - try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + for f in try_filters { + f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); let mut set = bitarr![0; 0x10000]; for pair in f_buf.windows(2) { let bigram = (pair[0] as usize) << 8 | pair[1] as usize; @@ -397,6 +411,7 @@ impl PngImage { if size < best_size { best_size = size; std::mem::swap(&mut best_line, &mut f_buf); + best_line_raw = line_data.clone(); } } } @@ -405,8 +420,8 @@ impl PngImage { let mut best_size = i32::MIN; // FxHasher is the fastest rust hasher currently available for this purpose let mut counts = FxHashMap::::default(); - for try_filter in try_filters { - try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + for f in try_filters { + f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); counts.clear(); for pair in f_buf.windows(2) { let bigram = (pair[0] as u16) << 8 | pair[1] as u16; @@ -416,6 +431,7 @@ impl PngImage { if size > best_size { best_size = size; std::mem::swap(&mut best_line, &mut f_buf); + best_line_raw = line_data.clone(); } } } @@ -431,8 +447,8 @@ impl PngImage { let capacity = compressor.zlib_compress_bound(limit); let mut dest = vec![0; capacity]; - for try_filter in try_filters { - try_filter.filter_line(bpp, line.data, last_line, &mut f_buf); + for f in try_filters { + f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes); filtered[line_start..].copy_from_slice(&f_buf); let size = compressor .zlib_compress(&filtered[filtered.len() - limit..], &mut dest) @@ -440,6 +456,7 @@ impl PngImage { if size < best_size { best_size = size; std::mem::swap(&mut best_line, &mut f_buf); + best_line_raw = line_data.clone(); } } filtered.resize(line_start, 0); @@ -447,10 +464,10 @@ impl PngImage { _ => unreachable!(), } filtered.extend_from_slice(&best_line); + prev_line = best_line_raw; } - last_line = line.data; - last_pass = line.pass; + prev_pass = line.pass; } filtered } diff --git a/src/reduction/alpha.rs b/src/reduction/alpha.rs index b3c5c9a1..a1b8e68b 100644 --- a/src/reduction/alpha.rs +++ b/src/reduction/alpha.rs @@ -1,34 +1,9 @@ -use crate::colors::AlphaOptim; use crate::colors::ColorType; -use crate::evaluate::Evaluator; use crate::headers::IhdrData; use crate::png::PngImage; -#[cfg(not(feature = "parallel"))] -use crate::rayon::prelude::*; -use indexmap::IndexSet; -#[cfg(feature = "parallel")] -use rayon::prelude::*; -use std::sync::Arc; -pub(crate) fn try_alpha_reductions( - png: Arc, - alphas: &IndexSet, - eval: &Evaluator, -) -> bool { - match png.ihdr.color_type { - ColorType::RGBA | ColorType::GrayscaleAlpha if !alphas.is_empty() => { - alphas - .par_iter() - .with_max_len(1) - .filter_map(|&alpha| filtered_alpha_channel(&png, alpha)) - .for_each(|image| eval.try_image(Arc::new(image))); - true - } - _ => false, - } -} - -pub fn filtered_alpha_channel(png: &PngImage, optim: AlphaOptim) -> Option { +/// Clean the alpha channel by setting the color of all fully transparent pixels to black +pub fn cleaned_alpha_channel(png: &PngImage) -> Option { let (bpc, bpp) = match png.ihdr.color_type { ColorType::RGBA | ColorType::GrayscaleAlpha => { let cpp = png.channels_per_pixel(); @@ -40,167 +15,25 @@ pub fn filtered_alpha_channel(png: &PngImage, optim: AlphaOptim) -> Option return None, - AlphaOptim::Black => reduced_alpha_to_black(png, bpc, bpp), - AlphaOptim::White => reduced_alpha_to_white(png, bpc, bpp), - AlphaOptim::Up => reduced_alpha_to_up(png, bpc, bpp), - AlphaOptim::Down => reduced_alpha_to_down(png, bpc, bpp), - AlphaOptim::Left => reduced_alpha_to_left(png, bpc, bpp), - AlphaOptim::Right => reduced_alpha_to_right(png, bpc, bpp), - }; - - Some(PngImage { - data: raw_data, - ihdr: png.ihdr, - palette: png.palette.clone(), - transparency_pixel: png.transparency_pixel.clone(), - aux_headers: png.aux_headers.clone(), - }) -} - -fn reduced_alpha_to_black(png: &PngImage, bpc: usize, bpp: usize) -> Vec { let mut reduced = Vec::with_capacity(png.data.len()); for line in png.scan_lines() { reduced.push(line.filter); for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { + if pixel.iter().skip(bpp - bpc).all(|b| *b == 0) { reduced.resize(reduced.len() + bpp, 0); } else { reduced.extend_from_slice(pixel); } } } - reduced -} -fn reduced_alpha_to_white(png: &PngImage, bpc: usize, bpp: usize) -> Vec { - let mut reduced = Vec::with_capacity(png.data.len()); - for line in png.scan_lines() { - reduced.push(line.filter); - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - reduced.resize(reduced.len() + bpp - bpc, 255); - reduced.resize(reduced.len() + bpc, 0); - } else { - reduced.extend_from_slice(pixel); - } - } - } - reduced -} - -fn reduced_alpha_to_up(png: &PngImage, bpc: usize, bpp: usize) -> Vec { - let mut reduced = Vec::with_capacity(png.data.len()); - let mut prev_line = Vec::new(); - let mut transparent = Vec::new(); - for line in png.scan_lines() { - if line.data.len() != prev_line.len() { - prev_line = vec![0; line.data.len()]; - transparent = vec![0; line.data.len()]; - } - reduced.push(line.filter); - let line_start = reduced.len(); - let mut line_transparent = true; - for (col, (pixel, prev_pixel)) in - line.data.chunks(bpp).zip(prev_line.chunks(bpp)).enumerate() - { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - // Copy the color values from the previous line - reduced.extend_from_slice(&prev_pixel[0..(bpp - bpc)]); - reduced.resize(reduced.len() + bpc, 0); - transparent[col] += 1; - } else { - if transparent[col] > 0 { - // Copy the current color values upwards in this column - let mut offset = line_start + col * bpp; - for _ in 0..transparent[col] { - offset -= prev_line.len() + 1; - reduced[offset..(offset + bpp - bpc)] - .copy_from_slice(&pixel[..(bpp - bpc)]); - } - } - transparent[col] = i32::MIN; // Prevent copying upwards again - reduced.extend_from_slice(pixel); - line_transparent = false; - } - } - if line_transparent { - // Zero out the line if it's fully transparent - reduced.truncate(line_start); - reduced.resize(line_start + prev_line.len(), 0); - transparent = vec![0; prev_line.len()]; - } - prev_line = reduced[line_start..].to_vec(); - } - reduced -} - -fn reduced_alpha_to_down(png: &PngImage, bpc: usize, bpp: usize) -> Vec { - let mut reduced = Vec::with_capacity(png.data.len()); - let mut prev_line = Vec::new(); - for line in png.scan_lines() { - if line.data.len() != prev_line.len() { - prev_line = vec![0; line.data.len()]; - } - reduced.push(line.filter); - let line_start = reduced.len(); - for (pixel, prev_pixel) in line.data.chunks(bpp).zip(prev_line.chunks(bpp)) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - reduced.extend_from_slice(&prev_pixel[0..(bpp - bpc)]); - reduced.resize(reduced.len() + bpc, 0); - } else { - reduced.extend_from_slice(pixel); - } - } - prev_line = reduced[line_start..].to_vec(); - } - reduced -} - -fn reduced_alpha_to_left(png: &PngImage, bpc: usize, bpp: usize) -> Vec { - let mut reduced = Vec::with_capacity(png.data.len()); - for line in png.scan_lines() { - reduced.push(line.filter); - let mut prev_pixel = vec![0; bpp]; - let mut transparent = 0; - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - // Count number of consecutive transparent pixel bytes - transparent += bpp; - } else { - prev_pixel[..(bpp - bpc)].copy_from_slice(&pixel[..(bpp - bpc)]); - if transparent > 0 { - // Copy the current color values to preceding transparent pixels - reduced.extend(prev_pixel.iter().cycle().take(transparent)); - transparent = 0; - } - reduced.extend_from_slice(pixel); - } - } - if transparent > 0 { - reduced.extend(prev_pixel.iter().cycle().take(transparent)); - } - } - reduced -} - -fn reduced_alpha_to_right(png: &PngImage, bpc: usize, bpp: usize) -> Vec { - let mut reduced = Vec::with_capacity(png.data.len()); - for line in png.scan_lines() { - reduced.push(line.filter); - let mut prev_pixel = vec![0; bpp]; - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { - reduced.extend_from_slice(&prev_pixel[0..(bpp - bpc)]); - reduced.resize(reduced.len() + bpc, 0); - } else { - prev_pixel[..(bpp - bpc)].copy_from_slice(&pixel[..(bpp - bpc)]); - reduced.extend_from_slice(pixel); - } - } - } - reduced + Some(PngImage { + data: reduced, + ihdr: png.ihdr, + palette: png.palette.clone(), + transparency_pixel: png.transparency_pixel.clone(), + aux_headers: png.aux_headers.clone(), + }) } #[must_use] diff --git a/src/reduction/mod.rs b/src/reduction/mod.rs index b775f753..9f8273fd 100644 --- a/src/reduction/mod.rs +++ b/src/reduction/mod.rs @@ -12,12 +12,12 @@ use crate::bit_depth::reduce_bit_depth_8_or_less; pub mod color; use crate::color::*; -pub(crate) use crate::alpha::{filtered_alpha_channel, try_alpha_reductions}; +pub(crate) use crate::alpha::cleaned_alpha_channel; pub(crate) use crate::bit_depth::reduce_bit_depth; /// Attempt to reduce the number of colors in the palette /// Returns `None` if palette hasn't changed -pub fn reduced_palette(png: &PngImage) -> Option { +pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option { if png.ihdr.color_type != ColorType::Indexed { // Can't reduce if there is no palette return None; @@ -90,10 +90,16 @@ pub fn reduced_palette(png: &PngImage) -> Option { continue; } // There are invalid files that use pixel indices beyond palette size - let color = palette + let mut color = palette .get(i) .cloned() .unwrap_or_else(|| RGBA8::new(0, 0, 0, 255)); + // If there are multiple fully transparent entries, reduce them into one + if optimize_alpha && color.a == 0 { + color.r = 0; + color.g = 0; + color.b = 0; + } match seen.entry(color) { Vacant(new) => { palette_map[i] = Some(next_index as u8); @@ -191,7 +197,11 @@ fn reordered_palette(palette: &[RGBA8], palette_map: &[Option; 256]) -> Vec< /// Attempt to reduce the color type of the image /// Returns true if the color type was reduced, false otherwise -pub fn reduce_color_type(png: &PngImage, grayscale_reduction: bool) -> Option { +pub fn reduce_color_type( + png: &PngImage, + grayscale_reduction: bool, + optimize_alpha: bool, +) -> Option { let mut should_reduce_bit_depth = false; let mut reduced = Cow::Borrowed(png); @@ -234,7 +244,7 @@ pub fn reduce_color_type(png: &PngImage, grayscale_reduction: bool) -> Option (OutFile, oxipng::Options) { fn test_it_converts( input: &str, - alpha: Option, + optimize_alpha: bool, color_type_in: ColorType, bit_depth_in: BitDepth, color_type_out: ColorType, @@ -30,9 +30,7 @@ fn test_it_converts( ) { let input = PathBuf::from(input); let (output, mut opts) = get_opts(&input); - if let Some(alpha) = alpha { - opts.alphas = [alpha].iter().cloned().collect(); - } + opts.optimize_alpha = optimize_alpha; let png = PngData::new(&input, opts.fix_errors).unwrap(); assert_eq!(png.raw.ihdr.color_type, color_type_in); @@ -64,7 +62,7 @@ fn test_it_converts( fn rgba_16_should_be_rgba_16() { test_it_converts( "tests/files/rgba_16_should_be_rgba_16.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::RGBA, @@ -76,7 +74,7 @@ fn rgba_16_should_be_rgba_16() { fn rgba_16_should_be_rgba_8() { test_it_converts( "tests/files/rgba_16_should_be_rgba_8.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::RGBA, @@ -88,7 +86,7 @@ fn rgba_16_should_be_rgba_8() { fn rgba_8_should_be_rgba_8() { test_it_converts( "tests/files/rgba_8_should_be_rgba_8.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::RGBA, @@ -100,7 +98,7 @@ fn rgba_8_should_be_rgba_8() { fn rgba_16_should_be_rgb_16() { test_it_converts( "tests/files/rgba_16_should_be_rgb_16.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::RGB, @@ -112,7 +110,7 @@ fn rgba_16_should_be_rgb_16() { fn rgba_16_should_be_rgb_8() { test_it_converts( "tests/files/rgba_16_should_be_rgb_8.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::RGB, @@ -124,7 +122,7 @@ fn rgba_16_should_be_rgb_8() { fn rgba_8_should_be_rgb_8() { test_it_converts( "tests/files/rgba_8_should_be_rgb_8.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::RGB, @@ -136,7 +134,7 @@ fn rgba_8_should_be_rgb_8() { fn rgba_16_should_be_palette_8() { test_it_converts( "tests/files/rgba_16_should_be_palette_8.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::Indexed, @@ -148,7 +146,7 @@ fn rgba_16_should_be_palette_8() { fn rgba_8_should_be_palette_8() { test_it_converts( "tests/files/rgba_8_should_be_palette_8.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::Indexed, @@ -160,7 +158,7 @@ fn rgba_8_should_be_palette_8() { fn rgba_16_should_be_palette_4() { test_it_converts( "tests/files/rgba_16_should_be_palette_4.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::Indexed, @@ -172,7 +170,7 @@ fn rgba_16_should_be_palette_4() { fn rgba_8_should_be_palette_4() { test_it_converts( "tests/files/rgba_8_should_be_palette_4.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::Indexed, @@ -184,7 +182,7 @@ fn rgba_8_should_be_palette_4() { fn rgba_16_should_be_palette_2() { test_it_converts( "tests/files/rgba_16_should_be_palette_2.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::Indexed, @@ -196,7 +194,7 @@ fn rgba_16_should_be_palette_2() { fn rgba_8_should_be_palette_2() { test_it_converts( "tests/files/rgba_8_should_be_palette_2.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::Indexed, @@ -208,7 +206,7 @@ fn rgba_8_should_be_palette_2() { fn rgba_16_should_be_palette_1() { test_it_converts( "tests/files/rgba_16_should_be_palette_1.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::Indexed, @@ -220,7 +218,7 @@ fn rgba_16_should_be_palette_1() { fn rgba_8_should_be_palette_1() { test_it_converts( "tests/files/rgba_8_should_be_palette_1.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::Indexed, @@ -232,7 +230,7 @@ fn rgba_8_should_be_palette_1() { fn rgba_16_should_be_grayscale_alpha_16() { test_it_converts( "tests/files/rgba_16_should_be_grayscale_alpha_16.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::GrayscaleAlpha, @@ -244,7 +242,7 @@ fn rgba_16_should_be_grayscale_alpha_16() { fn rgba_16_should_be_grayscale_alpha_8() { test_it_converts( "tests/files/rgba_16_should_be_grayscale_alpha_8.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::GrayscaleAlpha, @@ -256,7 +254,7 @@ fn rgba_16_should_be_grayscale_alpha_8() { fn rgba_8_should_be_grayscale_alpha_8() { test_it_converts( "tests/files/rgba_8_should_be_grayscale_alpha_8.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::GrayscaleAlpha, @@ -268,7 +266,7 @@ fn rgba_8_should_be_grayscale_alpha_8() { fn rgba_16_should_be_grayscale_16() { test_it_converts( "tests/files/rgba_16_should_be_grayscale_16.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::Grayscale, @@ -280,7 +278,7 @@ fn rgba_16_should_be_grayscale_16() { fn rgba_16_should_be_grayscale_8() { test_it_converts( "tests/files/rgba_16_should_be_grayscale_8.png", - None, + false, ColorType::RGBA, BitDepth::Sixteen, ColorType::Grayscale, @@ -292,7 +290,7 @@ fn rgba_16_should_be_grayscale_8() { fn rgba_8_should_be_grayscale_8() { test_it_converts( "tests/files/rgba_8_should_be_grayscale_8.png", - None, + false, ColorType::RGBA, BitDepth::Eight, ColorType::Grayscale, @@ -304,7 +302,7 @@ fn rgba_8_should_be_grayscale_8() { fn rgb_16_should_be_rgb_16() { test_it_converts( "tests/files/rgb_16_should_be_rgb_16.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::RGB, @@ -316,7 +314,7 @@ fn rgb_16_should_be_rgb_16() { fn rgb_16_should_be_rgb_8() { test_it_converts( "tests/files/rgb_16_should_be_rgb_8.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::RGB, @@ -328,7 +326,7 @@ fn rgb_16_should_be_rgb_8() { fn rgb_8_should_be_rgb_8() { test_it_converts( "tests/files/rgb_8_should_be_rgb_8.png", - None, + false, ColorType::RGB, BitDepth::Eight, ColorType::RGB, @@ -340,7 +338,7 @@ fn rgb_8_should_be_rgb_8() { fn rgb_16_should_be_palette_8() { test_it_converts( "tests/files/rgb_16_should_be_palette_8.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::Indexed, @@ -352,7 +350,7 @@ fn rgb_16_should_be_palette_8() { fn rgb_8_should_be_palette_8() { test_it_converts( "tests/files/rgb_8_should_be_palette_8.png", - None, + false, ColorType::RGB, BitDepth::Eight, ColorType::Indexed, @@ -364,7 +362,7 @@ fn rgb_8_should_be_palette_8() { fn rgb_16_should_be_palette_4() { test_it_converts( "tests/files/rgb_16_should_be_palette_4.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::Indexed, @@ -376,7 +374,7 @@ fn rgb_16_should_be_palette_4() { fn rgb_8_should_be_palette_4() { test_it_converts( "tests/files/rgb_8_should_be_palette_4.png", - None, + false, ColorType::RGB, BitDepth::Eight, ColorType::Indexed, @@ -388,7 +386,7 @@ fn rgb_8_should_be_palette_4() { fn rgb_16_should_be_palette_2() { test_it_converts( "tests/files/rgb_16_should_be_palette_2.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::Indexed, @@ -400,7 +398,7 @@ fn rgb_16_should_be_palette_2() { fn rgb_8_should_be_palette_2() { test_it_converts( "tests/files/rgb_8_should_be_palette_2.png", - None, + false, ColorType::RGB, BitDepth::Eight, ColorType::Indexed, @@ -412,7 +410,7 @@ fn rgb_8_should_be_palette_2() { fn rgb_16_should_be_palette_1() { test_it_converts( "tests/files/rgb_16_should_be_palette_1.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::Indexed, @@ -424,7 +422,7 @@ fn rgb_16_should_be_palette_1() { fn rgb_8_should_be_palette_1() { test_it_converts( "tests/files/rgb_8_should_be_palette_1.png", - None, + false, ColorType::RGB, BitDepth::Eight, ColorType::Indexed, @@ -436,7 +434,7 @@ fn rgb_8_should_be_palette_1() { fn rgb_16_should_be_grayscale_16() { test_it_converts( "tests/files/rgb_16_should_be_grayscale_16.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::Grayscale, @@ -448,7 +446,7 @@ fn rgb_16_should_be_grayscale_16() { fn rgb_16_should_be_grayscale_8() { test_it_converts( "tests/files/rgb_16_should_be_grayscale_8.png", - None, + false, ColorType::RGB, BitDepth::Sixteen, ColorType::Grayscale, @@ -460,7 +458,7 @@ fn rgb_16_should_be_grayscale_8() { fn rgb_8_should_be_grayscale_8() { test_it_converts( "tests/files/rgb_8_should_be_grayscale_8.png", - None, + false, ColorType::RGB, BitDepth::Eight, ColorType::Grayscale, @@ -472,7 +470,7 @@ fn rgb_8_should_be_grayscale_8() { fn palette_8_should_be_palette_8() { test_it_converts( "tests/files/palette_8_should_be_palette_8.png", - None, + false, ColorType::Indexed, BitDepth::Eight, ColorType::Indexed, @@ -484,7 +482,7 @@ fn palette_8_should_be_palette_8() { fn palette_8_should_be_palette_4() { test_it_converts( "tests/files/palette_8_should_be_palette_4.png", - None, + false, ColorType::Indexed, BitDepth::Eight, ColorType::Indexed, @@ -496,7 +494,7 @@ fn palette_8_should_be_palette_4() { fn palette_4_should_be_palette_4() { test_it_converts( "tests/files/palette_4_should_be_palette_4.png", - None, + false, ColorType::Indexed, BitDepth::Four, ColorType::Indexed, @@ -508,7 +506,7 @@ fn palette_4_should_be_palette_4() { fn palette_8_should_be_palette_2() { test_it_converts( "tests/files/palette_8_should_be_palette_2.png", - None, + false, ColorType::Indexed, BitDepth::Eight, ColorType::Indexed, @@ -520,7 +518,7 @@ fn palette_8_should_be_palette_2() { fn palette_4_should_be_palette_2() { test_it_converts( "tests/files/palette_4_should_be_palette_2.png", - None, + false, ColorType::Indexed, BitDepth::Four, ColorType::Indexed, @@ -532,7 +530,7 @@ fn palette_4_should_be_palette_2() { fn palette_2_should_be_palette_2() { test_it_converts( "tests/files/palette_2_should_be_palette_2.png", - None, + false, ColorType::Indexed, BitDepth::Two, ColorType::Indexed, @@ -544,7 +542,7 @@ fn palette_2_should_be_palette_2() { fn palette_8_should_be_palette_1() { test_it_converts( "tests/files/palette_8_should_be_palette_1.png", - None, + false, ColorType::Indexed, BitDepth::Eight, ColorType::Indexed, @@ -556,7 +554,7 @@ fn palette_8_should_be_palette_1() { fn palette_4_should_be_palette_1() { test_it_converts( "tests/files/palette_4_should_be_palette_1.png", - None, + false, ColorType::Indexed, BitDepth::Four, ColorType::Indexed, @@ -568,7 +566,7 @@ fn palette_4_should_be_palette_1() { fn palette_2_should_be_palette_1() { test_it_converts( "tests/files/palette_2_should_be_palette_1.png", - None, + false, ColorType::Indexed, BitDepth::Two, ColorType::Indexed, @@ -580,7 +578,7 @@ fn palette_2_should_be_palette_1() { fn palette_1_should_be_palette_1() { test_it_converts( "tests/files/palette_1_should_be_palette_1.png", - None, + false, ColorType::Indexed, BitDepth::One, ColorType::Indexed, @@ -592,7 +590,7 @@ fn palette_1_should_be_palette_1() { fn grayscale_alpha_16_should_be_grayscale_alpha_16() { test_it_converts( "tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png", - None, + false, ColorType::GrayscaleAlpha, BitDepth::Sixteen, ColorType::GrayscaleAlpha, @@ -604,7 +602,7 @@ fn grayscale_alpha_16_should_be_grayscale_alpha_16() { fn grayscale_alpha_16_should_be_grayscale_alpha_8() { test_it_converts( "tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png", - None, + false, ColorType::GrayscaleAlpha, BitDepth::Sixteen, ColorType::GrayscaleAlpha, @@ -616,7 +614,7 @@ fn grayscale_alpha_16_should_be_grayscale_alpha_8() { fn grayscale_alpha_8_should_be_grayscale_alpha_8() { test_it_converts( "tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png", - None, + false, ColorType::GrayscaleAlpha, BitDepth::Eight, ColorType::GrayscaleAlpha, @@ -628,7 +626,7 @@ fn grayscale_alpha_8_should_be_grayscale_alpha_8() { fn grayscale_alpha_16_should_be_grayscale_16() { test_it_converts( "tests/files/grayscale_alpha_16_should_be_grayscale_16.png", - None, + false, ColorType::GrayscaleAlpha, BitDepth::Sixteen, ColorType::Grayscale, @@ -640,7 +638,7 @@ fn grayscale_alpha_16_should_be_grayscale_16() { fn grayscale_alpha_16_should_be_grayscale_8() { test_it_converts( "tests/files/grayscale_alpha_16_should_be_grayscale_8.png", - None, + false, ColorType::GrayscaleAlpha, BitDepth::Sixteen, ColorType::Grayscale, @@ -652,7 +650,7 @@ fn grayscale_alpha_16_should_be_grayscale_8() { fn grayscale_alpha_8_should_be_grayscale_8() { test_it_converts( "tests/files/grayscale_alpha_8_should_be_grayscale_8.png", - None, + false, ColorType::GrayscaleAlpha, BitDepth::Eight, ColorType::Grayscale, @@ -664,7 +662,7 @@ fn grayscale_alpha_8_should_be_grayscale_8() { fn grayscale_16_should_be_grayscale_16() { test_it_converts( "tests/files/grayscale_16_should_be_grayscale_16.png", - None, + false, ColorType::Grayscale, BitDepth::Sixteen, ColorType::Grayscale, @@ -676,7 +674,7 @@ fn grayscale_16_should_be_grayscale_16() { fn grayscale_16_should_be_grayscale_8() { test_it_converts( "tests/files/grayscale_16_should_be_grayscale_8.png", - None, + false, ColorType::Grayscale, BitDepth::Sixteen, ColorType::Grayscale, @@ -688,7 +686,7 @@ fn grayscale_16_should_be_grayscale_8() { fn grayscale_8_should_be_grayscale_8() { test_it_converts( "tests/files/grayscale_8_should_be_grayscale_8.png", - None, + false, ColorType::Grayscale, BitDepth::Eight, ColorType::Grayscale, @@ -827,10 +825,10 @@ fn palette_should_be_reduced_with_both() { } #[test] -fn rgba_16_reduce_alpha_black() { +fn rgba_16_reduce_alpha() { test_it_converts( - "tests/files/rgba_16_reduce_alpha_black.png", - None, + "tests/files/rgba_16_reduce_alpha.png", + true, ColorType::RGBA, BitDepth::Sixteen, ColorType::RGBA, @@ -839,10 +837,10 @@ fn rgba_16_reduce_alpha_black() { } #[test] -fn rgba_8_reduce_alpha_black() { +fn rgba_8_reduce_alpha() { test_it_converts( - "tests/files/rgba_8_reduce_alpha_black.png", - None, + "tests/files/rgba_8_reduce_alpha.png", + true, ColorType::RGBA, BitDepth::Eight, ColorType::RGBA, @@ -851,10 +849,10 @@ fn rgba_8_reduce_alpha_black() { } #[test] -fn grayscale_alpha_16_reduce_alpha_black() { +fn grayscale_alpha_16_reduce_alpha() { test_it_converts( - "tests/files/grayscale_alpha_16_reduce_alpha_black.png", - None, + "tests/files/grayscale_alpha_16_reduce_alpha.png", + true, ColorType::GrayscaleAlpha, BitDepth::Sixteen, ColorType::GrayscaleAlpha, @@ -863,250 +861,10 @@ fn grayscale_alpha_16_reduce_alpha_black() { } #[test] -fn grayscale_alpha_8_reduce_alpha_black() { +fn grayscale_alpha_8_reduce_alpha() { test_it_converts( - "tests/files/grayscale_alpha_8_reduce_alpha_black.png", - None, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_16_reduce_alpha_white() { - test_it_converts( - "tests/files/rgba_16_reduce_alpha_white.png", - Some(AlphaOptim::White), - ColorType::RGBA, - BitDepth::Sixteen, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_8_reduce_alpha_white() { - test_it_converts( - "tests/files/rgba_8_reduce_alpha_white.png", - Some(AlphaOptim::White), - ColorType::RGBA, - BitDepth::Eight, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_16_reduce_alpha_white() { - test_it_converts( - "tests/files/grayscale_alpha_16_reduce_alpha_white.png", - Some(AlphaOptim::White), - ColorType::GrayscaleAlpha, - BitDepth::Sixteen, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_8_reduce_alpha_white() { - test_it_converts( - "tests/files/grayscale_alpha_8_reduce_alpha_white.png", - Some(AlphaOptim::White), - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_16_reduce_alpha_down() { - test_it_converts( - "tests/files/rgba_16_reduce_alpha_down.png", - Some(AlphaOptim::Down), - ColorType::RGBA, - BitDepth::Sixteen, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_8_reduce_alpha_down() { - test_it_converts( - "tests/files/rgba_8_reduce_alpha_down.png", - Some(AlphaOptim::Down), - ColorType::RGBA, - BitDepth::Eight, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_16_reduce_alpha_down() { - test_it_converts( - "tests/files/grayscale_alpha_16_reduce_alpha_down.png", - Some(AlphaOptim::Down), - ColorType::GrayscaleAlpha, - BitDepth::Sixteen, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_8_reduce_alpha_down() { - test_it_converts( - "tests/files/grayscale_alpha_8_reduce_alpha_down.png", - Some(AlphaOptim::Down), - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_16_reduce_alpha_up() { - test_it_converts( - "tests/files/rgba_16_reduce_alpha_up.png", - Some(AlphaOptim::Up), - ColorType::RGBA, - BitDepth::Sixteen, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_8_reduce_alpha_up() { - test_it_converts( - "tests/files/rgba_8_reduce_alpha_up.png", - Some(AlphaOptim::Up), - ColorType::RGBA, - BitDepth::Eight, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_16_reduce_alpha_up() { - test_it_converts( - "tests/files/grayscale_alpha_16_reduce_alpha_up.png", - Some(AlphaOptim::Up), - ColorType::GrayscaleAlpha, - BitDepth::Sixteen, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_8_reduce_alpha_up() { - test_it_converts( - "tests/files/grayscale_alpha_8_reduce_alpha_up.png", - Some(AlphaOptim::Up), - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_16_reduce_alpha_left() { - test_it_converts( - "tests/files/rgba_16_reduce_alpha_left.png", - Some(AlphaOptim::Left), - ColorType::RGBA, - BitDepth::Sixteen, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_8_reduce_alpha_left() { - test_it_converts( - "tests/files/rgba_8_reduce_alpha_left.png", - Some(AlphaOptim::Left), - ColorType::RGBA, - BitDepth::Eight, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_16_reduce_alpha_left() { - test_it_converts( - "tests/files/grayscale_alpha_16_reduce_alpha_left.png", - Some(AlphaOptim::Left), - ColorType::GrayscaleAlpha, - BitDepth::Sixteen, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_8_reduce_alpha_left() { - test_it_converts( - "tests/files/grayscale_alpha_8_reduce_alpha_left.png", - Some(AlphaOptim::Left), - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_16_reduce_alpha_right() { - test_it_converts( - "tests/files/rgba_16_reduce_alpha_right.png", - Some(AlphaOptim::Right), - ColorType::RGBA, - BitDepth::Sixteen, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn rgba_8_reduce_alpha_right() { - test_it_converts( - "tests/files/rgba_8_reduce_alpha_right.png", - Some(AlphaOptim::Right), - ColorType::RGBA, - BitDepth::Eight, - ColorType::RGBA, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_16_reduce_alpha_right() { - test_it_converts( - "tests/files/grayscale_alpha_16_reduce_alpha_right.png", - Some(AlphaOptim::Right), - ColorType::GrayscaleAlpha, - BitDepth::Sixteen, - ColorType::GrayscaleAlpha, - BitDepth::Eight, - ); -} - -#[test] -fn grayscale_alpha_8_reduce_alpha_right() { - test_it_converts( - "tests/files/grayscale_alpha_8_reduce_alpha_right.png", - Some(AlphaOptim::Right), + "tests/files/grayscale_alpha_8_reduce_alpha.png", + true, ColorType::GrayscaleAlpha, BitDepth::Eight, ColorType::GrayscaleAlpha, diff --git a/tests/regression.rs b/tests/regression.rs index 30797d3d..4bd5ed3f 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -308,103 +308,11 @@ fn issue_92_filter_5() { } #[test] -fn issue_113_white() { +fn issue_113() { let input = "tests/files/issue-113.png"; - let (_, mut opts) = get_opts(Path::new(input)); + let (output, mut opts) = get_opts(Path::new(input)); opts.interlace = Some(1); - opts.alphas = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Black); - let output = OutFile::Path(Some(Path::new(input).with_extension("-white-out.png"))); - test_it_converts( - input, - Some((output, opts)), - ColorType::RGBA, - BitDepth::Eight, - ColorType::GrayscaleAlpha, - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Black); - let output = OutFile::Path(Some(Path::new(input).with_extension("-black-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Right); - let output = OutFile::Path(Some(Path::new(input).with_extension("-right-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Left); - let output = OutFile::Path(Some(Path::new(input).with_extension("-left-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Up); - let output = OutFile::Path(Some(Path::new(input).with_extension("-up-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Down); - let output = OutFile::Path(Some(Path::new(input).with_extension("-down-out.png"))); + opts.optimize_alpha = true; test_it_converts( input, Some((output, opts)), @@ -429,97 +337,10 @@ fn issue_129() { } #[test] -fn issue_133_black() { +fn issue_133() { let input = "tests/files/issue-133.png"; - let (_, mut opts) = get_opts(Path::new(input)); - opts.alphas = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Black); - let output = OutFile::Path(Some(Path::new(input).with_extension("-black-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::White); - let output = OutFile::Path(Some(Path::new(input).with_extension("-white-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Up); - let output = OutFile::Path(Some(Path::new(input).with_extension("-up-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Down); - let output = OutFile::Path(Some(Path::new(input).with_extension("-down-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Right); - let output = OutFile::Path(Some(Path::new(input).with_extension("-right-out.png"))); - 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 = IndexSet::new(); - opts.alphas.insert(AlphaOptim::Left); - let output = OutFile::Path(Some(Path::new(input).with_extension("-left-out.png"))); + let (output, mut opts) = get_opts(Path::new(input)); + opts.optimize_alpha = true; test_it_converts( input, Some((output, opts)),