🚀 Revamp alpha optimisation (#475)

This commit is contained in:
andrews05 2022-12-11 15:14:50 +13:00 committed by GitHub
parent 54ee621e41
commit 131a3c6af7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 321 additions and 1080 deletions

View file

@ -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",
));

View file

@ -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);
});
}

View file

@ -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));
}

View file

@ -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);
});
}

View file

@ -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",
}
)
}
}

View file

@ -44,6 +44,7 @@ pub(crate) struct Evaluator {
deadline: Arc<Deadline>,
filters: IndexSet<RowFilter>,
compression: u8,
optimize_alpha: bool,
nth: AtomicUsize,
best_candidate_size: Arc<AtomicMin>,
/// images are sent to the caller thread for evaluation
@ -55,13 +56,19 @@ pub(crate) struct Evaluator {
}
impl Evaluator {
pub fn new(deadline: Arc<Deadline>, filters: IndexSet<RowFilter>, compression: u8) -> Self {
pub fn new(
deadline: Arc<Deadline>,
filters: IndexSet<RowFilter>,
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)
{

View file

@ -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<u8>) {
pub fn filter_line(
self,
bpp: usize,
data: &mut [u8],
prev_line: &[u8],
buf: &mut Vec<u8>,
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<u8>,
) -> 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))

View file

@ -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<u8>,
/// Alpha filtering strategies to use
pub alphas: IndexSet<colors::AlphaOptim>,
/// 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);

View file

@ -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") {

View file

@ -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<u8> {
pub fn filter_image(&self, filter: RowFilter, optimize_alpha: bool) -> Vec<u8> {
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<u8> = 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<u8> = 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::<u16, u32>::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
}

View file

@ -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<PngImage>,
alphas: &IndexSet<AlphaOptim>,
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<PngImage> {
/// Clean the alpha channel by setting the color of all fully transparent pixels to black
pub fn cleaned_alpha_channel(png: &PngImage) -> Option<PngImage> {
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<PngIm
}
};
let raw_data = match optim {
AlphaOptim::NoOp => 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<u8> {
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<u8> {
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<u8> {
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<u8> {
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<u8> {
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<u8> {
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]

View file

@ -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<PngImage> {
pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option<PngImage> {
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<PngImage> {
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<u8>; 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<PngImage> {
pub fn reduce_color_type(
png: &PngImage,
grayscale_reduction: bool,
optimize_alpha: bool,
) -> Option<PngImage> {
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<Pn
//Make sure that palette gets sorted. Ideally, this should be done within reduced_color_to_palette.
if should_reduce_bit_depth && reduced.ihdr.color_type == ColorType::Indexed {
if let Some(r) = reduced_palette(&reduced) {
if let Some(r) = reduced_palette(&reduced, optimize_alpha) {
reduced = Cow::Owned(r);
should_reduce_bit_depth = true;
}

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View file

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View file

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View file

@ -22,7 +22,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
alpha: Option<AlphaOptim>,
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,

View file

@ -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)),