Reduce memory usage of fast evaluation (#713)
This is an experiment I started a while ago before life happened. It
reduces memory usage of fast evaluation (-o2 and lower), bringing it
inline with normal (slow) evaluation. It does this by not retaining the
filtered image data of the evaluations, but instead retaining the row
filters that were used in each line so it can be quickly re-filtered
when required. This does incur a tiny performance penalty, but it's
negligible even at `-o0`.
Although this is fully functional, there are a few rough spots in the
code so I'm just opening it as a draft for now.
```
PR -sao6
70.08 real 614.36 user 2.66 sys
2024243200 maximum resident set size
1839339520 peak memory footprint
PR -sao2
11.16 real 60.85 user 1.33 sys
1982562304 maximum resident set size
1842173824 peak memory footprint
PR -sao2 -t1
55.11 real 53.85 user 0.76 sys
429457408 maximum resident set size
245008064 peak memory footprint
master -sao6
67.70 real 616.07 user 2.72 sys
2043379712 maximum resident set size
1838340416 peak memory footprint
master -sao2
11.53 real 60.63 user 1.25 sys
2753396736 maximum resident set size
2283741440 peak memory footprint
master -sao2 -t1
54.29 real 53.55 user 0.72 sys
626311168 maximum resident set size
305252544 peak memory footprint
```
Note that this involves some refactoring of `RowFilter` and the new
`FilterStrategy`. These are breaking changes so it will ultimately be
destined for v10. One advantage to this new structure is it opens the
door for future changes such as allowing the Brute strategy to take a
parameter for the number of lines.
This commit is contained in:
parent
ff9beb00fe
commit
71290acc62
16 changed files with 280 additions and 234 deletions
|
|
@ -13,7 +13,7 @@ fn filters_16_bits_filter_0(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::None, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::NONE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -21,7 +21,7 @@ fn filters_8_bits_filter_0(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::None, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::NONE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -31,7 +31,7 @@ fn filters_4_bits_filter_0(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::None, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::NONE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -41,7 +41,7 @@ fn filters_2_bits_filter_0(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::None, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::NONE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -51,7 +51,7 @@ fn filters_1_bits_filter_0(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::None, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::NONE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -59,7 +59,7 @@ fn filters_16_bits_filter_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, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::SUB, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -67,7 +67,7 @@ fn filters_8_bits_filter_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, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::SUB, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -77,7 +77,7 @@ fn filters_4_bits_filter_1(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::SUB, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -87,7 +87,7 @@ fn filters_2_bits_filter_1(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::SUB, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -97,7 +97,7 @@ fn filters_1_bits_filter_1(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::SUB, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -105,7 +105,7 @@ fn filters_16_bits_filter_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, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::UP, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -113,7 +113,7 @@ fn filters_8_bits_filter_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, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::UP, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -123,7 +123,7 @@ fn filters_4_bits_filter_2(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::UP, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -133,7 +133,7 @@ fn filters_2_bits_filter_2(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::UP, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -143,7 +143,7 @@ fn filters_1_bits_filter_2(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::UP, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -151,7 +151,7 @@ fn filters_16_bits_filter_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, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::AVERAGE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -159,7 +159,7 @@ fn filters_8_bits_filter_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, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::AVERAGE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -169,7 +169,7 @@ fn filters_4_bits_filter_3(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::AVERAGE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -179,7 +179,7 @@ fn filters_2_bits_filter_3(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::AVERAGE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -189,7 +189,7 @@ fn filters_1_bits_filter_3(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::AVERAGE, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -197,7 +197,7 @@ fn filters_16_bits_filter_4(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::PAETH, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -205,7 +205,7 @@ fn filters_8_bits_filter_4(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::PAETH, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -215,7 +215,7 @@ fn filters_4_bits_filter_4(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::PAETH, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -225,7 +225,7 @@ fn filters_2_bits_filter_4(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::PAETH, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -235,7 +235,7 @@ fn filters_1_bits_filter_4(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::PAETH, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -243,7 +243,7 @@ fn filters_16_bits_filter_5(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::MinSum, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -251,7 +251,7 @@ fn filters_8_bits_filter_5(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::MinSum, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -261,7 +261,7 @@ fn filters_4_bits_filter_5(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::MinSum, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -271,7 +271,7 @@ fn filters_2_bits_filter_5(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::MinSum, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -281,5 +281,5 @@ fn filters_1_bits_filter_5(b: &mut Bencher) {
|
|||
));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::MinSum, false));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ fn filters_minsum(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::MinSum, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -21,7 +21,7 @@ fn filters_entropy(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Entropy, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::Entropy, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -29,7 +29,7 @@ fn filters_bigrams(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Bigrams, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::Bigrams, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -37,7 +37,7 @@ fn filters_bigent(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::BigEnt, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::BigEnt, false));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
@ -45,5 +45,5 @@ fn filters_brute(b: &mut Bencher) {
|
|||
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
|
||||
let png = PngData::new(&input, &Options::default()).unwrap();
|
||||
|
||||
b.iter(|| png.raw.filter_image(RowFilter::Brute, false));
|
||||
b.iter(|| png.raw.filter_image(FilterStrategy::Brute, false));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,15 +17,20 @@ use rayon::prelude::*;
|
|||
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
use crate::rayon;
|
||||
use crate::{Deadline, PngError, atomicmin::AtomicMin, deflate, filters::RowFilter, png::PngImage};
|
||||
use crate::{
|
||||
Deadline, PngError, atomicmin::AtomicMin, deflate, filters::FilterStrategy, png::PngImage,
|
||||
};
|
||||
|
||||
pub(crate) struct Candidate {
|
||||
pub image: Arc<PngImage>,
|
||||
pub data: Vec<u8>,
|
||||
pub data_is_compressed: bool,
|
||||
pub idat_data: Option<Vec<u8>>,
|
||||
pub estimated_output_size: usize,
|
||||
pub filter: RowFilter,
|
||||
// For determining tie-breaker
|
||||
/// The input filter, which is retained for printing and for APNG frames.
|
||||
pub filter: FilterStrategy,
|
||||
/// The filter returned by the filter function, which may be Predefined.
|
||||
/// Use this for the next round to avoid recomputing the filter.
|
||||
pub filter_used: FilterStrategy,
|
||||
/// For determining tie-breaker
|
||||
nth: usize,
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +39,7 @@ impl Candidate {
|
|||
(
|
||||
self.estimated_output_size,
|
||||
self.image.data.len(),
|
||||
self.filter,
|
||||
self.filter.clone(),
|
||||
// Prefer the later image added (e.g. baseline, which is always added last)
|
||||
usize::MAX - self.nth,
|
||||
)
|
||||
|
|
@ -44,7 +49,7 @@ impl Candidate {
|
|||
/// Collect image versions and pick one that compresses best
|
||||
pub(crate) struct Evaluator {
|
||||
deadline: Arc<Deadline>,
|
||||
filters: IndexSet<RowFilter>,
|
||||
filters: IndexSet<FilterStrategy>,
|
||||
deflater: Deflaters,
|
||||
optimize_alpha: bool,
|
||||
final_round: bool,
|
||||
|
|
@ -62,7 +67,7 @@ pub(crate) struct Evaluator {
|
|||
impl Evaluator {
|
||||
pub fn new(
|
||||
deadline: Arc<Deadline>,
|
||||
filters: IndexSet<RowFilter>,
|
||||
filters: IndexSet<FilterStrategy>,
|
||||
deflater: Deflaters,
|
||||
optimize_alpha: bool,
|
||||
final_round: bool,
|
||||
|
|
@ -141,21 +146,21 @@ impl Evaluator {
|
|||
// which are dangerous to do in side Rayon's loop.
|
||||
// Instead, only update (atomic) best size in real time,
|
||||
// and the best result later without need for locks.
|
||||
filters_iter.for_each(|&filter| {
|
||||
filters_iter.for_each(|filter| {
|
||||
if deadline.passed() {
|
||||
return;
|
||||
}
|
||||
let filtered = image.filter_image(filter, optimize_alpha);
|
||||
let (filtered, filter_used) = image.filter_image(filter.clone(), optimize_alpha);
|
||||
let idat_data = deflater.deflate(&filtered, best_candidate_size.get());
|
||||
if let Ok(idat_data) = idat_data {
|
||||
let estimated_output_size = image.estimated_output_size(&idat_data);
|
||||
// For the final round we need the IDAT data, otherwise the filtered data
|
||||
// We only need to retain the IDAT data in the final round
|
||||
let new = Candidate {
|
||||
image: image.clone(),
|
||||
data: if final_round { idat_data } else { filtered },
|
||||
data_is_compressed: final_round,
|
||||
idat_data: if final_round { Some(idat_data) } else { None },
|
||||
estimated_output_size,
|
||||
filter,
|
||||
filter: filter.clone(),
|
||||
filter_used,
|
||||
nth,
|
||||
};
|
||||
best_candidate_size.set_min(estimated_output_size);
|
||||
|
|
|
|||
|
|
@ -1,30 +1,62 @@
|
|||
use std::{fmt, fmt::Display, mem::transmute};
|
||||
|
||||
use crate::error::PngError;
|
||||
|
||||
/// Filtering strategy for use in [`Options`][crate::Options]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
||||
pub enum FilterStrategy {
|
||||
/// Same filter for all rows
|
||||
Basic(RowFilter),
|
||||
/// Minimum sum of absolute differences
|
||||
MinSum,
|
||||
/// Shannon entropy
|
||||
Entropy,
|
||||
/// Count of distinct bigrams
|
||||
Bigrams,
|
||||
/// Shannon entropy of bigrams
|
||||
BigEnt,
|
||||
/// Deflate compression
|
||||
Brute,
|
||||
/// Predefined filter for each row
|
||||
Predefined(Vec<RowFilter>),
|
||||
}
|
||||
|
||||
impl FilterStrategy {
|
||||
pub const NONE: Self = Self::Basic(RowFilter::None);
|
||||
pub const SUB: Self = Self::Basic(RowFilter::Sub);
|
||||
pub const UP: Self = Self::Basic(RowFilter::Up);
|
||||
pub const AVERAGE: Self = Self::Basic(RowFilter::Average);
|
||||
pub const PAETH: Self = Self::Basic(RowFilter::Paeth);
|
||||
}
|
||||
|
||||
impl Display for FilterStrategy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Basic(filter) => filter.fmt(f),
|
||||
Self::MinSum => "MinSum".fmt(f),
|
||||
Self::Entropy => "Entropy".fmt(f),
|
||||
Self::Bigrams => "Bigrams".fmt(f),
|
||||
Self::BigEnt => "BigEnt".fmt(f),
|
||||
Self::Brute => "Brute".fmt(f),
|
||||
Self::Predefined(_) => "Predefined".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PNG delta filters
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
|
||||
pub enum RowFilter {
|
||||
// Standard filter types
|
||||
None,
|
||||
Sub,
|
||||
Up,
|
||||
Average,
|
||||
Paeth,
|
||||
// Heuristic strategies
|
||||
MinSum,
|
||||
Entropy,
|
||||
Bigrams,
|
||||
BigEnt,
|
||||
Brute,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for RowFilter {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
if value > Self::LAST {
|
||||
if value > 4 {
|
||||
return Err(());
|
||||
}
|
||||
unsafe { transmute(value as i8) }
|
||||
|
|
@ -40,11 +72,6 @@ impl Display for RowFilter {
|
|||
Self::Up => "Up",
|
||||
Self::Average => "Average",
|
||||
Self::Paeth => "Paeth",
|
||||
Self::MinSum => "MinSum",
|
||||
Self::Entropy => "Entropy",
|
||||
Self::Bigrams => "Bigrams",
|
||||
Self::BigEnt => "BigEnt",
|
||||
Self::Brute => "Brute",
|
||||
},
|
||||
f,
|
||||
)
|
||||
|
|
@ -52,9 +79,7 @@ impl Display for RowFilter {
|
|||
}
|
||||
|
||||
impl RowFilter {
|
||||
pub const LAST: u8 = Self::Brute as u8;
|
||||
pub(crate) const STANDARD: [Self; 5] =
|
||||
[Self::None, Self::Sub, Self::Up, Self::Average, Self::Paeth];
|
||||
pub(crate) const ALL: [Self; 5] = [Self::None, Self::Sub, Self::Up, Self::Average, Self::Paeth];
|
||||
pub(crate) const SINGLE_LINE: [Self; 2] = [Self::None, Self::Sub];
|
||||
|
||||
pub(crate) fn filter_line(
|
||||
|
|
@ -115,7 +140,6 @@ impl RowFilter {
|
|||
});
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +164,7 @@ impl RowFilter {
|
|||
_ => i - 1,
|
||||
};
|
||||
match self {
|
||||
Self::None => unreachable!(),
|
||||
Self::Sub => {
|
||||
for j in 0..color_bytes {
|
||||
pixels[i][j] = pixels[prev][j];
|
||||
|
|
@ -171,7 +196,6 @@ impl RowFilter {
|
|||
};
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -183,7 +207,7 @@ impl RowFilter {
|
|||
data: &[u8],
|
||||
prev_line: &[u8],
|
||||
buf: &mut Vec<u8>,
|
||||
) -> Result<(), PngError> {
|
||||
) {
|
||||
buf.clear();
|
||||
buf.reserve(data.len());
|
||||
assert!(data.len() >= bpp);
|
||||
|
|
@ -232,9 +256,7 @@ impl RowFilter {
|
|||
);
|
||||
}
|
||||
}
|
||||
_ => return Err(PngError::InvalidData),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
32
src/lib.rs
32
src/lib.rs
|
|
@ -45,7 +45,7 @@ pub use crate::{
|
|||
colors::{BitDepth, ColorType},
|
||||
deflate::Deflaters,
|
||||
error::PngError,
|
||||
filters::RowFilter,
|
||||
filters::{FilterStrategy, RowFilter},
|
||||
headers::StripChunks,
|
||||
interlace::Interlacing,
|
||||
options::{InFile, Options, OutFile},
|
||||
|
|
@ -170,7 +170,7 @@ impl RawImage {
|
|||
|
||||
let mut png = PngData {
|
||||
raw: result.image,
|
||||
idat_data: result.data,
|
||||
idat_data: result.idat_data.unwrap(),
|
||||
aux_chunks,
|
||||
frames: Vec::new(),
|
||||
};
|
||||
|
|
@ -359,7 +359,7 @@ fn optimize_png(
|
|||
};
|
||||
if let Some(result) = optimize_raw(raw.clone(), &opts, deadline.clone(), max_size) {
|
||||
png.raw = result.image;
|
||||
png.idat_data = result.data;
|
||||
png.idat_data = result.idat_data.unwrap();
|
||||
recompress_frames(png, &opts, deadline, result.filter)?;
|
||||
postprocess_chunks(&mut png.aux_chunks, &png.raw.ihdr, &raw.ihdr);
|
||||
}
|
||||
|
|
@ -432,7 +432,7 @@ fn optimize_raw(
|
|||
opts.filter.clone()
|
||||
} else {
|
||||
// None and Bigrams work well together, especially for alpha reductions
|
||||
indexset! {RowFilter::None, RowFilter::Bigrams}
|
||||
indexset! {FilterStrategy::NONE, FilterStrategy::Bigrams}
|
||||
};
|
||||
// This will collect all versions of images and pick one that compresses best
|
||||
let eval = Evaluator::new(
|
||||
|
|
@ -472,7 +472,7 @@ fn optimize_raw(
|
|||
(eval_result?, eval_deflater)
|
||||
};
|
||||
|
||||
if result.data_is_compressed
|
||||
if result.idat_data.is_some()
|
||||
&& max_size.is_none_or(|max_size| result.estimated_output_size < max_size)
|
||||
{
|
||||
debug!("Found better result:");
|
||||
|
|
@ -489,7 +489,7 @@ fn perform_trials(
|
|||
deadline: Arc<Deadline>,
|
||||
max_size: Option<usize>,
|
||||
mut eval_result: Option<Candidate>,
|
||||
eval_filters: IndexSet<RowFilter>,
|
||||
eval_filters: IndexSet<FilterStrategy>,
|
||||
eval_deflater: Deflaters,
|
||||
) -> Option<Candidate> {
|
||||
let mut filters = opts.filter.clone();
|
||||
|
|
@ -499,7 +499,7 @@ fn perform_trials(
|
|||
|
||||
if eval_result.is_some() {
|
||||
// Some filters have already been evaluated, we don't need to try them again
|
||||
filters = filters.difference(&eval_filters).copied().collect();
|
||||
filters = filters.difference(&eval_filters).cloned().collect();
|
||||
}
|
||||
|
||||
if !filters.is_empty() {
|
||||
|
|
@ -523,14 +523,14 @@ fn perform_trials(
|
|||
// We should have a result here - fail if not (e.g. deadline passed)
|
||||
let mut result = eval_result?;
|
||||
|
||||
if !result.data_is_compressed {
|
||||
if result.idat_data.is_none() {
|
||||
// Compress with the main deflater
|
||||
debug!("Trying filter {} with {}", result.filter, opts.deflate);
|
||||
match opts.deflate.deflate(&result.data, max_size) {
|
||||
let (data, _) = image.filter_image(result.filter_used.clone(), opts.optimize_alpha);
|
||||
match opts.deflate.deflate(&data, max_size) {
|
||||
Ok(idat_data) => {
|
||||
result.estimated_output_size = result.image.estimated_output_size(&idat_data);
|
||||
result.data = idat_data;
|
||||
result.data_is_compressed = true;
|
||||
result.idat_data = Some(idat_data);
|
||||
trace!("{} bytes", result.estimated_output_size);
|
||||
}
|
||||
Err(PngError::DeflatedDataTooLong(bytes)) => {
|
||||
|
|
@ -548,10 +548,10 @@ fn perform_trials(
|
|||
// Pick a filter automatically
|
||||
if image.ihdr.bit_depth as u8 >= 8 {
|
||||
// Bigrams is the best all-rounder when there's at least one byte per pixel
|
||||
filters.insert(RowFilter::Bigrams);
|
||||
filters.insert(FilterStrategy::Bigrams);
|
||||
} else {
|
||||
// Otherwise delta filters generally don't work well, so just stick with None
|
||||
filters.insert(RowFilter::None);
|
||||
filters.insert(FilterStrategy::NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -627,11 +627,13 @@ fn recompress_frames(
|
|||
png: &mut PngData,
|
||||
opts: &Options,
|
||||
deadline: Arc<Deadline>,
|
||||
filter: RowFilter,
|
||||
filter: FilterStrategy,
|
||||
) -> PngResult<()> {
|
||||
if !opts.idat_recoding || png.frames.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
// Ensure we don't try to recompress frames with a predefined filter
|
||||
debug_assert!(!matches!(filter, FilterStrategy::Predefined { .. }));
|
||||
png.frames
|
||||
.par_iter_mut()
|
||||
.with_max_len(1)
|
||||
|
|
@ -644,7 +646,7 @@ fn recompress_frames(
|
|||
ihdr.width = frame.width;
|
||||
ihdr.height = frame.height;
|
||||
let image = PngImage::new(ihdr, &frame.data)?;
|
||||
let filtered = image.filter_image(filter, opts.optimize_alpha);
|
||||
let (filtered, _) = image.filter_image(filter.clone(), opts.optimize_alpha);
|
||||
let max_size = Some(frame.data.len() - 1);
|
||||
if let Ok(data) = opts.deflate.deflate(&filtered, max_size) {
|
||||
debug!(
|
||||
|
|
|
|||
21
src/main.rs
21
src/main.rs
|
|
@ -26,7 +26,7 @@ use clap::ArgMatches;
|
|||
mod cli;
|
||||
use indexmap::IndexSet;
|
||||
use log::{Level, LevelFilter, error, warn};
|
||||
use oxipng::{Deflaters, InFile, Options, OutFile, PngError, RowFilter, StripChunks};
|
||||
use oxipng::{Deflaters, FilterStrategy, InFile, Options, OutFile, PngError, StripChunks};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::cli::DISPLAY_CHUNKS;
|
||||
|
|
@ -36,8 +36,7 @@ fn main() -> ExitCode {
|
|||
// Set the value parser for filters which isn't appropriate to do in the build_command function
|
||||
.mut_arg("filters", |arg| {
|
||||
arg.value_parser(|x: &str| {
|
||||
parse_numeric_range_opts(x, 0, RowFilter::LAST)
|
||||
.map_err(|_| "Invalid option for filters")
|
||||
parse_numeric_range_opts(x, 0, 9).map_err(|_| "Invalid option for filters")
|
||||
})
|
||||
})
|
||||
.after_help("Run `oxipng --help` to see full details of all options")
|
||||
|
|
@ -199,10 +198,18 @@ fn parse_opts_into_struct(
|
|||
};
|
||||
|
||||
if let Some(x) = matches.get_one::<IndexSet<u8>>("filters") {
|
||||
opts.filter.clear();
|
||||
for &f in x {
|
||||
opts.filter.insert(f.try_into().unwrap());
|
||||
}
|
||||
opts.filter = x
|
||||
.iter()
|
||||
.map(|&f| match f {
|
||||
0..=4 => FilterStrategy::Basic(f.try_into().unwrap()),
|
||||
5 => FilterStrategy::MinSum,
|
||||
6 => FilterStrategy::Entropy,
|
||||
7 => FilterStrategy::Bigrams,
|
||||
8 => FilterStrategy::BigEnt,
|
||||
9 => FilterStrategy::Brute,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(&num) = matches.get_one::<u64>("timeout") {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ use std::{
|
|||
use indexmap::{IndexSet, indexset};
|
||||
use log::warn;
|
||||
|
||||
use crate::{deflate::Deflaters, filters::RowFilter, headers::StripChunks, interlace::Interlacing};
|
||||
use crate::{
|
||||
deflate::Deflaters, filters::FilterStrategy, headers::StripChunks, interlace::Interlacing,
|
||||
};
|
||||
|
||||
/// Write destination for [`optimize`][crate::optimize].
|
||||
/// You can use [`optimize_from_memory`](crate::optimize_from_memory) to avoid external I/O.
|
||||
|
|
@ -94,10 +96,10 @@ pub struct Options {
|
|||
///
|
||||
/// Default: `false`
|
||||
pub force: bool,
|
||||
/// Which `RowFilters` to try on the file
|
||||
/// Which `FilterStrategy` to try on the file
|
||||
///
|
||||
/// Default: `None,Sub,Entropy,Bigrams`
|
||||
pub filter: IndexSet<RowFilter>,
|
||||
pub filter: IndexSet<FilterStrategy>,
|
||||
/// Whether to change the interlacing type of the file.
|
||||
///
|
||||
/// These are the interlacing types avaliable:
|
||||
|
|
@ -207,10 +209,10 @@ impl Options {
|
|||
fn apply_preset_3(mut self) -> Self {
|
||||
self.fast_evaluation = false;
|
||||
self.filter = indexset! {
|
||||
RowFilter::None,
|
||||
RowFilter::Bigrams,
|
||||
RowFilter::BigEnt,
|
||||
RowFilter::Brute
|
||||
FilterStrategy::NONE,
|
||||
FilterStrategy::Bigrams,
|
||||
FilterStrategy::BigEnt,
|
||||
FilterStrategy::Brute
|
||||
};
|
||||
self
|
||||
}
|
||||
|
|
@ -224,10 +226,10 @@ impl Options {
|
|||
|
||||
fn apply_preset_5(mut self) -> Self {
|
||||
self.fast_evaluation = false;
|
||||
self.filter.insert(RowFilter::Up);
|
||||
self.filter.insert(RowFilter::MinSum);
|
||||
self.filter.insert(RowFilter::BigEnt);
|
||||
self.filter.insert(RowFilter::Brute);
|
||||
self.filter.insert(FilterStrategy::UP);
|
||||
self.filter.insert(FilterStrategy::MinSum);
|
||||
self.filter.insert(FilterStrategy::BigEnt);
|
||||
self.filter.insert(FilterStrategy::Brute);
|
||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||
*compression = 12;
|
||||
}
|
||||
|
|
@ -235,8 +237,8 @@ impl Options {
|
|||
}
|
||||
|
||||
fn apply_preset_6(mut self) -> Self {
|
||||
self.filter.insert(RowFilter::Average);
|
||||
self.filter.insert(RowFilter::Paeth);
|
||||
self.filter.insert(FilterStrategy::AVERAGE);
|
||||
self.filter.insert(FilterStrategy::PAETH);
|
||||
self.apply_preset_5()
|
||||
}
|
||||
}
|
||||
|
|
@ -247,7 +249,12 @@ impl Default for Options {
|
|||
Self {
|
||||
fix_errors: false,
|
||||
force: false,
|
||||
filter: indexset! {RowFilter::None, RowFilter::Sub, RowFilter::Entropy, RowFilter::Bigrams},
|
||||
filter: indexset! {
|
||||
FilterStrategy::NONE,
|
||||
FilterStrategy::SUB,
|
||||
FilterStrategy::Entropy,
|
||||
FilterStrategy::Bigrams
|
||||
},
|
||||
interlace: Some(Interlacing::None),
|
||||
optimize_alpha: false,
|
||||
bit_depth_reduction: true,
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ impl PngImage {
|
|||
}
|
||||
last_line.resize(line.data.len(), 0);
|
||||
let filter = RowFilter::try_from(line.filter).map_err(|_| PngError::InvalidData)?;
|
||||
filter.unfilter_line(bpp, line.data, &last_line, &mut unfiltered_buf)?;
|
||||
filter.unfilter_line(bpp, line.data, &last_line, &mut unfiltered_buf);
|
||||
unfiltered.extend_from_slice(&unfiltered_buf);
|
||||
std::mem::swap(&mut last_line, &mut unfiltered_buf);
|
||||
unfiltered_buf.clear();
|
||||
|
|
@ -386,7 +386,11 @@ impl PngImage {
|
|||
|
||||
/// Apply the specified filter type to all rows in the image
|
||||
#[must_use]
|
||||
pub fn filter_image(&self, filter: RowFilter, optimize_alpha: bool) -> Vec<u8> {
|
||||
pub fn filter_image(
|
||||
&self,
|
||||
strategy: FilterStrategy,
|
||||
optimize_alpha: bool,
|
||||
) -> (Vec<u8>, FilterStrategy) {
|
||||
let mut filtered = Vec::with_capacity(self.data.len());
|
||||
let bpp = self.bytes_per_channel() * self.channels_per_pixel();
|
||||
// If alpha optimization is enabled, determine how many bytes of alpha there are per pixel
|
||||
|
|
@ -399,31 +403,39 @@ impl PngImage {
|
|||
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(false) {
|
||||
// For heuristic strategies, keep track of the actual filter used for each line
|
||||
let mut filters_used = Vec::new();
|
||||
for (i, line) in self.scan_lines(false).enumerate() {
|
||||
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 {
|
||||
if let FilterStrategy::Basic(mut filter) = strategy {
|
||||
// Standard filters
|
||||
let filter = if prev_pass == line.pass || filter <= RowFilter::Sub {
|
||||
filter
|
||||
} else {
|
||||
RowFilter::None
|
||||
};
|
||||
if prev_pass != line.pass && filter > RowFilter::Sub {
|
||||
filter = RowFilter::None;
|
||||
}
|
||||
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 if let FilterStrategy::Predefined(lines) = &strategy {
|
||||
// Predefined filter for each line
|
||||
let filter = lines.get(i).unwrap_or(&RowFilter::None);
|
||||
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_filter = RowFilter::None;
|
||||
if line_data.iter().all(|&x| x == 0) {
|
||||
// Assume None if the line is all zeros
|
||||
filtered.push(RowFilter::None as u8);
|
||||
filtered.push(best_filter as u8);
|
||||
filtered.extend_from_slice(&line_data);
|
||||
prev_line = line_data;
|
||||
filters_used.push(best_filter);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -431,12 +443,12 @@ impl PngImage {
|
|||
let mut best_line_raw = Vec::new();
|
||||
// Avoid vertical filtering on first line of each interlacing pass
|
||||
let try_filters = if prev_pass == line.pass {
|
||||
RowFilter::STANDARD.iter()
|
||||
RowFilter::ALL.iter()
|
||||
} else {
|
||||
RowFilter::SINGLE_LINE.iter()
|
||||
};
|
||||
match filter {
|
||||
RowFilter::MinSum => {
|
||||
match strategy {
|
||||
FilterStrategy::MinSum => {
|
||||
// MSAD algorithm mentioned in libpng reference docs
|
||||
// http://www.libpng.org/pub/png/book/chapter09.html
|
||||
let mut best_size = usize::MAX;
|
||||
|
|
@ -450,10 +462,11 @@ impl PngImage {
|
|||
best_size = size;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
best_line_raw.clone_from(&line_data);
|
||||
best_filter = *f;
|
||||
}
|
||||
}
|
||||
}
|
||||
RowFilter::Entropy => {
|
||||
FilterStrategy::Entropy => {
|
||||
// Shannon entropy algorithm, from LodePNG
|
||||
// https://github.com/lvandeve/lodepng
|
||||
let mut best_size = i32::MIN;
|
||||
|
|
@ -473,10 +486,11 @@ impl PngImage {
|
|||
best_size = size;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
best_line_raw.clone_from(&line_data);
|
||||
best_filter = *f;
|
||||
}
|
||||
}
|
||||
}
|
||||
RowFilter::Bigrams => {
|
||||
FilterStrategy::Bigrams => {
|
||||
// Count distinct bigrams, from pngwolf
|
||||
// https://bjoern.hoehrmann.de/pngwolf/
|
||||
let mut best_size = usize::MAX;
|
||||
|
|
@ -492,10 +506,11 @@ impl PngImage {
|
|||
best_size = size;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
best_line_raw.clone_from(&line_data);
|
||||
best_filter = *f;
|
||||
}
|
||||
}
|
||||
}
|
||||
RowFilter::BigEnt => {
|
||||
FilterStrategy::BigEnt => {
|
||||
// Bigram entropy, combined from Entropy and Bigrams filters
|
||||
let mut best_size = i32::MIN;
|
||||
// FxHasher is the fastest rust hasher currently available for this purpose
|
||||
|
|
@ -512,10 +527,11 @@ impl PngImage {
|
|||
best_size = size;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
best_line_raw.clone_from(&line_data);
|
||||
best_filter = *f;
|
||||
}
|
||||
}
|
||||
}
|
||||
RowFilter::Brute => {
|
||||
FilterStrategy::Brute => {
|
||||
// Brute force by compressing each filter attempt
|
||||
// Similar to that of LodePNG but includes some previous lines for context
|
||||
let mut best_size = usize::MAX;
|
||||
|
|
@ -537,6 +553,7 @@ impl PngImage {
|
|||
best_size = size;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
best_line_raw.clone_from(&line_data);
|
||||
best_filter = *f;
|
||||
}
|
||||
}
|
||||
filtered.resize(line_start, 0);
|
||||
|
|
@ -545,11 +562,17 @@ impl PngImage {
|
|||
}
|
||||
filtered.extend_from_slice(&best_line);
|
||||
prev_line = best_line_raw;
|
||||
filters_used.push(best_filter);
|
||||
}
|
||||
|
||||
prev_pass = line.pass;
|
||||
}
|
||||
filtered
|
||||
|
||||
if filters_used.is_empty() {
|
||||
(filtered, strategy)
|
||||
} else {
|
||||
(filtered, FilterStrategy::Predefined(filters_used))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
141
tests/filters.rs
141
tests/filters.rs
|
|
@ -12,20 +12,17 @@ const GRAYSCALE_ALPHA: u8 = 4;
|
|||
const RGBA: u8 = 6;
|
||||
|
||||
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
||||
let mut options = oxipng::Options {
|
||||
let options = oxipng::Options {
|
||||
force: true,
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
}
|
||||
|
||||
fn test_it_converts(
|
||||
input: &str,
|
||||
filter: RowFilter,
|
||||
filter: FilterStrategy,
|
||||
color_type_in: u8,
|
||||
bit_depth_in: BitDepth,
|
||||
color_type_out: u8,
|
||||
|
|
@ -68,7 +65,7 @@ fn test_it_converts(
|
|||
fn filter_0_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgba_16.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
RGBA,
|
||||
BitDepth::Sixteen,
|
||||
RGBA,
|
||||
|
|
@ -80,7 +77,7 @@ fn filter_0_for_rgba_16() {
|
|||
fn filter_1_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgba_16.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
RGBA,
|
||||
BitDepth::Sixteen,
|
||||
RGBA,
|
||||
|
|
@ -92,7 +89,7 @@ fn filter_1_for_rgba_16() {
|
|||
fn filter_2_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgba_16.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
RGBA,
|
||||
BitDepth::Sixteen,
|
||||
RGBA,
|
||||
|
|
@ -104,7 +101,7 @@ fn filter_2_for_rgba_16() {
|
|||
fn filter_3_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgba_16.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
RGBA,
|
||||
BitDepth::Sixteen,
|
||||
RGBA,
|
||||
|
|
@ -116,7 +113,7 @@ fn filter_3_for_rgba_16() {
|
|||
fn filter_4_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgba_16.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
RGBA,
|
||||
BitDepth::Sixteen,
|
||||
RGBA,
|
||||
|
|
@ -128,7 +125,7 @@ fn filter_4_for_rgba_16() {
|
|||
fn filter_5_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgba_16.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
RGBA,
|
||||
BitDepth::Sixteen,
|
||||
RGBA,
|
||||
|
|
@ -140,7 +137,7 @@ fn filter_5_for_rgba_16() {
|
|||
fn filter_0_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgba_8.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -152,7 +149,7 @@ fn filter_0_for_rgba_8() {
|
|||
fn filter_1_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgba_8.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -164,7 +161,7 @@ fn filter_1_for_rgba_8() {
|
|||
fn filter_2_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgba_8.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -176,7 +173,7 @@ fn filter_2_for_rgba_8() {
|
|||
fn filter_3_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgba_8.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -188,7 +185,7 @@ fn filter_3_for_rgba_8() {
|
|||
fn filter_4_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgba_8.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -200,7 +197,7 @@ fn filter_4_for_rgba_8() {
|
|||
fn filter_5_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgba_8.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -212,7 +209,7 @@ fn filter_5_for_rgba_8() {
|
|||
fn filter_0_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgb_16.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -224,7 +221,7 @@ fn filter_0_for_rgb_16() {
|
|||
fn filter_1_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgb_16.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -236,7 +233,7 @@ fn filter_1_for_rgb_16() {
|
|||
fn filter_2_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgb_16.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -248,7 +245,7 @@ fn filter_2_for_rgb_16() {
|
|||
fn filter_3_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgb_16.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -260,7 +257,7 @@ fn filter_3_for_rgb_16() {
|
|||
fn filter_4_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgb_16.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -272,7 +269,7 @@ fn filter_4_for_rgb_16() {
|
|||
fn filter_5_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgb_16.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -284,7 +281,7 @@ fn filter_5_for_rgb_16() {
|
|||
fn filter_0_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgb_8.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -296,7 +293,7 @@ fn filter_0_for_rgb_8() {
|
|||
fn filter_1_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgb_8.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -308,7 +305,7 @@ fn filter_1_for_rgb_8() {
|
|||
fn filter_2_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgb_8.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -320,7 +317,7 @@ fn filter_2_for_rgb_8() {
|
|||
fn filter_3_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgb_8.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -332,7 +329,7 @@ fn filter_3_for_rgb_8() {
|
|||
fn filter_4_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgb_8.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -344,7 +341,7 @@ fn filter_4_for_rgb_8() {
|
|||
fn filter_5_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgb_8.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -356,7 +353,7 @@ fn filter_5_for_rgb_8() {
|
|||
fn filter_0_for_grayscale_alpha_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_grayscale_alpha_16.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -368,7 +365,7 @@ fn filter_0_for_grayscale_alpha_16() {
|
|||
fn filter_1_for_grayscale_alpha_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_grayscale_alpha_16.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -380,7 +377,7 @@ fn filter_1_for_grayscale_alpha_16() {
|
|||
fn filter_2_for_grayscale_alpha_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_grayscale_alpha_16.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -392,7 +389,7 @@ fn filter_2_for_grayscale_alpha_16() {
|
|||
fn filter_3_for_grayscale_alpha_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_grayscale_alpha_16.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -404,7 +401,7 @@ fn filter_3_for_grayscale_alpha_16() {
|
|||
fn filter_4_for_grayscale_alpha_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_grayscale_alpha_16.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -416,7 +413,7 @@ fn filter_4_for_grayscale_alpha_16() {
|
|||
fn filter_5_for_grayscale_alpha_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_grayscale_alpha_16.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -428,7 +425,7 @@ fn filter_5_for_grayscale_alpha_16() {
|
|||
fn filter_0_for_grayscale_alpha_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_grayscale_alpha_8.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -440,7 +437,7 @@ fn filter_0_for_grayscale_alpha_8() {
|
|||
fn filter_1_for_grayscale_alpha_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_grayscale_alpha_8.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -452,7 +449,7 @@ fn filter_1_for_grayscale_alpha_8() {
|
|||
fn filter_2_for_grayscale_alpha_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_grayscale_alpha_8.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -464,7 +461,7 @@ fn filter_2_for_grayscale_alpha_8() {
|
|||
fn filter_3_for_grayscale_alpha_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_grayscale_alpha_8.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -476,7 +473,7 @@ fn filter_3_for_grayscale_alpha_8() {
|
|||
fn filter_4_for_grayscale_alpha_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_grayscale_alpha_8.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -488,7 +485,7 @@ fn filter_4_for_grayscale_alpha_8() {
|
|||
fn filter_5_for_grayscale_alpha_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_grayscale_alpha_8.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
GRAYSCALE_ALPHA,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE_ALPHA,
|
||||
|
|
@ -500,7 +497,7 @@ fn filter_5_for_grayscale_alpha_8() {
|
|||
fn filter_0_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_grayscale_16.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
GRAYSCALE,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE,
|
||||
|
|
@ -512,7 +509,7 @@ fn filter_0_for_grayscale_16() {
|
|||
fn filter_1_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_grayscale_16.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
GRAYSCALE,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE,
|
||||
|
|
@ -524,7 +521,7 @@ fn filter_1_for_grayscale_16() {
|
|||
fn filter_2_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_grayscale_16.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
GRAYSCALE,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE,
|
||||
|
|
@ -536,7 +533,7 @@ fn filter_2_for_grayscale_16() {
|
|||
fn filter_3_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_grayscale_16.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
GRAYSCALE,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE,
|
||||
|
|
@ -548,7 +545,7 @@ fn filter_3_for_grayscale_16() {
|
|||
fn filter_4_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_grayscale_16.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
GRAYSCALE,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE,
|
||||
|
|
@ -560,7 +557,7 @@ fn filter_4_for_grayscale_16() {
|
|||
fn filter_5_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_grayscale_16.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
GRAYSCALE,
|
||||
BitDepth::Sixteen,
|
||||
GRAYSCALE,
|
||||
|
|
@ -572,7 +569,7 @@ fn filter_5_for_grayscale_16() {
|
|||
fn filter_0_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_grayscale_8.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -584,7 +581,7 @@ fn filter_0_for_grayscale_8() {
|
|||
fn filter_1_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_grayscale_8.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -596,7 +593,7 @@ fn filter_1_for_grayscale_8() {
|
|||
fn filter_2_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_grayscale_8.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -608,7 +605,7 @@ fn filter_2_for_grayscale_8() {
|
|||
fn filter_3_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_grayscale_8.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -620,7 +617,7 @@ fn filter_3_for_grayscale_8() {
|
|||
fn filter_4_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_grayscale_8.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -632,7 +629,7 @@ fn filter_4_for_grayscale_8() {
|
|||
fn filter_5_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_grayscale_8.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -644,7 +641,7 @@ fn filter_5_for_grayscale_8() {
|
|||
fn filter_0_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_palette_4.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
INDEXED,
|
||||
BitDepth::Four,
|
||||
INDEXED,
|
||||
|
|
@ -656,7 +653,7 @@ fn filter_0_for_palette_4() {
|
|||
fn filter_1_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_palette_4.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
INDEXED,
|
||||
BitDepth::Four,
|
||||
INDEXED,
|
||||
|
|
@ -668,7 +665,7 @@ fn filter_1_for_palette_4() {
|
|||
fn filter_2_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_palette_4.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
INDEXED,
|
||||
BitDepth::Four,
|
||||
INDEXED,
|
||||
|
|
@ -680,7 +677,7 @@ fn filter_2_for_palette_4() {
|
|||
fn filter_3_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_palette_4.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
INDEXED,
|
||||
BitDepth::Four,
|
||||
INDEXED,
|
||||
|
|
@ -692,7 +689,7 @@ fn filter_3_for_palette_4() {
|
|||
fn filter_4_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_palette_4.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
INDEXED,
|
||||
BitDepth::Four,
|
||||
INDEXED,
|
||||
|
|
@ -704,7 +701,7 @@ fn filter_4_for_palette_4() {
|
|||
fn filter_5_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_palette_4.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
INDEXED,
|
||||
BitDepth::Four,
|
||||
INDEXED,
|
||||
|
|
@ -716,7 +713,7 @@ fn filter_5_for_palette_4() {
|
|||
fn filter_0_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_palette_2.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
INDEXED,
|
||||
BitDepth::Two,
|
||||
INDEXED,
|
||||
|
|
@ -728,7 +725,7 @@ fn filter_0_for_palette_2() {
|
|||
fn filter_1_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_palette_2.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
INDEXED,
|
||||
BitDepth::Two,
|
||||
INDEXED,
|
||||
|
|
@ -740,7 +737,7 @@ fn filter_1_for_palette_2() {
|
|||
fn filter_2_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_palette_2.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
INDEXED,
|
||||
BitDepth::Two,
|
||||
INDEXED,
|
||||
|
|
@ -752,7 +749,7 @@ fn filter_2_for_palette_2() {
|
|||
fn filter_3_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_palette_2.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
INDEXED,
|
||||
BitDepth::Two,
|
||||
INDEXED,
|
||||
|
|
@ -764,7 +761,7 @@ fn filter_3_for_palette_2() {
|
|||
fn filter_4_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_palette_2.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
INDEXED,
|
||||
BitDepth::Two,
|
||||
INDEXED,
|
||||
|
|
@ -776,7 +773,7 @@ fn filter_4_for_palette_2() {
|
|||
fn filter_5_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_palette_2.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
INDEXED,
|
||||
BitDepth::Two,
|
||||
INDEXED,
|
||||
|
|
@ -788,7 +785,7 @@ fn filter_5_for_palette_2() {
|
|||
fn filter_0_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_palette_1.png",
|
||||
RowFilter::None,
|
||||
FilterStrategy::NONE,
|
||||
INDEXED,
|
||||
BitDepth::One,
|
||||
INDEXED,
|
||||
|
|
@ -800,7 +797,7 @@ fn filter_0_for_palette_1() {
|
|||
fn filter_1_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_palette_1.png",
|
||||
RowFilter::Sub,
|
||||
FilterStrategy::SUB,
|
||||
INDEXED,
|
||||
BitDepth::One,
|
||||
INDEXED,
|
||||
|
|
@ -812,7 +809,7 @@ fn filter_1_for_palette_1() {
|
|||
fn filter_2_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_palette_1.png",
|
||||
RowFilter::Up,
|
||||
FilterStrategy::UP,
|
||||
INDEXED,
|
||||
BitDepth::One,
|
||||
INDEXED,
|
||||
|
|
@ -824,7 +821,7 @@ fn filter_2_for_palette_1() {
|
|||
fn filter_3_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_palette_1.png",
|
||||
RowFilter::Average,
|
||||
FilterStrategy::AVERAGE,
|
||||
INDEXED,
|
||||
BitDepth::One,
|
||||
INDEXED,
|
||||
|
|
@ -836,7 +833,7 @@ fn filter_3_for_palette_1() {
|
|||
fn filter_4_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_palette_1.png",
|
||||
RowFilter::Paeth,
|
||||
FilterStrategy::PAETH,
|
||||
INDEXED,
|
||||
BitDepth::One,
|
||||
INDEXED,
|
||||
|
|
@ -848,7 +845,7 @@ fn filter_4_for_palette_1() {
|
|||
fn filter_5_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_palette_1.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
INDEXED,
|
||||
BitDepth::One,
|
||||
INDEXED,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ fn get_opts(input: &Path) -> (OutFile, Options) {
|
|||
let options = Options {
|
||||
force: true,
|
||||
fast_evaluation: false,
|
||||
filter: indexset! {RowFilter::None},
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
..Default::default()
|
||||
};
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
|
|
@ -409,7 +409,7 @@ fn interlaced_0_to_1_other_filter_mode() {
|
|||
let input = PathBuf::from("tests/files/interlaced_0_to_1_other_filter_mode.png");
|
||||
let (output, mut opts) = get_opts(&input);
|
||||
opts.interlace = Some(Interlacing::Adam7);
|
||||
opts.filter = indexset! {RowFilter::Paeth};
|
||||
opts.filter = indexset! {FilterStrategy::PAETH};
|
||||
|
||||
test_it_converts_callbacks(
|
||||
input,
|
||||
|
|
|
|||
|
|
@ -12,16 +12,13 @@ const GRAYSCALE_ALPHA: u8 = 4;
|
|||
const RGBA: u8 = 6;
|
||||
|
||||
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
||||
let mut options = oxipng::Options {
|
||||
let options = oxipng::Options {
|
||||
force: true,
|
||||
fast_evaluation: false,
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
interlace: None,
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,11 @@ const RGB: u8 = 2;
|
|||
const INDEXED: u8 = 3;
|
||||
|
||||
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
||||
let mut options = oxipng::Options {
|
||||
let options = oxipng::Options {
|
||||
force: true,
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use oxipng::{internal_tests::*, *};
|
|||
fn get_opts() -> Options {
|
||||
Options {
|
||||
force: true,
|
||||
filter: indexset! { RowFilter::None },
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,12 @@ const GRAYSCALE_ALPHA: u8 = 4;
|
|||
const RGBA: u8 = 6;
|
||||
|
||||
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
||||
let mut options = oxipng::Options {
|
||||
let options = oxipng::Options {
|
||||
force: true,
|
||||
fast_evaluation: false,
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,11 @@ const GRAYSCALE_ALPHA: u8 = 4;
|
|||
const RGBA: u8 = 6;
|
||||
|
||||
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
||||
let mut options = oxipng::Options {
|
||||
let options = oxipng::Options {
|
||||
force: true,
|
||||
filter: indexset! {FilterStrategy::NONE},
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,20 +11,16 @@ const INDEXED: u8 = 3;
|
|||
const RGBA: u8 = 6;
|
||||
|
||||
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
||||
let mut options = oxipng::Options {
|
||||
let options = oxipng::Options {
|
||||
force: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(OutFile::from_path(input.with_extension("out.png")), options)
|
||||
}
|
||||
|
||||
fn test_it_converts(
|
||||
input: &str,
|
||||
filter: RowFilter,
|
||||
filter: FilterStrategy,
|
||||
color_type_in: u8,
|
||||
bit_depth_in: BitDepth,
|
||||
color_type_out: u8,
|
||||
|
|
@ -34,8 +30,7 @@ fn test_it_converts(
|
|||
|
||||
let (output, mut opts) = get_opts(&input);
|
||||
let png = PngData::new(&input, &opts).unwrap();
|
||||
opts.filter = IndexSet::new();
|
||||
opts.filter.insert(filter);
|
||||
opts.filter = indexset! {filter};
|
||||
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
|
||||
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
|
||||
|
||||
|
|
@ -67,7 +62,7 @@ fn test_it_converts(
|
|||
fn filter_minsum() {
|
||||
test_it_converts(
|
||||
"tests/files/rgb_16_should_be_rgb_16.png",
|
||||
RowFilter::MinSum,
|
||||
FilterStrategy::MinSum,
|
||||
RGB,
|
||||
BitDepth::Sixteen,
|
||||
RGB,
|
||||
|
|
@ -79,7 +74,7 @@ fn filter_minsum() {
|
|||
fn filter_entropy() {
|
||||
test_it_converts(
|
||||
"tests/files/rgb_8_should_be_rgb_8.png",
|
||||
RowFilter::Entropy,
|
||||
FilterStrategy::Entropy,
|
||||
RGB,
|
||||
BitDepth::Eight,
|
||||
RGB,
|
||||
|
|
@ -91,7 +86,7 @@ fn filter_entropy() {
|
|||
fn filter_bigrams() {
|
||||
test_it_converts(
|
||||
"tests/files/rgba_8_should_be_rgba_8.png",
|
||||
RowFilter::Bigrams,
|
||||
FilterStrategy::Bigrams,
|
||||
RGBA,
|
||||
BitDepth::Eight,
|
||||
RGBA,
|
||||
|
|
@ -103,7 +98,7 @@ fn filter_bigrams() {
|
|||
fn filter_bigent() {
|
||||
test_it_converts(
|
||||
"tests/files/grayscale_8_should_be_grayscale_8.png",
|
||||
RowFilter::BigEnt,
|
||||
FilterStrategy::BigEnt,
|
||||
GRAYSCALE,
|
||||
BitDepth::Eight,
|
||||
GRAYSCALE,
|
||||
|
|
@ -115,7 +110,7 @@ fn filter_bigent() {
|
|||
fn filter_brute() {
|
||||
test_it_converts(
|
||||
"tests/files/palette_8_should_be_palette_8.png",
|
||||
RowFilter::Brute,
|
||||
FilterStrategy::Brute,
|
||||
INDEXED,
|
||||
BitDepth::Eight,
|
||||
INDEXED,
|
||||
|
|
|
|||
Loading…
Reference in a new issue