Move options into separate mod
This commit is contained in:
parent
97af04a539
commit
8088b1d20a
2 changed files with 255 additions and 244 deletions
247
src/lib.rs
247
src/lib.rs
|
|
@ -34,10 +34,9 @@ use crate::reduction::*;
|
||||||
use log::{debug, info, trace, warn};
|
use log::{debug, info, trace, warn};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
|
||||||
use std::fs::{File, Metadata};
|
use std::fs::{File, Metadata};
|
||||||
use std::io::{stdin, stdout, BufWriter, Read, Write};
|
use std::io::{stdin, stdout, BufWriter, Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
@ -48,6 +47,7 @@ pub use crate::error::PngError;
|
||||||
pub use crate::filters::RowFilter;
|
pub use crate::filters::RowFilter;
|
||||||
pub use crate::headers::StripChunks;
|
pub use crate::headers::StripChunks;
|
||||||
pub use crate::interlace::Interlacing;
|
pub use crate::interlace::Interlacing;
|
||||||
|
pub use crate::options::{Options, OutFile, InFile};
|
||||||
pub use indexmap::{indexset, IndexSet};
|
pub use indexmap::{indexset, IndexSet};
|
||||||
pub use rgb::{RGB16, RGBA8};
|
pub use rgb::{RGB16, RGBA8};
|
||||||
|
|
||||||
|
|
@ -59,6 +59,7 @@ mod evaluate;
|
||||||
mod filters;
|
mod filters;
|
||||||
mod headers;
|
mod headers;
|
||||||
mod interlace;
|
mod interlace;
|
||||||
|
mod options;
|
||||||
mod png;
|
mod png;
|
||||||
mod reduction;
|
mod reduction;
|
||||||
#[cfg(feature = "sanity-checks")]
|
#[cfg(feature = "sanity-checks")]
|
||||||
|
|
@ -75,250 +76,8 @@ pub mod internal_tests {
|
||||||
pub use crate::sanity_checks::*;
|
pub use crate::sanity_checks::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum OutFile {
|
|
||||||
/// Don't actually write any output, just calculate the best results.
|
|
||||||
None,
|
|
||||||
/// Write output to a file.
|
|
||||||
///
|
|
||||||
/// * `path`: Path to write the output file. `None` means same as input.
|
|
||||||
/// * `preserve_attrs`: Ensure the output file has the same permissions & timestamps as the input file.
|
|
||||||
Path {
|
|
||||||
path: Option<PathBuf>,
|
|
||||||
preserve_attrs: bool,
|
|
||||||
},
|
|
||||||
/// Write to standard output.
|
|
||||||
StdOut,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutFile {
|
|
||||||
/// Construct a new `OutFile` with the given path.
|
|
||||||
///
|
|
||||||
/// This is a convenience method for `OutFile::Path { path: Some(path), preserve_attrs: false }`.
|
|
||||||
pub fn from_path(path: PathBuf) -> Self {
|
|
||||||
OutFile::Path {
|
|
||||||
path: Some(path),
|
|
||||||
preserve_attrs: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> Option<&Path> {
|
|
||||||
match *self {
|
|
||||||
OutFile::Path {
|
|
||||||
path: Some(ref p), ..
|
|
||||||
} => Some(p.as_path()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Where to read images from
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum InFile {
|
|
||||||
Path(PathBuf),
|
|
||||||
StdIn,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InFile {
|
|
||||||
pub fn path(&self) -> Option<&Path> {
|
|
||||||
match *self {
|
|
||||||
InFile::Path(ref p) => Some(p.as_path()),
|
|
||||||
InFile::StdIn => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for InFile {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
InFile::Path(ref p) => write!(f, "{}", p.display()),
|
|
||||||
InFile::StdIn => f.write_str("stdin"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<PathBuf>> From<T> for InFile {
|
|
||||||
fn from(s: T) -> Self {
|
|
||||||
InFile::Path(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type PngResult<T> = Result<T, PngError>;
|
pub type PngResult<T> = Result<T, PngError>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
/// Options controlling the output of the `optimize` function
|
|
||||||
pub struct Options {
|
|
||||||
/// Attempt to fix errors when decoding the input file rather than returning an `Err`.
|
|
||||||
///
|
|
||||||
/// Default: `false`
|
|
||||||
pub fix_errors: bool,
|
|
||||||
/// Write to output even if there was no improvement in compression.
|
|
||||||
///
|
|
||||||
/// Default: `false`
|
|
||||||
pub force: bool,
|
|
||||||
/// Which RowFilters to try on the file
|
|
||||||
///
|
|
||||||
/// Default: `None,Sub,Entropy,Bigrams`
|
|
||||||
pub filter: IndexSet<RowFilter>,
|
|
||||||
/// Whether to change the interlacing type of the file.
|
|
||||||
///
|
|
||||||
/// `None` will not change the current interlacing type.
|
|
||||||
///
|
|
||||||
/// `Some(x)` will change the file to interlacing mode `x`.
|
|
||||||
///
|
|
||||||
/// Default: `Some(Interlacing::None)`
|
|
||||||
pub interlace: Option<Interlacing>,
|
|
||||||
/// Whether to allow transparent pixels to be altered to improve compression.
|
|
||||||
pub optimize_alpha: bool,
|
|
||||||
/// Whether to attempt bit depth reduction
|
|
||||||
///
|
|
||||||
/// Default: `true`
|
|
||||||
pub bit_depth_reduction: bool,
|
|
||||||
/// Whether to attempt color type reduction
|
|
||||||
///
|
|
||||||
/// Default: `true`
|
|
||||||
pub color_type_reduction: bool,
|
|
||||||
/// Whether to attempt palette reduction
|
|
||||||
///
|
|
||||||
/// Default: `true`
|
|
||||||
pub palette_reduction: bool,
|
|
||||||
/// Whether to attempt grayscale reduction
|
|
||||||
///
|
|
||||||
/// Default: `true`
|
|
||||||
pub grayscale_reduction: bool,
|
|
||||||
/// Whether to perform recoding of IDAT and other compressed chunks
|
|
||||||
///
|
|
||||||
/// If any type of reduction is performed, IDAT recoding will be performed
|
|
||||||
/// regardless of this setting
|
|
||||||
///
|
|
||||||
/// Default: `true`
|
|
||||||
pub idat_recoding: bool,
|
|
||||||
/// Whether to forcibly reduce 16-bit to 8-bit by scaling
|
|
||||||
///
|
|
||||||
/// Default: `false`
|
|
||||||
pub scale_16: bool,
|
|
||||||
/// Which chunks to strip from the PNG file, if any
|
|
||||||
///
|
|
||||||
/// Default: `None`
|
|
||||||
pub strip: StripChunks,
|
|
||||||
/// Which DEFLATE algorithm to use
|
|
||||||
///
|
|
||||||
/// Default: `Libdeflater`
|
|
||||||
pub deflate: Deflaters,
|
|
||||||
/// Whether to use fast evaluation to pick the best filter
|
|
||||||
///
|
|
||||||
/// Default: `true`
|
|
||||||
pub fast_evaluation: bool,
|
|
||||||
|
|
||||||
/// Maximum amount of time to spend on optimizations.
|
|
||||||
/// Further potential optimizations are skipped if the timeout is exceeded.
|
|
||||||
pub timeout: Option<Duration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Options {
|
|
||||||
pub fn from_preset(level: u8) -> Options {
|
|
||||||
let opts = Options::default();
|
|
||||||
match level {
|
|
||||||
0 => opts.apply_preset_0(),
|
|
||||||
1 => opts.apply_preset_1(),
|
|
||||||
2 => opts.apply_preset_2(),
|
|
||||||
3 => opts.apply_preset_3(),
|
|
||||||
4 => opts.apply_preset_4(),
|
|
||||||
5 => opts.apply_preset_5(),
|
|
||||||
6 => opts.apply_preset_6(),
|
|
||||||
_ => {
|
|
||||||
warn!("Level 7 and above don't exist yet and are identical to level 6");
|
|
||||||
opts.apply_preset_6()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_compression() -> Options {
|
|
||||||
Options::from_preset(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following methods make assumptions that they are operating
|
|
||||||
// on an `Options` struct generated by the `default` method.
|
|
||||||
fn apply_preset_0(mut self) -> Self {
|
|
||||||
self.filter.clear();
|
|
||||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
|
||||||
*compression = 5;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_preset_1(mut self) -> Self {
|
|
||||||
self.filter.clear();
|
|
||||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
|
||||||
*compression = 10;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_preset_2(self) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_preset_3(mut self) -> Self {
|
|
||||||
self.fast_evaluation = false;
|
|
||||||
self.filter = indexset! {
|
|
||||||
RowFilter::None,
|
|
||||||
RowFilter::Bigrams,
|
|
||||||
RowFilter::BigEnt,
|
|
||||||
RowFilter::Brute
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_preset_4(mut self) -> Self {
|
|
||||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
|
||||||
*compression = 12;
|
|
||||||
}
|
|
||||||
self.apply_preset_3()
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
|
||||||
*compression = 12;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_preset_6(mut self) -> Self {
|
|
||||||
self.filter.insert(RowFilter::Average);
|
|
||||||
self.filter.insert(RowFilter::Paeth);
|
|
||||||
self.apply_preset_5()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Options {
|
|
||||||
fn default() -> Options {
|
|
||||||
// Default settings based on -o 2 from the CLI interface
|
|
||||||
Options {
|
|
||||||
fix_errors: false,
|
|
||||||
force: false,
|
|
||||||
filter: indexset! {RowFilter::None, RowFilter::Sub, RowFilter::Entropy, RowFilter::Bigrams},
|
|
||||||
interlace: Some(Interlacing::None),
|
|
||||||
optimize_alpha: false,
|
|
||||||
bit_depth_reduction: true,
|
|
||||||
color_type_reduction: true,
|
|
||||||
palette_reduction: true,
|
|
||||||
grayscale_reduction: true,
|
|
||||||
idat_recoding: true,
|
|
||||||
scale_16: false,
|
|
||||||
strip: StripChunks::None,
|
|
||||||
deflate: Deflaters::Libdeflater { compression: 11 },
|
|
||||||
fast_evaluation: true,
|
|
||||||
timeout: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// A raw image definition which can be used to create an optimized png
|
/// A raw image definition which can be used to create an optimized png
|
||||||
pub struct RawImage {
|
pub struct RawImage {
|
||||||
|
|
|
||||||
252
src/options.rs
Normal file
252
src/options.rs
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
use log::warn;
|
||||||
|
use std::fmt;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::deflate::Deflaters;
|
||||||
|
use crate::filters::RowFilter;
|
||||||
|
use crate::headers::StripChunks;
|
||||||
|
use crate::interlace::Interlacing;
|
||||||
|
use indexmap::{indexset, IndexSet};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum OutFile {
|
||||||
|
/// Don't actually write any output, just calculate the best results.
|
||||||
|
None,
|
||||||
|
/// Write output to a file.
|
||||||
|
///
|
||||||
|
/// * `path`: Path to write the output file. `None` means same as input.
|
||||||
|
/// * `preserve_attrs`: Ensure the output file has the same permissions & timestamps as the input file.
|
||||||
|
Path {
|
||||||
|
path: Option<PathBuf>,
|
||||||
|
preserve_attrs: bool,
|
||||||
|
},
|
||||||
|
/// Write to standard output.
|
||||||
|
StdOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutFile {
|
||||||
|
/// Construct a new `OutFile` with the given path.
|
||||||
|
///
|
||||||
|
/// This is a convenience method for `OutFile::Path { path: Some(path), preserve_attrs: false }`.
|
||||||
|
pub fn from_path(path: PathBuf) -> Self {
|
||||||
|
OutFile::Path {
|
||||||
|
path: Some(path),
|
||||||
|
preserve_attrs: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> Option<&Path> {
|
||||||
|
match *self {
|
||||||
|
OutFile::Path {
|
||||||
|
path: Some(ref p), ..
|
||||||
|
} => Some(p.as_path()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where to read images from
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum InFile {
|
||||||
|
Path(PathBuf),
|
||||||
|
StdIn,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InFile {
|
||||||
|
pub fn path(&self) -> Option<&Path> {
|
||||||
|
match *self {
|
||||||
|
InFile::Path(ref p) => Some(p.as_path()),
|
||||||
|
InFile::StdIn => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for InFile {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
InFile::Path(ref p) => write!(f, "{}", p.display()),
|
||||||
|
InFile::StdIn => f.write_str("stdin"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<PathBuf>> From<T> for InFile {
|
||||||
|
fn from(s: T) -> Self {
|
||||||
|
InFile::Path(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
/// Options controlling the output of the `optimize` function
|
||||||
|
pub struct Options {
|
||||||
|
/// Attempt to fix errors when decoding the input file rather than returning an `Err`.
|
||||||
|
///
|
||||||
|
/// Default: `false`
|
||||||
|
pub fix_errors: bool,
|
||||||
|
/// Write to output even if there was no improvement in compression.
|
||||||
|
///
|
||||||
|
/// Default: `false`
|
||||||
|
pub force: bool,
|
||||||
|
/// Which RowFilters to try on the file
|
||||||
|
///
|
||||||
|
/// Default: `None,Sub,Entropy,Bigrams`
|
||||||
|
pub filter: IndexSet<RowFilter>,
|
||||||
|
/// Whether to change the interlacing type of the file.
|
||||||
|
///
|
||||||
|
/// `None` will not change the current interlacing type.
|
||||||
|
///
|
||||||
|
/// `Some(x)` will change the file to interlacing mode `x`.
|
||||||
|
///
|
||||||
|
/// Default: `Some(Interlacing::None)`
|
||||||
|
pub interlace: Option<Interlacing>,
|
||||||
|
/// Whether to allow transparent pixels to be altered to improve compression.
|
||||||
|
pub optimize_alpha: bool,
|
||||||
|
/// Whether to attempt bit depth reduction
|
||||||
|
///
|
||||||
|
/// Default: `true`
|
||||||
|
pub bit_depth_reduction: bool,
|
||||||
|
/// Whether to attempt color type reduction
|
||||||
|
///
|
||||||
|
/// Default: `true`
|
||||||
|
pub color_type_reduction: bool,
|
||||||
|
/// Whether to attempt palette reduction
|
||||||
|
///
|
||||||
|
/// Default: `true`
|
||||||
|
pub palette_reduction: bool,
|
||||||
|
/// Whether to attempt grayscale reduction
|
||||||
|
///
|
||||||
|
/// Default: `true`
|
||||||
|
pub grayscale_reduction: bool,
|
||||||
|
/// Whether to perform recoding of IDAT and other compressed chunks
|
||||||
|
///
|
||||||
|
/// If any type of reduction is performed, IDAT recoding will be performed
|
||||||
|
/// regardless of this setting
|
||||||
|
///
|
||||||
|
/// Default: `true`
|
||||||
|
pub idat_recoding: bool,
|
||||||
|
/// Whether to forcibly reduce 16-bit to 8-bit by scaling
|
||||||
|
///
|
||||||
|
/// Default: `false`
|
||||||
|
pub scale_16: bool,
|
||||||
|
/// Which chunks to strip from the PNG file, if any
|
||||||
|
///
|
||||||
|
/// Default: `None`
|
||||||
|
pub strip: StripChunks,
|
||||||
|
/// Which DEFLATE algorithm to use
|
||||||
|
///
|
||||||
|
/// Default: `Libdeflater`
|
||||||
|
pub deflate: Deflaters,
|
||||||
|
/// Whether to use fast evaluation to pick the best filter
|
||||||
|
///
|
||||||
|
/// Default: `true`
|
||||||
|
pub fast_evaluation: bool,
|
||||||
|
|
||||||
|
/// Maximum amount of time to spend on optimizations.
|
||||||
|
/// Further potential optimizations are skipped if the timeout is exceeded.
|
||||||
|
pub timeout: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
pub fn from_preset(level: u8) -> Options {
|
||||||
|
let opts = Options::default();
|
||||||
|
match level {
|
||||||
|
0 => opts.apply_preset_0(),
|
||||||
|
1 => opts.apply_preset_1(),
|
||||||
|
2 => opts.apply_preset_2(),
|
||||||
|
3 => opts.apply_preset_3(),
|
||||||
|
4 => opts.apply_preset_4(),
|
||||||
|
5 => opts.apply_preset_5(),
|
||||||
|
6 => opts.apply_preset_6(),
|
||||||
|
_ => {
|
||||||
|
warn!("Level 7 and above don't exist yet and are identical to level 6");
|
||||||
|
opts.apply_preset_6()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_compression() -> Options {
|
||||||
|
Options::from_preset(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following methods make assumptions that they are operating
|
||||||
|
// on an `Options` struct generated by the `default` method.
|
||||||
|
fn apply_preset_0(mut self) -> Self {
|
||||||
|
self.filter.clear();
|
||||||
|
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||||
|
*compression = 5;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_preset_1(mut self) -> Self {
|
||||||
|
self.filter.clear();
|
||||||
|
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||||
|
*compression = 10;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_preset_2(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_preset_3(mut self) -> Self {
|
||||||
|
self.fast_evaluation = false;
|
||||||
|
self.filter = indexset! {
|
||||||
|
RowFilter::None,
|
||||||
|
RowFilter::Bigrams,
|
||||||
|
RowFilter::BigEnt,
|
||||||
|
RowFilter::Brute
|
||||||
|
};
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_preset_4(mut self) -> Self {
|
||||||
|
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||||
|
*compression = 12;
|
||||||
|
}
|
||||||
|
self.apply_preset_3()
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if let Deflaters::Libdeflater { compression } = &mut self.deflate {
|
||||||
|
*compression = 12;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_preset_6(mut self) -> Self {
|
||||||
|
self.filter.insert(RowFilter::Average);
|
||||||
|
self.filter.insert(RowFilter::Paeth);
|
||||||
|
self.apply_preset_5()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Options {
|
||||||
|
fn default() -> Options {
|
||||||
|
// Default settings based on -o 2 from the CLI interface
|
||||||
|
Options {
|
||||||
|
fix_errors: false,
|
||||||
|
force: false,
|
||||||
|
filter: indexset! {RowFilter::None, RowFilter::Sub, RowFilter::Entropy, RowFilter::Bigrams},
|
||||||
|
interlace: Some(Interlacing::None),
|
||||||
|
optimize_alpha: false,
|
||||||
|
bit_depth_reduction: true,
|
||||||
|
color_type_reduction: true,
|
||||||
|
palette_reduction: true,
|
||||||
|
grayscale_reduction: true,
|
||||||
|
idat_recoding: true,
|
||||||
|
scale_16: false,
|
||||||
|
strip: StripChunks::None,
|
||||||
|
deflate: Deflaters::Libdeflater { compression: 11 },
|
||||||
|
fast_evaluation: true,
|
||||||
|
timeout: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue