diff --git a/.idea/markdown.xml b/.idea/markdown.xml new file mode 100644 index 0000000..e6da414 --- /dev/null +++ b/.idea/markdown.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0c7fded..0c99857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "libcaesium" -version = "0.7.0" +version = "0.8.0" authors = ["Matteo Paonessa "] -edition = "2018" +edition = "2021" categories = ["multimedia::images"] keywords = [ "compression", @@ -24,17 +24,17 @@ license = "Apache-2.0" [dependencies] mozjpeg-sys = "1.0.1" oxipng = "5.0.1" -libc = "0.2.76" +libc = "0.2.119" gifsicle = "1.92.5" webp = "0.2.1" -infer = "0.6.0" +infer = "0.7.0" image = { version = "0.24", default-features = false, features = ["jpeg", "png", "webp", "gif"] } img-parts = "0.2.3" bytes = "1.1.0" [dev-dependencies] dssim = "3.2.0" -kamadak-exif = "0.5.0" +kamadak-exif = "0.5.4" [lib] name = "caesium" diff --git a/README.md b/README.md index e948115..412b91f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ pub fn compress( - `output_path` - output file path (full filename) - `parameters` - options struct, containing compression parameters (see below) -The output folder where the file is compressed **must** exist. +NOTE: The output folder where the file is compressed **must** exist. ### Compression options Libcaesium supports a few compression parameters for each file it supports. They are defined into a top level struct containing each supported file parameters, as follows: @@ -57,7 +57,7 @@ pub struct Parameters { ``` - `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`. -- `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`. +- `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 GIF support is experimental, has many know issues and does not support optimization. Expect bugs (especially on Windows). @@ -69,7 +69,7 @@ pub struct Parameters { - `quality`: in a range from 0 to 100, the quality of the resulting image. If the optimization flag is `true`, the level is set to `100`. Default: `80`. #### webp -WebP compression is tricky. The format is already well optimized and using the `optimize` flag will probably result in a bigger image. +WebP's compression is tricky. The format is already well optimized and using the `optimize` flag will probably result in a bigger image. ```Rust pub struct Parameters { pub quality: u32, @@ -84,14 +84,23 @@ pub extern fn c_compress( input_path: *const c_char, output_path: *const c_char, params: CCSParameters -) -> bool +) -> CCSResult ``` #### Parameters - `input_path` - input file path (full filename) - `output_path` - output file path (full filename) - `parameters` - options struct, containing compression parameters (see below) #### Return -`true` if all goes well, `false` otherwise. +A `CCSResult` struct +```Rust +#[repr(C)] +pub struct CCSResult { + pub success: bool, + pub error_message: *const c_char, +} +``` +If `success` is `true` the compression process ended successfully and `error_message` will be empty. +On failure, the `error_message` will be filled with a string containing a brief explanation of the error. ### Compression options The C options struct is slightly different from the Rust one: @@ -131,4 +140,4 @@ Libcaesium also supports optimization, by setting the _quality_ to 0. This perfo but with a smaller size (10-12% usually). PNG is lossless, so libcaesium will always perform optimization rather than compression. GIF optimization is possible, but currently not supported. -WebP optimization is also possible, but it will probably result in a bigger output file as it's well suited to losslessly convert from PNG or JPEG.s +WebP's optimization is also possible, but it will probably result in a bigger output file as it's well suited to losslessly convert from PNG or JPEG.s diff --git a/src/jpeg.rs b/src/jpeg.rs index a26e73a..8e0c552 100644 --- a/src/jpeg.rs +++ b/src/jpeg.rs @@ -16,6 +16,7 @@ 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()); @@ -27,12 +28,11 @@ pub fn compress(input_path: String, output_path: String, parameters: CSParameter } unsafe { - let compression_buffer: (*mut u8, u64); - if parameters.optimize { - compression_buffer = lossless(in_file, parameters)?; + let compression_buffer: (*mut u8, u64) = if parameters.optimize { + lossless(in_file, parameters)? } else { - compression_buffer = lossy(in_file, parameters)?; - } + lossy(in_file, parameters)? + }; let mut output_file_buffer = File::create(output_path)?; output_file_buffer.write_all(std::slice::from_raw_parts(compression_buffer.0, compression_buffer.1 as usize))?; } diff --git a/src/lib.rs b/src/lib.rs index b36b1b1..41a8352 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ mod resize; use std::error::Error; use crate::utils::get_filetype; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::os::raw::c_char; #[repr(C)] @@ -23,6 +23,12 @@ pub struct CCSParameters { pub height: u32, } +#[repr(C)] +pub struct CCSResult { + pub success: bool, + pub error_message: *const c_char, +} + pub struct CSParameters { pub jpeg: jpeg::Parameters, pub png: png::Parameters, @@ -68,8 +74,9 @@ pub fn initialize_parameters() -> CSParameters #[no_mangle] #[allow(clippy::missing_safety_doc)] -pub unsafe extern fn c_compress(input_path: *const c_char, output_path: *const c_char, params: CCSParameters) -> bool { +pub unsafe extern fn c_compress(input_path: *const c_char, output_path: *const c_char, params: CCSParameters) -> CCSResult { let mut parameters = initialize_parameters(); + parameters.jpeg.quality = params.jpeg_quality; parameters.png.level = params.png_level; parameters.optimize = params.optimize; @@ -80,10 +87,29 @@ pub unsafe extern fn c_compress(input_path: *const c_char, output_path: *const c parameters.width = params.width; parameters.height = params.height; - let x = compress(CStr::from_ptr(input_path).to_str().unwrap().to_string(), - CStr::from_ptr(output_path).to_str().unwrap().to_string(), - parameters).is_ok(); - x + let mut error_message = CString::new("").unwrap(); + + match compress(CStr::from_ptr(input_path).to_str().unwrap().to_string(), + CStr::from_ptr(output_path).to_str().unwrap().to_string(), + parameters) { + Ok(_) => { + let em_pointer = error_message.as_ptr(); + std::mem::forget(error_message); + CCSResult { + success: true, + error_message: em_pointer + } + } + Err(e) => { + error_message = CString::new(e.to_string()).unwrap(); + let em_pointer = error_message.as_ptr(); + std::mem::forget(error_message); + CCSResult { + success: false, + error_message: em_pointer + } + } + } } pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), Box> { @@ -92,43 +118,21 @@ pub fn compress(input_path: String, output_path: String, parameters: CSParameter match file_type { utils::SupportedFileTypes::Jpeg => { - return match jpeg::compress(input_path, output_path, parameters) { - Ok(_) => Ok(()), - Err(e) => { - eprintln!("JPEG compression error: {}", e.to_string()); - Err(e.into()) - } - }; + jpeg::compress(input_path, output_path, parameters)?; } utils::SupportedFileTypes::Png => { - return match png::compress(input_path, output_path, parameters) { - Ok(_) => Ok(()), - Err(e) => { - eprintln!("PNG compression error: {}", e.to_string()); - Err(e.into()) - } - }; + png::compress(input_path, output_path, parameters)?; } utils::SupportedFileTypes::Gif => { - return match gif::compress(input_path, output_path, parameters) { - Ok(_) => Ok(()), - Err(e) => { - eprintln!("GIF compression error: {}", e.to_string()); - Err(e.into()) - } - }; + gif::compress(input_path, output_path, parameters)?; } utils::SupportedFileTypes::WebP => { - return match webp::compress(input_path, output_path, parameters) { - Ok(_) => Ok(()), - Err(e) => { - eprintln!("WebP compression error: {}", e.to_string()); - Err(e.into()) - } - }; + webp::compress(input_path, output_path, parameters)?; } _ => return Err("Unknown file type".into()) } + + Ok(()) } fn validate_parameters(parameters: &CSParameters) -> Result<(), Box> { diff --git a/tests/integration.rs b/tests/integration.rs index 5b2cd7c..b70d469 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,5 +1,3 @@ -use caesium; - #[test] fn unknown_file_type() { let output = "tests/samples/output/should_not_be_there"; diff --git a/tests/webp.rs b/tests/webp.rs index d8ab630..b446287 100644 --- a/tests/webp.rs +++ b/tests/webp.rs @@ -1,4 +1,3 @@ -use caesium; use std::sync::Once; use std::fs;