Refactor filters as enum
This commit is contained in:
parent
420d904ba9
commit
77d063aa84
11 changed files with 331 additions and 275 deletions
|
|
@ -3,7 +3,7 @@
|
|||
extern crate oxipng;
|
||||
extern crate test;
|
||||
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{internal_tests::*, RowFilter};
|
||||
use std::path::PathBuf;
|
||||
use test::Bencher;
|
||||
|
||||
|
|
@ -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(0);
|
||||
png.raw.filter_image(RowFilter::None);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(0);
|
||||
png.raw.filter_image(RowFilter::None);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(0);
|
||||
png.raw.filter_image(RowFilter::None);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(0);
|
||||
png.raw.filter_image(RowFilter::None);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(0);
|
||||
png.raw.filter_image(RowFilter::None);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(1);
|
||||
png.raw.filter_image(RowFilter::Sub);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(1);
|
||||
png.raw.filter_image(RowFilter::Sub);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(1);
|
||||
png.raw.filter_image(RowFilter::Sub);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(1);
|
||||
png.raw.filter_image(RowFilter::Sub);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(1);
|
||||
png.raw.filter_image(RowFilter::Sub);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(2);
|
||||
png.raw.filter_image(RowFilter::Up);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(2);
|
||||
png.raw.filter_image(RowFilter::Up);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(2);
|
||||
png.raw.filter_image(RowFilter::Up);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(2);
|
||||
png.raw.filter_image(RowFilter::Up);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(2);
|
||||
png.raw.filter_image(RowFilter::Up);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(3);
|
||||
png.raw.filter_image(RowFilter::Average);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(3);
|
||||
png.raw.filter_image(RowFilter::Average);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(3);
|
||||
png.raw.filter_image(RowFilter::Average);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(3);
|
||||
png.raw.filter_image(RowFilter::Average);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(3);
|
||||
png.raw.filter_image(RowFilter::Average);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(4);
|
||||
png.raw.filter_image(RowFilter::Paeth);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(4);
|
||||
png.raw.filter_image(RowFilter::Paeth);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(4);
|
||||
png.raw.filter_image(RowFilter::Paeth);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(4);
|
||||
png.raw.filter_image(RowFilter::Paeth);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(4);
|
||||
png.raw.filter_image(RowFilter::Paeth);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(5);
|
||||
png.raw.filter_image(RowFilter::MinSum);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(5);
|
||||
png.raw.filter_image(RowFilter::MinSum);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(5);
|
||||
png.raw.filter_image(RowFilter::MinSum);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(5);
|
||||
png.raw.filter_image(RowFilter::MinSum);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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(5);
|
||||
png.raw.filter_image(RowFilter::MinSum);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
use crate::atomicmin::AtomicMin;
|
||||
use crate::deflate;
|
||||
use crate::filters::RowFilter;
|
||||
use crate::png::PngData;
|
||||
use crate::png::PngImage;
|
||||
use crate::png::STD_COMPRESSION;
|
||||
use crate::png::STD_FILTERS;
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
use crate::rayon;
|
||||
use crate::Deadline;
|
||||
|
|
@ -19,9 +18,13 @@ use std::sync::atomic::AtomicUsize;
|
|||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Must use normal (lazy) compression, as faster ones (greedy) are not representative
|
||||
const STD_COMPRESSION: u8 = 5;
|
||||
const STD_FILTERS: [RowFilter; 2] = [RowFilter::None, RowFilter::MinSum];
|
||||
|
||||
struct Candidate {
|
||||
image: PngData,
|
||||
filter: u8,
|
||||
filter: RowFilter,
|
||||
// first wins tie-breaker
|
||||
nth: usize,
|
||||
}
|
||||
|
|
|
|||
280
src/filters.rs
280
src/filters.rs
|
|
@ -1,130 +1,180 @@
|
|||
use std::{fmt::Display, mem::transmute};
|
||||
|
||||
use crate::error::PngError;
|
||||
|
||||
pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec<u8>) {
|
||||
assert!(data.len() >= bpp);
|
||||
assert!(last_line.is_empty() || data.len() == last_line.len());
|
||||
buf.reserve(data.len());
|
||||
match filter {
|
||||
0 => {
|
||||
buf.extend_from_slice(data);
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for RowFilter {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
if value > Self::LAST {
|
||||
return Err(());
|
||||
}
|
||||
1 => {
|
||||
buf.extend_from_slice(&data[0..bpp]);
|
||||
buf.extend(
|
||||
data.iter()
|
||||
.skip(bpp)
|
||||
.zip(data.iter())
|
||||
.map(|(cur, last)| cur.wrapping_sub(*last)),
|
||||
);
|
||||
}
|
||||
2 => {
|
||||
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)),
|
||||
);
|
||||
};
|
||||
}
|
||||
3 => {
|
||||
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),
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
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]),
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
unsafe { transmute(value as i8) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unfilter_line(
|
||||
filter: u8,
|
||||
bpp: usize,
|
||||
data: &[u8],
|
||||
last_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());
|
||||
match filter {
|
||||
0 => {
|
||||
buf.extend_from_slice(data);
|
||||
}
|
||||
1 => {
|
||||
for (i, &cur) in data.iter().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(b),
|
||||
None => cur,
|
||||
});
|
||||
impl Display for RowFilter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:8}",
|
||||
match *self {
|
||||
Self::None => "None",
|
||||
Self::Sub => "Sub",
|
||||
Self::Up => "Up",
|
||||
Self::Average => "Average",
|
||||
Self::Paeth => "Paeth",
|
||||
Self::MinSum => "MinSum",
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
buf.extend(
|
||||
data.iter()
|
||||
.zip(last_line)
|
||||
.map(|(&cur, &last)| cur.wrapping_add(last)),
|
||||
);
|
||||
}
|
||||
3 => {
|
||||
for (i, (&cur, &last)) in data.iter().zip(last_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),
|
||||
None => cur.wrapping_add(last >> 1),
|
||||
});
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RowFilter {
|
||||
pub const LAST: u8 = Self::MinSum as u8;
|
||||
pub const STANDARD: [Self; 5] = [Self::None, Self::Sub, Self::Up, Self::Average, Self::Paeth];
|
||||
|
||||
pub fn filter_line(self, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec<u8>) {
|
||||
assert!(data.len() >= bpp);
|
||||
assert!(last_line.is_empty() || data.len() == last_line.len());
|
||||
buf.reserve(data.len());
|
||||
match self {
|
||||
Self::None => {
|
||||
buf.extend_from_slice(data);
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
for (i, (&cur, &up)) in data.iter().zip(last_line).enumerate() {
|
||||
buf.push(
|
||||
match i
|
||||
.checked_sub(bpp)
|
||||
.map(|x| (buf.get(x).copied(), last_line.get(x).copied()))
|
||||
{
|
||||
Some((Some(left), Some(left_up))) => {
|
||||
cur.wrapping_add(paeth_predictor(left, up, left_up))
|
||||
}
|
||||
_ => cur.wrapping_add(up),
|
||||
},
|
||||
Self::Sub => {
|
||||
buf.extend_from_slice(&data[0..bpp]);
|
||||
buf.extend(
|
||||
data.iter()
|
||||
.skip(bpp)
|
||||
.zip(data.iter())
|
||||
.map(|(cur, last)| cur.wrapping_sub(*last)),
|
||||
);
|
||||
}
|
||||
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)),
|
||||
);
|
||||
};
|
||||
}
|
||||
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),
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
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]),
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
_ => return Err(PngError::InvalidData),
|
||||
}
|
||||
Ok(())
|
||||
|
||||
pub fn unfilter_line(
|
||||
self,
|
||||
bpp: usize,
|
||||
data: &[u8],
|
||||
last_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());
|
||||
match self {
|
||||
Self::None => {
|
||||
buf.extend_from_slice(data);
|
||||
}
|
||||
Self::Sub => {
|
||||
for (i, &cur) in data.iter().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(b),
|
||||
None => cur,
|
||||
});
|
||||
}
|
||||
}
|
||||
Self::Up => {
|
||||
buf.extend(
|
||||
data.iter()
|
||||
.zip(last_line)
|
||||
.map(|(&cur, &last)| cur.wrapping_add(last)),
|
||||
);
|
||||
}
|
||||
Self::Average => {
|
||||
for (i, (&cur, &last)) in data.iter().zip(last_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),
|
||||
None => cur.wrapping_add(last >> 1),
|
||||
});
|
||||
}
|
||||
}
|
||||
Self::Paeth => {
|
||||
for (i, (&cur, &up)) in data.iter().zip(last_line).enumerate() {
|
||||
buf.push(
|
||||
match i
|
||||
.checked_sub(bpp)
|
||||
.map(|x| (buf.get(x).copied(), last_line.get(x).copied()))
|
||||
{
|
||||
Some((Some(left), Some(left_up))) => {
|
||||
cur.wrapping_add(paeth_predictor(left, up, left_up))
|
||||
}
|
||||
_ => cur.wrapping_add(up),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => return Err(PngError::InvalidData),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {
|
||||
|
|
|
|||
28
src/lib.rs
28
src/lib.rs
|
|
@ -45,6 +45,7 @@ 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;
|
||||
pub use crate::headers::Headers;
|
||||
pub use indexmap::{IndexMap, IndexSet};
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ pub struct Options {
|
|||
/// Which filters to try on the file (0-5)
|
||||
///
|
||||
/// Default: `0,5`
|
||||
pub filter: IndexSet<u8>,
|
||||
pub filter: IndexSet<RowFilter>,
|
||||
/// Whether to change the interlacing type of the file.
|
||||
///
|
||||
/// `None` will not change the current interlacing type.
|
||||
|
|
@ -253,9 +254,10 @@ impl Options {
|
|||
}
|
||||
|
||||
fn apply_preset_3(mut self) -> Self {
|
||||
for i in 1..=4 {
|
||||
self.filter.insert(i);
|
||||
}
|
||||
self.filter.insert(RowFilter::Sub);
|
||||
self.filter.insert(RowFilter::Up);
|
||||
self.filter.insert(RowFilter::Average);
|
||||
self.filter.insert(RowFilter::Paeth);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -292,8 +294,8 @@ impl Default for Options {
|
|||
fn default() -> Options {
|
||||
// Default settings based on -o 2 from the CLI interface
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(0);
|
||||
filter.insert(5);
|
||||
filter.insert(RowFilter::None);
|
||||
filter.insert(RowFilter::MinSum);
|
||||
let mut compression = IndexSet::new();
|
||||
compression.insert(11);
|
||||
// We always need NoOp to be present
|
||||
|
|
@ -465,7 +467,7 @@ pub fn optimize_from_memory(data: &[u8], opts: &Options) -> PngResult<Vec<u8>> {
|
|||
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
|
||||
/// Defines options to be used for a single compression trial
|
||||
struct TrialOptions {
|
||||
pub filter: u8,
|
||||
pub filter: RowFilter,
|
||||
pub compression: u8,
|
||||
}
|
||||
|
||||
|
|
@ -511,9 +513,9 @@ fn optimize_png(
|
|||
let use_filter = if png.raw.ihdr.bit_depth.as_u8() >= 8
|
||||
&& png.raw.ihdr.color_type != colors::ColorType::Indexed
|
||||
{
|
||||
5
|
||||
RowFilter::MinSum
|
||||
} else {
|
||||
0
|
||||
RowFilter::None
|
||||
};
|
||||
if filter.is_empty() {
|
||||
filter.insert(use_filter);
|
||||
|
|
@ -570,7 +572,7 @@ fn optimize_png(
|
|||
|
||||
info!("Trying: {} combinations", results.len());
|
||||
|
||||
let filters: IndexMap<u8, Vec<u8>> = filter
|
||||
let filters: IndexMap<RowFilter, Vec<u8>> = filter
|
||||
.par_iter()
|
||||
.with_max_len(1)
|
||||
.map(|f| {
|
||||
|
|
@ -601,7 +603,7 @@ fn optimize_png(
|
|||
Ok(n) => n,
|
||||
Err(PngError::DeflatedDataTooLong(max)) => {
|
||||
debug!(
|
||||
" zc = {} f = {} >{} bytes",
|
||||
" zc = {} f = {} >{} bytes",
|
||||
trial.compression, trial.filter, max,
|
||||
);
|
||||
return None;
|
||||
|
|
@ -614,7 +616,7 @@ fn optimize_png(
|
|||
best_size.set_min(new_size);
|
||||
|
||||
debug!(
|
||||
" zc = {} f = {} {} bytes",
|
||||
" zc = {} f = {} {} bytes",
|
||||
trial.compression,
|
||||
trial.filter,
|
||||
new_idat.len()
|
||||
|
|
@ -638,7 +640,7 @@ fn optimize_png(
|
|||
png.idat_data = idat_data;
|
||||
info!("Found better combination:");
|
||||
info!(
|
||||
" zc = {} f = {} {} bytes",
|
||||
" zc = {} f = {} {} bytes",
|
||||
opts.compression,
|
||||
opts.filter,
|
||||
png.idat_data.len()
|
||||
|
|
|
|||
14
src/main.rs
14
src/main.rs
|
|
@ -20,6 +20,7 @@ use oxipng::AlphaOptim;
|
|||
use oxipng::Deflaters;
|
||||
use oxipng::Headers;
|
||||
use oxipng::Options;
|
||||
use oxipng::RowFilter;
|
||||
use oxipng::{InFile, OutFile};
|
||||
use std::fs::DirBuilder;
|
||||
#[cfg(feature = "zopfli")]
|
||||
|
|
@ -168,11 +169,15 @@ fn main() {
|
|||
)
|
||||
.arg(
|
||||
Arg::new("filters")
|
||||
.help("PNG delta filters (0-5) - Default: 0,5")
|
||||
.help(&*format!(
|
||||
"PNG delta filters (0-{}) - Default: 0,{}",
|
||||
RowFilter::LAST,
|
||||
RowFilter::MinSum as u8
|
||||
))
|
||||
.short('f')
|
||||
.long("filters")
|
||||
.takes_value(true)
|
||||
.validator(|x| match parse_numeric_range_opts(x, 0, 5) {
|
||||
.validator(|x| match parse_numeric_range_opts(x, 0, RowFilter::LAST) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("Invalid option for filters".to_owned()),
|
||||
}),
|
||||
|
|
@ -381,7 +386,10 @@ fn parse_opts_into_struct(
|
|||
}
|
||||
|
||||
if let Some(x) = matches.value_of("filters") {
|
||||
opts.filter = parse_numeric_range_opts(x, 0, 5).unwrap();
|
||||
opts.filter.clear();
|
||||
for f in parse_numeric_range_opts(x, 0, RowFilter::LAST).unwrap() {
|
||||
opts.filter.insert(RowFilter::try_from(f).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(x) = matches.value_of("timeout") {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ use std::iter::Iterator;
|
|||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Must use normal (lazy) compression, as faster ones (greedy) are not representative
|
||||
pub(crate) const STD_COMPRESSION: u8 = 5;
|
||||
pub(crate) const STD_FILTERS: [u8; 2] = [0, 5];
|
||||
|
||||
pub(crate) mod scan_lines;
|
||||
|
||||
use self::scan_lines::{ScanLines, ScanLinesMut};
|
||||
|
|
@ -292,7 +288,8 @@ impl PngImage {
|
|||
last_pass = line.pass;
|
||||
}
|
||||
last_line.resize(line.data.len(), 0);
|
||||
unfilter_line(line.filter, bpp, line.data, &last_line, &mut unfiltered_buf)?;
|
||||
let filter = RowFilter::try_from(line.filter).map_err(|_| PngError::InvalidData)?;
|
||||
filter.unfilter_line(bpp, line.data, &last_line, &mut unfiltered_buf)?;
|
||||
unfiltered.push(0);
|
||||
unfiltered.extend_from_slice(&unfiltered_buf);
|
||||
std::mem::swap(&mut last_line, &mut unfiltered_buf);
|
||||
|
|
@ -302,13 +299,7 @@ impl PngImage {
|
|||
}
|
||||
|
||||
/// Apply the specified filter type to all rows in the image
|
||||
/// 0: None
|
||||
/// 1: Sub
|
||||
/// 2: Up
|
||||
/// 3: Average
|
||||
/// 4: Paeth
|
||||
/// 5: All (heuristically pick the best filter for each line)
|
||||
pub fn filter_image(&self, filter: u8) -> Vec<u8> {
|
||||
pub fn filter_image(&self, filter: RowFilter) -> 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] = &[];
|
||||
|
|
@ -320,42 +311,44 @@ impl PngImage {
|
|||
last_line = &[];
|
||||
}
|
||||
match filter {
|
||||
0 | 1 | 2 | 3 | 4 => {
|
||||
let filter = if last_pass == line.pass || filter <= 1 {
|
||||
filter
|
||||
} else {
|
||||
0
|
||||
};
|
||||
filtered.push(filter);
|
||||
filter_line(filter, bpp, line.data, last_line, &mut f_buf);
|
||||
filtered.extend_from_slice(&f_buf);
|
||||
}
|
||||
5 => {
|
||||
RowFilter::MinSum => {
|
||||
// Heuristically guess best filter per line
|
||||
// Uses MSAD algorithm mentioned in libpng reference docs
|
||||
// http://www.libpng.org/pub/png/book/chapter09.html
|
||||
let mut best_filter = 0;
|
||||
let mut best_filter = RowFilter::None;
|
||||
let mut best_line = Vec::new();
|
||||
let mut best_size = u64::MAX;
|
||||
|
||||
// Avoid vertical filtering on first line of each interlacing pass
|
||||
for filter in if last_pass == line.pass { 0..5 } else { 0..2 } {
|
||||
filter_line(filter, bpp, line.data, last_line, &mut f_buf);
|
||||
for try_filter in RowFilter::STANDARD {
|
||||
// Avoid vertical filtering on first line of each interlacing pass
|
||||
if last_pass != line.pass && try_filter > RowFilter::Sub {
|
||||
continue;
|
||||
}
|
||||
try_filter.filter_line(bpp, line.data, last_line, &mut f_buf);
|
||||
let size = f_buf.iter().fold(0_u64, |acc, &x| {
|
||||
let signed = x as i8;
|
||||
acc + i16::from(signed).unsigned_abs() as u64
|
||||
});
|
||||
if size < best_size {
|
||||
best_size = size;
|
||||
best_filter = filter;
|
||||
best_filter = try_filter;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
}
|
||||
f_buf.clear() //discard buffer, and start again
|
||||
}
|
||||
filtered.push(best_filter);
|
||||
filtered.push(best_filter as u8);
|
||||
filtered.extend_from_slice(&best_line);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
_ => {
|
||||
let filter = if last_pass == line.pass || filter <= RowFilter::Sub {
|
||||
filter
|
||||
} else {
|
||||
RowFilter::None
|
||||
};
|
||||
filtered.push(filter as u8);
|
||||
filter.filter_line(bpp, line.data, last_line, &mut f_buf);
|
||||
filtered.extend_from_slice(&f_buf);
|
||||
}
|
||||
}
|
||||
last_line = line.data;
|
||||
last_pass = line.pass;
|
||||
|
|
|
|||
138
tests/filters.rs
138
tests/filters.rs
|
|
@ -1,5 +1,5 @@
|
|||
use indexmap::IndexSet;
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{internal_tests::*, RowFilter};
|
||||
use oxipng::{InFile, OutFile};
|
||||
use std::fs::remove_file;
|
||||
use std::path::Path;
|
||||
|
|
@ -11,7 +11,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
|||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(0);
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(
|
||||
|
|
@ -22,7 +22,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
|||
|
||||
fn test_it_converts(
|
||||
input: &str,
|
||||
filter: u8,
|
||||
filter: RowFilter,
|
||||
color_type_in: ColorType,
|
||||
bit_depth_in: BitDepth,
|
||||
color_type_out: ColorType,
|
||||
|
|
@ -67,7 +67,7 @@ fn test_it_converts(
|
|||
fn filter_0_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgba_16.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -79,7 +79,7 @@ fn filter_0_for_rgba_16() {
|
|||
fn filter_1_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgba_16.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -91,7 +91,7 @@ fn filter_1_for_rgba_16() {
|
|||
fn filter_2_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgba_16.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -103,7 +103,7 @@ fn filter_2_for_rgba_16() {
|
|||
fn filter_3_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgba_16.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -115,7 +115,7 @@ fn filter_3_for_rgba_16() {
|
|||
fn filter_4_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgba_16.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -127,7 +127,7 @@ fn filter_4_for_rgba_16() {
|
|||
fn filter_5_for_rgba_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgba_16.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -139,7 +139,7 @@ fn filter_5_for_rgba_16() {
|
|||
fn filter_0_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgba_8.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -151,7 +151,7 @@ fn filter_0_for_rgba_8() {
|
|||
fn filter_1_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgba_8.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -163,7 +163,7 @@ fn filter_1_for_rgba_8() {
|
|||
fn filter_2_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgba_8.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -175,7 +175,7 @@ fn filter_2_for_rgba_8() {
|
|||
fn filter_3_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgba_8.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -187,7 +187,7 @@ fn filter_3_for_rgba_8() {
|
|||
fn filter_4_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgba_8.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -199,7 +199,7 @@ fn filter_4_for_rgba_8() {
|
|||
fn filter_5_for_rgba_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgba_8.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::RGBA,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGBA,
|
||||
|
|
@ -211,7 +211,7 @@ fn filter_5_for_rgba_8() {
|
|||
fn filter_0_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgb_16.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::RGB,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGB,
|
||||
|
|
@ -223,7 +223,7 @@ fn filter_0_for_rgb_16() {
|
|||
fn filter_1_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgb_16.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::RGB,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGB,
|
||||
|
|
@ -235,7 +235,7 @@ fn filter_1_for_rgb_16() {
|
|||
fn filter_2_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgb_16.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::RGB,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGB,
|
||||
|
|
@ -247,7 +247,7 @@ fn filter_2_for_rgb_16() {
|
|||
fn filter_3_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgb_16.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::RGB,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGB,
|
||||
|
|
@ -259,7 +259,7 @@ fn filter_3_for_rgb_16() {
|
|||
fn filter_4_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgb_16.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::RGB,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGB,
|
||||
|
|
@ -271,7 +271,7 @@ fn filter_4_for_rgb_16() {
|
|||
fn filter_5_for_rgb_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgb_16.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::RGB,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::RGB,
|
||||
|
|
@ -283,7 +283,7 @@ fn filter_5_for_rgb_16() {
|
|||
fn filter_0_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_rgb_8.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::RGB,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGB,
|
||||
|
|
@ -295,7 +295,7 @@ fn filter_0_for_rgb_8() {
|
|||
fn filter_1_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_rgb_8.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::RGB,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGB,
|
||||
|
|
@ -307,7 +307,7 @@ fn filter_1_for_rgb_8() {
|
|||
fn filter_2_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_rgb_8.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::RGB,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGB,
|
||||
|
|
@ -319,7 +319,7 @@ fn filter_2_for_rgb_8() {
|
|||
fn filter_3_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_rgb_8.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::RGB,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGB,
|
||||
|
|
@ -331,7 +331,7 @@ fn filter_3_for_rgb_8() {
|
|||
fn filter_4_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_rgb_8.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::RGB,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGB,
|
||||
|
|
@ -343,7 +343,7 @@ fn filter_4_for_rgb_8() {
|
|||
fn filter_5_for_rgb_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_rgb_8.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::RGB,
|
||||
BitDepth::Eight,
|
||||
ColorType::RGB,
|
||||
|
|
@ -355,7 +355,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",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -367,7 +367,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",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -379,7 +379,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",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -391,7 +391,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",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -403,7 +403,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",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -415,7 +415,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",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -427,7 +427,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",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Eight,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -439,7 +439,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",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Eight,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -451,7 +451,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",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Eight,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -463,7 +463,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",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Eight,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -475,7 +475,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",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Eight,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -487,7 +487,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",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::GrayscaleAlpha,
|
||||
BitDepth::Eight,
|
||||
ColorType::GrayscaleAlpha,
|
||||
|
|
@ -499,7 +499,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",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -511,7 +511,7 @@ fn filter_0_for_grayscale_16() {
|
|||
fn filter_1_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_grayscale_16.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -523,7 +523,7 @@ fn filter_1_for_grayscale_16() {
|
|||
fn filter_2_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_grayscale_16.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -535,7 +535,7 @@ fn filter_2_for_grayscale_16() {
|
|||
fn filter_3_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_grayscale_16.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -547,7 +547,7 @@ fn filter_3_for_grayscale_16() {
|
|||
fn filter_4_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_grayscale_16.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -559,7 +559,7 @@ fn filter_4_for_grayscale_16() {
|
|||
fn filter_5_for_grayscale_16() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_grayscale_16.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Sixteen,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -571,7 +571,7 @@ fn filter_5_for_grayscale_16() {
|
|||
fn filter_0_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_grayscale_8.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Eight,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -583,7 +583,7 @@ fn filter_0_for_grayscale_8() {
|
|||
fn filter_1_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_grayscale_8.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Eight,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -595,7 +595,7 @@ fn filter_1_for_grayscale_8() {
|
|||
fn filter_2_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_grayscale_8.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Eight,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -607,7 +607,7 @@ fn filter_2_for_grayscale_8() {
|
|||
fn filter_3_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_grayscale_8.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Eight,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -619,7 +619,7 @@ fn filter_3_for_grayscale_8() {
|
|||
fn filter_4_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_grayscale_8.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Eight,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -631,7 +631,7 @@ fn filter_4_for_grayscale_8() {
|
|||
fn filter_5_for_grayscale_8() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_grayscale_8.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::Grayscale,
|
||||
BitDepth::Eight,
|
||||
ColorType::Grayscale,
|
||||
|
|
@ -643,7 +643,7 @@ fn filter_5_for_grayscale_8() {
|
|||
fn filter_0_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_palette_4.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Four,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -655,7 +655,7 @@ fn filter_0_for_palette_4() {
|
|||
fn filter_1_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_palette_4.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Four,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -667,7 +667,7 @@ fn filter_1_for_palette_4() {
|
|||
fn filter_2_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_palette_4.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Four,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -679,7 +679,7 @@ fn filter_2_for_palette_4() {
|
|||
fn filter_3_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_palette_4.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Four,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -691,7 +691,7 @@ fn filter_3_for_palette_4() {
|
|||
fn filter_4_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_palette_4.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Four,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -703,7 +703,7 @@ fn filter_4_for_palette_4() {
|
|||
fn filter_5_for_palette_4() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_palette_4.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Four,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -715,7 +715,7 @@ fn filter_5_for_palette_4() {
|
|||
fn filter_0_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_palette_2.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Two,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -727,7 +727,7 @@ fn filter_0_for_palette_2() {
|
|||
fn filter_1_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_palette_2.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Two,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -739,7 +739,7 @@ fn filter_1_for_palette_2() {
|
|||
fn filter_2_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_palette_2.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Two,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -751,7 +751,7 @@ fn filter_2_for_palette_2() {
|
|||
fn filter_3_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_palette_2.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Two,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -763,7 +763,7 @@ fn filter_3_for_palette_2() {
|
|||
fn filter_4_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_palette_2.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Two,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -775,7 +775,7 @@ fn filter_4_for_palette_2() {
|
|||
fn filter_5_for_palette_2() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_palette_2.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::Indexed,
|
||||
BitDepth::Two,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -787,7 +787,7 @@ fn filter_5_for_palette_2() {
|
|||
fn filter_0_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_0_for_palette_1.png",
|
||||
0,
|
||||
RowFilter::None,
|
||||
ColorType::Indexed,
|
||||
BitDepth::One,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -799,7 +799,7 @@ fn filter_0_for_palette_1() {
|
|||
fn filter_1_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_1_for_palette_1.png",
|
||||
1,
|
||||
RowFilter::Sub,
|
||||
ColorType::Indexed,
|
||||
BitDepth::One,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -811,7 +811,7 @@ fn filter_1_for_palette_1() {
|
|||
fn filter_2_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_2_for_palette_1.png",
|
||||
2,
|
||||
RowFilter::Up,
|
||||
ColorType::Indexed,
|
||||
BitDepth::One,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -823,7 +823,7 @@ fn filter_2_for_palette_1() {
|
|||
fn filter_3_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_3_for_palette_1.png",
|
||||
3,
|
||||
RowFilter::Average,
|
||||
ColorType::Indexed,
|
||||
BitDepth::One,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -835,7 +835,7 @@ fn filter_3_for_palette_1() {
|
|||
fn filter_4_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_4_for_palette_1.png",
|
||||
4,
|
||||
RowFilter::Paeth,
|
||||
ColorType::Indexed,
|
||||
BitDepth::One,
|
||||
ColorType::Indexed,
|
||||
|
|
@ -847,7 +847,7 @@ fn filter_4_for_palette_1() {
|
|||
fn filter_5_for_palette_1() {
|
||||
test_it_converts(
|
||||
"tests/files/filter_5_for_palette_1.png",
|
||||
5,
|
||||
RowFilter::MinSum,
|
||||
ColorType::Indexed,
|
||||
BitDepth::One,
|
||||
ColorType::Indexed,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use indexmap::IndexSet;
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{internal_tests::*, RowFilter};
|
||||
use oxipng::{InFile, OutFile};
|
||||
#[cfg(feature = "filetime")]
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -16,7 +16,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
|||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(0);
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(
|
||||
|
|
@ -173,7 +173,7 @@ fn verbose_mode() {
|
|||
assert_eq!(logs.len(), 1);
|
||||
logs.sort();
|
||||
for (i, log) in logs.into_iter().enumerate() {
|
||||
let expected_prefix = format!(" zc = 11 f = 0 ");
|
||||
let expected_prefix = format!(" zc = 11 f = None ");
|
||||
assert!(
|
||||
log.starts_with(&expected_prefix),
|
||||
"logs[{}] = {:?} doesn't start with {:?}",
|
||||
|
|
@ -454,7 +454,7 @@ fn interlaced_0_to_1_other_filter_mode() {
|
|||
let (output, mut opts) = get_opts(&input);
|
||||
opts.interlace = Some(1);
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(4);
|
||||
filter.insert(RowFilter::Paeth);
|
||||
opts.filter = filter;
|
||||
|
||||
let png = PngData::new(&input, opts.fix_errors).unwrap();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use indexmap::IndexSet;
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{internal_tests::*, RowFilter};
|
||||
use oxipng::{InFile, OutFile};
|
||||
use std::fs::remove_file;
|
||||
use std::path::Path;
|
||||
|
|
@ -11,7 +11,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
|||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(0);
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use indexmap::IndexSet;
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{internal_tests::*, RowFilter};
|
||||
use oxipng::{InFile, OutFile};
|
||||
use std::fs::remove_file;
|
||||
use std::path::Path;
|
||||
|
|
@ -11,7 +11,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
|||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(0);
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use indexmap::IndexSet;
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{internal_tests::*, RowFilter};
|
||||
use oxipng::{InFile, OutFile};
|
||||
use std::fs::remove_file;
|
||||
use std::path::Path;
|
||||
|
|
@ -11,7 +11,7 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
|
|||
..Default::default()
|
||||
};
|
||||
let mut filter = IndexSet::new();
|
||||
filter.insert(0);
|
||||
filter.insert(RowFilter::None);
|
||||
options.filter = filter;
|
||||
|
||||
(
|
||||
|
|
@ -288,7 +288,7 @@ fn issue_92_filter_0() {
|
|||
fn issue_92_filter_5() {
|
||||
let input = "tests/files/issue-92.png";
|
||||
let (_, mut opts) = get_opts(Path::new(input));
|
||||
opts.filter = [5].iter().cloned().collect();
|
||||
opts.filter = [RowFilter::MinSum].iter().cloned().collect();
|
||||
let output = OutFile::Path(Some(Path::new(input).with_extension("-f5-out.png")));
|
||||
|
||||
test_it_converts(
|
||||
|
|
|
|||
Loading…
Reference in a new issue