From ec485ce89b03f54b1a531f78df2be479c6c6ae49 Mon Sep 17 00:00:00 2001 From: Matteo Paonessa Date: Fri, 29 Jul 2022 12:19:00 +0200 Subject: [PATCH] pngquant for lossy compression --- Cargo.toml | 1 + README.md | 4 ++-- src/jpeg.rs | 1 - src/lib.rs | 8 ++++---- src/png.rs | 34 +++++++++++++++++++++------------- tests/png.rs | 2 +- 6 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f91d078..9ea99f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ infer = "0.8" image = { version = "0.24", default-features = false, features = ["jpeg", "png", "webp", "gif"] } img-parts = "0.2" bytes = "1.1" +imagequant = "4.0.0" [dev-dependencies] dssim = "3.2.0" diff --git a/README.md b/README.md index 412b91f..dfe680b 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,12 @@ pub struct Parameters { ```Rust pub struct Parameters { pub oxipng: oxipng::Options, - pub level: u32, + pub quality: u32, pub force_zopfli: bool } ``` - `oxipng`: oxipng options. Should be left as default unless you want to do something advanced. Refer to [oxipng](https://github.com/shssoichiro/oxipng) for documentation. -- `level`: level of optimization, from 1 to 7. Increasing the level will result in a smaller file, at the cost of computation time. If the optimization flag is `true`, the level is set to `6`. Default: `3`. +- `quality`: in a range from 1 to 100, the quality of the resulting image. Default `80`. - `force_zopfli`: if `optimization` is `true` and this option is also `true`, will use zopfli algorithm for compression, resulting in a smaller image, but it may take minutes to finish the process. Default `false`. #### gif diff --git a/src/jpeg.rs b/src/jpeg.rs index d0c98c2..2278d41 100644 --- a/src/jpeg.rs +++ b/src/jpeg.rs @@ -18,7 +18,6 @@ pub fn compress(input_path: String, output_path: String, parameters: CSParameter { let mut in_file = fs::read(input_path)?; - if parameters.width > 0 || parameters.height > 0 { if parameters.keep_metadata { let metadata = extract_metadata(in_file.clone()); diff --git a/src/lib.rs b/src/lib.rs index 41a8352..e1244f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ use std::os::raw::c_char; pub struct CCSParameters { pub keep_metadata: bool, pub jpeg_quality: u32, - pub png_level: u32, + pub png_quality: u32, pub png_force_zopfli: bool, pub gif_quality: u32, pub webp_quality: u32, @@ -48,7 +48,7 @@ pub fn initialize_parameters() -> CSParameters let png = png::Parameters { oxipng: oxipng::Options::default(), - level: 3, + quality: 80, force_zopfli: false, }; @@ -78,7 +78,7 @@ pub unsafe extern fn c_compress(input_path: *const c_char, output_path: *const c let mut parameters = initialize_parameters(); parameters.jpeg.quality = params.jpeg_quality; - parameters.png.level = params.png_level; + parameters.png.quality = params.png_quality; parameters.optimize = params.optimize; parameters.keep_metadata = params.keep_metadata; parameters.png.force_zopfli = params.png_force_zopfli; @@ -140,7 +140,7 @@ fn validate_parameters(parameters: &CSParameters) -> Result<(), Box> return Err("Invalid JPEG quality value".into()); } - if parameters.png.level == 0 || parameters.png.level > 7 { + if parameters.png.quality > 100 { return Err("Invalid PNG quality value".into()); } diff --git a/src/png.rs b/src/png.rs index cb775ae..84077d1 100644 --- a/src/png.rs +++ b/src/png.rs @@ -7,20 +7,35 @@ use crate::resize::resize; pub struct Parameters { pub oxipng: oxipng::Options, - pub level: u32, + pub quality: u32, pub force_zopfli: bool, } -pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error> { +pub fn compress (input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error> { let mut in_file = fs::read(input_path)?; if parameters.width > 0 || parameters.height > 0 { in_file = resize(in_file, parameters.width, parameters.height, Png)?; } - let mut oxipng_options = parameters.png.oxipng; - oxipng_options.deflate = oxipng::Deflaters::Libdeflater; + let optimized_png: Vec = if parameters.optimize { + lossless(in_file, parameters)? + } else { + lossy(in_file, parameters)? + }; + let mut output_file_buffer = File::create(output_path)?; + output_file_buffer.write_all(optimized_png.as_slice())?; + + Ok(()) +} + +pub fn lossy (in_file: Vec, parameters: CSParameters) -> Result, io::Error> { + lossless(in_file, parameters) +} + +pub fn lossless(in_file: Vec, parameters: CSParameters) -> Result, io::Error> { + let mut oxipng_options = parameters.png.oxipng; if !parameters.keep_metadata { oxipng_options.strip = oxipng::Headers::Safe; } @@ -28,11 +43,7 @@ pub fn compress(input_path: String, output_path: String, parameters: CSParameter if parameters.optimize && parameters.png.force_zopfli { oxipng_options.deflate = oxipng::Deflaters::Zopfli; } else { - let mut preset = parameters.png.level - 1; - if parameters.optimize { - preset = 6; - } - oxipng_options = oxipng::Options::from_preset(preset as u8); + oxipng_options = oxipng::Options::from_preset(6); } let optimized_png = match oxipng::optimize_from_memory(in_file.as_slice(), &oxipng_options) { @@ -40,8 +51,5 @@ pub fn compress(input_path: String, output_path: String, parameters: CSParameter Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)) }; - let mut output_file_buffer = File::create(output_path)?; - output_file_buffer.write_all(optimized_png.as_slice())?; - - Ok(()) + Ok(optimized_png) } \ No newline at end of file diff --git a/tests/png.rs b/tests/png.rs index 352ac8e..417d3d4 100644 --- a/tests/png.rs +++ b/tests/png.rs @@ -107,7 +107,7 @@ fn downscale_zopfli_compress_png() { let mut params = caesium::initialize_parameters(); params.width = 150; params.height = 150; - params.png.level = 3; + params.png.quality = 80; params.optimize = true; params.png.force_zopfli = true; caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.png"),