Enable option for multiple alpha transformations
|
|
@ -1,6 +1,12 @@
|
||||||
### Version 0.17.0 (unreleased)
|
### Version 0.17.0 (unreleased)
|
||||||
- [SEMVER_MAJOR] Bump minimum required rustc version to 1.19.0
|
- [SEMVER_MAJOR] Bump minimum required rustc version to 1.19.0
|
||||||
- Change all transparent pixels to `rgba(0, 0, 0, 0)` to improve compression
|
- [SEMVER_MINOR] Oxipng will now, by default, attempt to change all transparent pixels to `rgba(0, 0, 0, 0)` to improve compression.
|
||||||
|
It does fast trials with filters 0 and 5 to see if this is an improvement over
|
||||||
|
the existing alpha channel.
|
||||||
|
- [SEMVER_MINOR] Add a `-a` option to the command line (`alphas` in the struct) which enables 6 different
|
||||||
|
trials for optimizing the alpha channel, using the previously mentioned fast heuristic.
|
||||||
|
This option will make optimization of images with transparency somewhat slower,
|
||||||
|
but may improve compression.
|
||||||
|
|
||||||
### Version 0.16.3
|
### Version 0.16.3
|
||||||
- Fix command-line help text ([#70](https://github.com/shssoichiro/oxipng/issues/70))
|
- Fix command-line help text ([#70](https://github.com/shssoichiro/oxipng/issues/70))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ extern crate test;
|
||||||
extern crate oxipng;
|
extern crate oxipng;
|
||||||
|
|
||||||
use oxipng::png;
|
use oxipng::png;
|
||||||
|
use oxipng::colors::AlphaOptim;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
@ -254,3 +255,81 @@ fn reductions_palette_full_reduction(b: &mut Bencher) {
|
||||||
safe_png.reduce_palette();
|
safe_png.reduce_palette();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn reductions_alpha_black(b: &mut Bencher) {
|
||||||
|
let input = test::black_box(PathBuf::from(
|
||||||
|
"tests/files/rgba_8_reduce_alpha_black.png",
|
||||||
|
));
|
||||||
|
let png = png::PngData::new(&input, false).unwrap();
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut safe_png = png.clone();
|
||||||
|
safe_png.reduce_alpha_channel(AlphaOptim::Black);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn reductions_alpha_white(b: &mut Bencher) {
|
||||||
|
let input = test::black_box(PathBuf::from(
|
||||||
|
"tests/files/rgba_8_reduce_alpha_white.png",
|
||||||
|
));
|
||||||
|
let png = png::PngData::new(&input, false).unwrap();
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut safe_png = png.clone();
|
||||||
|
safe_png.reduce_alpha_channel(AlphaOptim::White);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn reductions_alpha_left(b: &mut Bencher) {
|
||||||
|
let input = test::black_box(PathBuf::from(
|
||||||
|
"tests/files/rgba_8_reduce_alpha_left.png",
|
||||||
|
));
|
||||||
|
let png = png::PngData::new(&input, false).unwrap();
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut safe_png = png.clone();
|
||||||
|
safe_png.reduce_alpha_channel(AlphaOptim::Left);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn reductions_alpha_right(b: &mut Bencher) {
|
||||||
|
let input = test::black_box(PathBuf::from(
|
||||||
|
"tests/files/rgba_8_reduce_alpha_right.png",
|
||||||
|
));
|
||||||
|
let png = png::PngData::new(&input, false).unwrap();
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut safe_png = png.clone();
|
||||||
|
safe_png.reduce_alpha_channel(AlphaOptim::Right);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn reductions_alpha_up(b: &mut Bencher) {
|
||||||
|
let input = test::black_box(PathBuf::from(
|
||||||
|
"tests/files/rgba_8_reduce_alpha_up.png",
|
||||||
|
));
|
||||||
|
let png = png::PngData::new(&input, false).unwrap();
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut safe_png = png.clone();
|
||||||
|
safe_png.reduce_alpha_channel(AlphaOptim::Up);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn reductions_alpha_down(b: &mut Bencher) {
|
||||||
|
let input = test::black_box(PathBuf::from(
|
||||||
|
"tests/files/rgba_8_reduce_alpha_down.png",
|
||||||
|
));
|
||||||
|
let png = png::PngData::new(&input, false).unwrap();
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut safe_png = png.clone();
|
||||||
|
safe_png.reduce_alpha_channel(AlphaOptim::Down);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,3 +103,33 @@ impl BitDepth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
|
||||||
|
/// Potential optimization methods for alpha channel
|
||||||
|
pub enum AlphaOptim {
|
||||||
|
NoOp,
|
||||||
|
Black,
|
||||||
|
White,
|
||||||
|
Up,
|
||||||
|
Right,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AlphaOptim {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match *self {
|
||||||
|
AlphaOptim::NoOp => "_",
|
||||||
|
AlphaOptim::Black => "B",
|
||||||
|
AlphaOptim::White => "W",
|
||||||
|
AlphaOptim::Up => "U",
|
||||||
|
AlphaOptim::Right => "R",
|
||||||
|
AlphaOptim::Down => "D",
|
||||||
|
AlphaOptim::Left => "L",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
82
src/lib.rs
|
|
@ -90,6 +90,8 @@ pub struct Options {
|
||||||
/// 8-15 are valid values
|
/// 8-15 are valid values
|
||||||
/// Default: `15`
|
/// Default: `15`
|
||||||
pub window: u8,
|
pub window: u8,
|
||||||
|
/// Alpha filtering strategies to use
|
||||||
|
pub alphas: HashSet<colors::AlphaOptim>,
|
||||||
/// Whether to attempt bit depth reduction
|
/// Whether to attempt bit depth reduction
|
||||||
/// Default: `true`
|
/// Default: `true`
|
||||||
pub bit_depth_reduction: bool,
|
pub bit_depth_reduction: bool,
|
||||||
|
|
@ -241,6 +243,9 @@ impl Default for Options {
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
strategies.insert(i);
|
strategies.insert(i);
|
||||||
}
|
}
|
||||||
|
let mut alphas = HashSet::new();
|
||||||
|
alphas.insert(colors::AlphaOptim::NoOp);
|
||||||
|
alphas.insert(colors::AlphaOptim::Black);
|
||||||
|
|
||||||
// Default to 1 thread on single-core, otherwise use threads = 1.5x CPU cores
|
// Default to 1 thread on single-core, otherwise use threads = 1.5x CPU cores
|
||||||
let num_cpus = num_cpus::get();
|
let num_cpus = num_cpus::get();
|
||||||
|
|
@ -265,6 +270,7 @@ impl Default for Options {
|
||||||
memory: memory,
|
memory: memory,
|
||||||
strategies: strategies,
|
strategies: strategies,
|
||||||
window: 15,
|
window: 15,
|
||||||
|
alphas,
|
||||||
bit_depth_reduction: true,
|
bit_depth_reduction: true,
|
||||||
color_type_reduction: true,
|
color_type_reduction: true,
|
||||||
palette_reduction: true,
|
palette_reduction: true,
|
||||||
|
|
@ -429,7 +435,7 @@ fn optimize_png(
|
||||||
original_data: &[u8],
|
original_data: &[u8],
|
||||||
opts: &Options,
|
opts: &Options,
|
||||||
) -> Result<Vec<u8>, PngError> {
|
) -> Result<Vec<u8>, PngError> {
|
||||||
type TrialWithData = (u8, u8, u8, u8, Vec<u8>);
|
type TrialWithData = (TrialOptions, Vec<u8>);
|
||||||
|
|
||||||
let original_png = png.clone();
|
let original_png = png.clone();
|
||||||
|
|
||||||
|
|
@ -495,7 +501,7 @@ fn optimize_png(
|
||||||
} else {
|
} else {
|
||||||
filter.len()
|
filter.len()
|
||||||
};
|
};
|
||||||
let mut results: Vec<(u8, u8, u8, u8)> = Vec::with_capacity(combinations);
|
let mut results: Vec<TrialOptions> = Vec::with_capacity(combinations);
|
||||||
if opts.verbosity.is_some() {
|
if opts.verbosity.is_some() {
|
||||||
eprintln!("Trying: {} combinations", combinations);
|
eprintln!("Trying: {} combinations", combinations);
|
||||||
}
|
}
|
||||||
|
|
@ -505,24 +511,34 @@ fn optimize_png(
|
||||||
for zc in compression {
|
for zc in compression {
|
||||||
for zm in memory {
|
for zm in memory {
|
||||||
for zs in &strategies {
|
for zs in &strategies {
|
||||||
results.push((*f, *zc, *zm, *zs));
|
results.push(TrialOptions {
|
||||||
|
filter: *f,
|
||||||
|
compression: *zc,
|
||||||
|
memory: *zm,
|
||||||
|
strategy: *zs,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Zopfli compression has no additional options
|
// Zopfli compression has no additional options
|
||||||
results.push((*f, 0, 0, 0));
|
results.push(TrialOptions {
|
||||||
|
filter: *f,
|
||||||
|
compression: 0,
|
||||||
|
memory: 0,
|
||||||
|
strategy: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut filters_tmp: Vec<(u8, Vec<u8>)> = Vec::with_capacity(filter.len());
|
let filters: HashMap<u8, Vec<u8>> = filter
|
||||||
filter
|
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.with_max_len(1)
|
.with_max_len(1)
|
||||||
.map(|f| (*f, png.filter_image(*f)))
|
.map(|f| {
|
||||||
.collect_into(&mut filters_tmp);
|
let png = png.clone();
|
||||||
|
(*f, png.filter_image(*f))
|
||||||
let filters: HashMap<u8, Vec<u8>> = filters_tmp.into_iter().collect();
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let original_len = original_png.idat_data.len();
|
let original_len = original_png.idat_data.len();
|
||||||
let added_interlacing = opts.interlace == Some(1) && original_png.ihdr_data.interlaced == 0;
|
let added_interlacing = opts.interlace == Some(1) && original_png.ihdr_data.interlaced == 0;
|
||||||
|
|
@ -532,9 +548,15 @@ fn optimize_png(
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.with_max_len(1)
|
.with_max_len(1)
|
||||||
.filter_map(|trial| {
|
.filter_map(|trial| {
|
||||||
let filtered = &filters[&trial.0];
|
let filtered = &filters[&trial.filter];
|
||||||
let new_idat = if opts.deflate == Deflaters::Zlib {
|
let new_idat = if opts.deflate == Deflaters::Zlib {
|
||||||
deflate::deflate(filtered, trial.1, trial.2, trial.3, opts.window)
|
deflate::deflate(
|
||||||
|
filtered,
|
||||||
|
trial.compression,
|
||||||
|
trial.memory,
|
||||||
|
trial.strategy,
|
||||||
|
opts.window,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
deflate::zopfli_deflate(filtered)
|
deflate::zopfli_deflate(filtered)
|
||||||
}.unwrap();
|
}.unwrap();
|
||||||
|
|
@ -542,32 +564,33 @@ fn optimize_png(
|
||||||
if opts.verbosity == Some(1) {
|
if opts.verbosity == Some(1) {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
" zc = {} zm = {} zs = {} f = {} {} bytes",
|
" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||||
trial.1,
|
trial.compression,
|
||||||
trial.2,
|
trial.memory,
|
||||||
trial.3,
|
trial.strategy,
|
||||||
trial.0,
|
trial.filter,
|
||||||
new_idat.len()
|
new_idat.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_idat.len() < original_len || added_interlacing || opts.force {
|
if new_idat.len() < original_len || added_interlacing || opts.force {
|
||||||
Some((trial.0, trial.1, trial.2, trial.3, new_idat))
|
Some((trial, new_idat))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.reduce_with(|i, j| if i.4.len() <= j.4.len() { i } else { j });
|
.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j });
|
||||||
|
|
||||||
if let Some(better) = best {
|
if let Some(better) = best {
|
||||||
png.idat_data = better.4;
|
png.idat_data = better.1;
|
||||||
if opts.verbosity.is_some() {
|
if opts.verbosity.is_some() {
|
||||||
|
let opts = better.0;
|
||||||
eprintln!("Found better combination:");
|
eprintln!("Found better combination:");
|
||||||
eprintln!(
|
eprintln!(
|
||||||
" zc = {} zm = {} zs = {} f = {} {} bytes",
|
" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||||
better.1,
|
opts.compression,
|
||||||
better.2,
|
opts.memory,
|
||||||
better.3,
|
opts.strategy,
|
||||||
better.0,
|
opts.filter,
|
||||||
png.idat_data.len()
|
png.idat_data.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -673,8 +696,6 @@ fn perform_reductions(png: &mut png::PngData, opts: &Options) -> bool {
|
||||||
report_reduction(png);
|
report_reduction(png);
|
||||||
}
|
}
|
||||||
|
|
||||||
png.reduce_alpha_channel();
|
|
||||||
|
|
||||||
if let Some(interlacing) = opts.interlace {
|
if let Some(interlacing) = opts.interlace {
|
||||||
if png.change_interlacing(interlacing) {
|
if png.change_interlacing(interlacing) {
|
||||||
png.ihdr_data.interlaced = interlacing;
|
png.ihdr_data.interlaced = interlacing;
|
||||||
|
|
@ -682,6 +703,8 @@ fn perform_reductions(png: &mut png::PngData, opts: &Options) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
png.try_alpha_reduction(&opts.alphas);
|
||||||
|
|
||||||
reduction_occurred
|
reduction_occurred
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -744,3 +767,12 @@ fn perform_strip(png: &mut png::PngData, opts: &Options) {
|
||||||
fn is_fully_optimized(original_size: usize, optimized_size: usize, opts: &Options) -> bool {
|
fn is_fully_optimized(original_size: usize, optimized_size: usize, opts: &Options) -> bool {
|
||||||
original_size <= optimized_size && !opts.force && opts.interlace.is_none()
|
original_size <= optimized_size && !opts.force && opts.interlace.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
/// Defines options to be used for a single compression trial
|
||||||
|
struct TrialOptions {
|
||||||
|
pub filter: u8,
|
||||||
|
pub compression: u8,
|
||||||
|
pub memory: u8,
|
||||||
|
pub strategy: u8,
|
||||||
|
}
|
||||||
|
|
|
||||||
13
src/main.rs
|
|
@ -12,6 +12,7 @@ extern crate clap;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
use oxipng::colors::AlphaOptim;
|
||||||
use oxipng::deflate::Deflaters;
|
use oxipng::deflate::Deflaters;
|
||||||
use oxipng::headers::Headers;
|
use oxipng::headers::Headers;
|
||||||
use oxipng::Options;
|
use oxipng::Options;
|
||||||
|
|
@ -156,6 +157,10 @@ fn main() {
|
||||||
.possible_value("8k")
|
.possible_value("8k")
|
||||||
.possible_value("16k")
|
.possible_value("16k")
|
||||||
.possible_value("32k"))
|
.possible_value("32k"))
|
||||||
|
.arg(Arg::with_name("alpha")
|
||||||
|
.help("Perform additional alpha optimizations")
|
||||||
|
.short("a")
|
||||||
|
.long("alpha"))
|
||||||
.arg(Arg::with_name("no-bit-reduction")
|
.arg(Arg::with_name("no-bit-reduction")
|
||||||
.help("No bit depth reduction")
|
.help("No bit depth reduction")
|
||||||
.long("nb"))
|
.long("nb"))
|
||||||
|
|
@ -336,6 +341,14 @@ fn parse_opts_into_struct(matches: &ArgMatches) -> Result<Options, String> {
|
||||||
opts.stdout = true;
|
opts.stdout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matches.is_present("alpha") {
|
||||||
|
opts.alphas.insert(AlphaOptim::White);
|
||||||
|
opts.alphas.insert(AlphaOptim::Up);
|
||||||
|
opts.alphas.insert(AlphaOptim::Down);
|
||||||
|
opts.alphas.insert(AlphaOptim::Left);
|
||||||
|
opts.alphas.insert(AlphaOptim::Right);
|
||||||
|
}
|
||||||
|
|
||||||
if matches.is_present("backup") {
|
if matches.is_present("backup") {
|
||||||
opts.backup = true;
|
opts.backup = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
158
src/png.rs
|
|
@ -1,6 +1,6 @@
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use byteorder::{BigEndian, WriteBytesExt};
|
use byteorder::{BigEndian, WriteBytesExt};
|
||||||
use colors::{BitDepth, ColorType};
|
use colors::{BitDepth, ColorType, AlphaOptim};
|
||||||
use crc::crc32;
|
use crc::crc32;
|
||||||
use deflate;
|
use deflate;
|
||||||
use error::PngError;
|
use error::PngError;
|
||||||
|
|
@ -15,6 +15,12 @@ use std::io::Read;
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
const STD_COMPRESSION: u8 = 8;
|
||||||
|
const STD_MEMORY: u8 = 9;
|
||||||
|
const STD_STRATEGY: u8 = 2; // Huffman only
|
||||||
|
const STD_WINDOW: u8 = 15;
|
||||||
|
const STD_FILTERS: [u8; 2] = [0, 5];
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// An iterator over the scan lines of a PNG image
|
/// An iterator over the scan lines of a PNG image
|
||||||
pub struct ScanLines<'a> {
|
pub struct ScanLines<'a> {
|
||||||
|
|
@ -737,7 +743,36 @@ impl PngData {
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reduce_alpha_channel(&mut self) -> bool {
|
pub fn try_alpha_reduction(&mut self, alphas: &HashSet<AlphaOptim>) {
|
||||||
|
assert!(!alphas.is_empty());
|
||||||
|
let best = alphas
|
||||||
|
.iter()
|
||||||
|
.map(|alpha| {
|
||||||
|
let mut image = self.clone();
|
||||||
|
image.reduce_alpha_channel(*alpha);
|
||||||
|
let size = STD_FILTERS
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
deflate::deflate(
|
||||||
|
&image.filter_image(*f),
|
||||||
|
STD_COMPRESSION,
|
||||||
|
STD_MEMORY,
|
||||||
|
STD_STRATEGY,
|
||||||
|
STD_WINDOW,
|
||||||
|
).unwrap()
|
||||||
|
.len()
|
||||||
|
})
|
||||||
|
.min()
|
||||||
|
.unwrap();
|
||||||
|
(size, image)
|
||||||
|
})
|
||||||
|
.min_by_key(|&(size, _)| size)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.raw_data = best.1.raw_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reduce_alpha_channel(&mut self, optim: AlphaOptim) -> bool {
|
||||||
let (bpc, bpp) = match self.ihdr_data.color_type {
|
let (bpc, bpp) = match self.ihdr_data.color_type {
|
||||||
ColorType::RGBA => {
|
ColorType::RGBA => {
|
||||||
match self.ihdr_data.bit_depth {
|
match self.ihdr_data.bit_depth {
|
||||||
|
|
@ -757,20 +792,121 @@ impl PngData {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut reduced = Vec::with_capacity(self.raw_data.len());
|
let mut reduced = Vec::with_capacity(self.raw_data.len());
|
||||||
for line in self.scan_lines() {
|
|
||||||
reduced.push(line.filter);
|
match optim {
|
||||||
for pixel in line.data.chunks(bpp) {
|
AlphaOptim::NoOp => {
|
||||||
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
return false;
|
||||||
for _ in 0..bpp {
|
}
|
||||||
reduced.push(0);
|
AlphaOptim::Black => {
|
||||||
|
for line in self.scan_lines() {
|
||||||
|
reduced.push(line.filter);
|
||||||
|
for pixel in line.data.chunks(bpp) {
|
||||||
|
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
||||||
|
for _ in 0..bpp {
|
||||||
|
reduced.push(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reduced.extend_from_slice(pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlphaOptim::White => {
|
||||||
|
for line in self.scan_lines() {
|
||||||
|
reduced.push(line.filter);
|
||||||
|
for pixel in line.data.chunks(bpp) {
|
||||||
|
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
||||||
|
for _ in 0..(bpp - bpc) {
|
||||||
|
reduced.push(255);
|
||||||
|
}
|
||||||
|
for _ in 0..bpc {
|
||||||
|
reduced.push(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reduced.extend_from_slice(pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlphaOptim::Up => {
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
let scan_lines = self.scan_lines().collect::<Vec<ScanLine>>();
|
||||||
|
let mut last_line = vec![0; scan_lines[0].data.len()];
|
||||||
|
let mut current_line = Vec::with_capacity(last_line.len());
|
||||||
|
for line in scan_lines.into_iter().rev() {
|
||||||
|
current_line.push(line.filter);
|
||||||
|
for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) {
|
||||||
|
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
||||||
|
current_line.extend_from_slice(&last_pixel[0..(bpp - bpc)]);
|
||||||
|
for _ in 0..bpc {
|
||||||
|
current_line.push(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_line.extend_from_slice(pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_line = current_line.clone();
|
||||||
|
lines.push(current_line.clone());
|
||||||
|
current_line.clear();
|
||||||
|
}
|
||||||
|
reduced.extend(lines.into_iter().rev().flatten());
|
||||||
|
}
|
||||||
|
AlphaOptim::Down => {
|
||||||
|
let mut last_line = vec![0; self.scan_lines().next().unwrap().data.len()];
|
||||||
|
for line in self.scan_lines() {
|
||||||
|
reduced.push(line.filter);
|
||||||
|
for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) {
|
||||||
|
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
||||||
|
reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]);
|
||||||
|
for _ in 0..bpc {
|
||||||
|
reduced.push(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reduced.extend_from_slice(pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_line = reduced.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlphaOptim::Left => {
|
||||||
|
for line in self.scan_lines() {
|
||||||
|
let mut line_bytes = Vec::with_capacity(line.data.len());
|
||||||
|
let mut last_pixel = vec![0; bpp];
|
||||||
|
for pixel in line.data.chunks(bpp).rev() {
|
||||||
|
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
||||||
|
line_bytes.extend_from_slice(&last_pixel[0..(bpp - bpc)]);
|
||||||
|
for _ in 0..bpc {
|
||||||
|
line_bytes.push(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line_bytes.extend_from_slice(pixel);
|
||||||
|
}
|
||||||
|
last_pixel = pixel.to_owned();
|
||||||
|
}
|
||||||
|
reduced.push(line.filter);
|
||||||
|
reduced.extend(line_bytes.chunks(bpp).rev().flatten());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlphaOptim::Right => {
|
||||||
|
for line in self.scan_lines() {
|
||||||
|
reduced.push(line.filter);
|
||||||
|
let mut last_pixel = vec![0; bpp];
|
||||||
|
for pixel in line.data.chunks(bpp) {
|
||||||
|
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
|
||||||
|
reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]);
|
||||||
|
for _ in 0..bpc {
|
||||||
|
reduced.push(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reduced.extend_from_slice(pixel);
|
||||||
|
}
|
||||||
|
last_pixel = pixel.to_owned();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
reduced.extend_from_slice(pixel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.raw_data = reduced;
|
self.raw_data = reduced;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
tests/files/grayscale_alpha_16_reduce_alpha_down.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
tests/files/grayscale_alpha_16_reduce_alpha_left.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
tests/files/grayscale_alpha_16_reduce_alpha_right.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
tests/files/grayscale_alpha_16_reduce_alpha_up.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
tests/files/grayscale_alpha_16_reduce_alpha_white.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
tests/files/grayscale_alpha_8_reduce_alpha_down.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tests/files/grayscale_alpha_8_reduce_alpha_left.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tests/files/grayscale_alpha_8_reduce_alpha_right.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tests/files/grayscale_alpha_8_reduce_alpha_up.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tests/files/grayscale_alpha_8_reduce_alpha_white.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
tests/files/rgba_16_reduce_alpha_down.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
tests/files/rgba_16_reduce_alpha_left.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
tests/files/rgba_16_reduce_alpha_right.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
tests/files/rgba_16_reduce_alpha_up.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
tests/files/rgba_16_reduce_alpha_white.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
tests/files/rgba_8_reduce_alpha_down.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
tests/files/rgba_8_reduce_alpha_left.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
tests/files/rgba_8_reduce_alpha_right.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
tests/files/rgba_8_reduce_alpha_up.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
tests/files/rgba_8_reduce_alpha_white.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
extern crate oxipng;
|
extern crate oxipng;
|
||||||
|
|
||||||
use oxipng::colors::{BitDepth, ColorType};
|
use oxipng::colors::{BitDepth, ColorType, AlphaOptim};
|
||||||
use oxipng::png;
|
use oxipng::png;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
@ -1096,7 +1096,6 @@ fn rgba_16_reduce_alpha_black() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rgba_8_reduce_alpha_black() {
|
fn rgba_8_reduce_alpha_black() {
|
||||||
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_black.png");
|
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_black.png");
|
||||||
|
|
@ -1131,7 +1130,6 @@ fn grayscale_alpha_16_reduce_alpha_black() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grayscale_alpha_8_reduce_alpha_black() {
|
fn grayscale_alpha_8_reduce_alpha_black() {
|
||||||
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_black.png");
|
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_black.png");
|
||||||
|
|
@ -1148,3 +1146,383 @@ fn grayscale_alpha_8_reduce_alpha_black() {
|
||||||
BitDepth::Eight,
|
BitDepth::Eight,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_16_reduce_alpha_white() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_16_reduce_alpha_white.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::White);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_8_reduce_alpha_white() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_white.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::White);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_16_reduce_alpha_white() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_16_reduce_alpha_white.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::White);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_8_reduce_alpha_white() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_white.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::White);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_16_reduce_alpha_down() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_16_reduce_alpha_down.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Down);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_8_reduce_alpha_down() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_down.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Down);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_16_reduce_alpha_down() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_16_reduce_alpha_down.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Down);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_8_reduce_alpha_down() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_down.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Down);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_16_reduce_alpha_up() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_16_reduce_alpha_up.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Up);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_8_reduce_alpha_up() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_up.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Up);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_16_reduce_alpha_up() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_16_reduce_alpha_up.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Up);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_8_reduce_alpha_up() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_up.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Up);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_16_reduce_alpha_left() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_16_reduce_alpha_left.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Left);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_8_reduce_alpha_left() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_left.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Left);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_16_reduce_alpha_left() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_16_reduce_alpha_left.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Left);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_8_reduce_alpha_left() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_left.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Left);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_16_reduce_alpha_right() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_16_reduce_alpha_right.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Right);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rgba_8_reduce_alpha_right() {
|
||||||
|
let input = PathBuf::from("tests/files/rgba_8_reduce_alpha_right.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Right);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::RGBA,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_16_reduce_alpha_right() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_16_reduce_alpha_right.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Right);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Sixteen,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_8_reduce_alpha_right() {
|
||||||
|
let input = PathBuf::from("tests/files/grayscale_alpha_8_reduce_alpha_right.png");
|
||||||
|
let mut opts = get_opts(&input);
|
||||||
|
opts.alphas = HashSet::with_capacity(1);
|
||||||
|
opts.alphas.insert(AlphaOptim::Right);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
test_it_converts(
|
||||||
|
&input,
|
||||||
|
&output,
|
||||||
|
&opts,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
ColorType::GrayscaleAlpha,
|
||||||
|
BitDepth::Eight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||