TIFF draft

This commit is contained in:
Matteo Paonessa 2024-02-13 20:17:05 +01:00
parent 09eb085bf3
commit 4c958aac64
12 changed files with 337 additions and 145 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "libcaesium"
version = "0.14.1"
version = "0.15.0"
authors = ["Matteo Paonessa <matteo.paonessa@gmail.com>"]
edition = "2021"
categories = ["multimedia::images"]
@ -21,11 +21,12 @@ repository = "https://github.com/Lymphatus/libcaesium"
license = "Apache-2.0"
[features]
default = ["jpg", "png", "webp", "gif", "parallel"]
default = ["jpg", "png", "webp", "gif", "tiff", "parallel"]
jpg = ["dep:mozjpeg-sys", "image/jpeg"]
png = ["dep:oxipng", "dep:lodepng", "dep:imagequant", "image/png"]
webp = ["dep:webp", "image/webp"]
gif = ["dep:gifsicle", "image/gif"]
tiff = ["dep:tiff", "image/tiff"]
parallel = ["oxipng?/parallel", "imagequant?/threads", "dssim/threads"]
[dependencies]
@ -39,7 +40,8 @@ image = { version = "0.24.8", default-features = false }
img-parts = "0.3"
bytes = "1.5"
lodepng = { version = "3.10", optional = true }
imagequant = {version = "4.3", optional = true, default-features = false}
imagequant = { version = "4.3", optional = true, default-features = false }
tiff = { version = "0.9", optional = true }
[dev-dependencies]
dssim = { version = "3.3", default-features = false, features = ["no-macos-vimage"] }

15
src/error.rs Normal file
View File

@ -0,0 +1,15 @@
use core::fmt;
pub type Result<T> = std::result::Result<T, CaesiumError>;
#[derive(Debug, Clone)]
pub struct CaesiumError {
pub message: String,
pub code: u32,
}
impl fmt::Display for CaesiumError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} [{}]", self.message, self.code)
}
}

View File

@ -1,7 +1,7 @@
use std::ffi::CString;
use std::os::raw::{c_int, c_void};
use crate::utils::CaesiumError;
use crate::error::CaesiumError;
use crate::CSParameters;
pub fn compress(

111
src/interface.rs Normal file
View File

@ -0,0 +1,111 @@
use crate::jpeg::ChromaSubsampling;
use crate::{compress, compress_to_size, error, initialize_parameters, CSParameters};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
#[repr(C)]
pub struct CCSParameters {
pub keep_metadata: bool,
pub jpeg_quality: u32,
pub jpeg_chroma_subsampling: u32,
pub png_quality: u32,
pub png_force_zopfli: bool,
pub gif_quality: u32,
pub webp_quality: u32,
pub optimize: bool,
pub width: u32,
pub height: u32,
}
#[repr(C)]
pub struct CCSResult {
pub success: bool,
pub code: u32,
pub error_message: *const c_char,
}
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn c_compress(
input_path: *const c_char,
output_path: *const c_char,
params: CCSParameters,
) -> CCSResult {
let parameters = c_set_parameters(params);
c_return_result(compress(
CStr::from_ptr(input_path).to_str().unwrap().to_string(),
CStr::from_ptr(output_path).to_str().unwrap().to_string(),
&parameters,
))
}
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn c_compress_to_size(
input_path: *const c_char,
output_path: *const c_char,
params: CCSParameters,
max_output_size: usize,
return_smallest: bool,
) -> CCSResult {
let mut parameters = c_set_parameters(params);
c_return_result(compress_to_size(
CStr::from_ptr(input_path).to_str().unwrap().to_string(),
CStr::from_ptr(output_path).to_str().unwrap().to_string(),
&mut parameters,
max_output_size,
return_smallest,
))
}
fn c_return_result(result: error::Result<()>) -> CCSResult {
let mut error_message = CString::new("").unwrap();
match result {
Ok(_) => {
let em_pointer = error_message.as_ptr();
std::mem::forget(error_message);
CCSResult {
success: true,
code: 0,
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,
code: e.code,
error_message: em_pointer,
}
}
}
}
fn c_set_parameters(params: CCSParameters) -> CSParameters {
let mut parameters = initialize_parameters();
parameters.jpeg.quality = params.jpeg_quality;
parameters.png.quality = params.png_quality;
parameters.optimize = params.optimize;
parameters.keep_metadata = params.keep_metadata;
parameters.png.force_zopfli = params.png_force_zopfli;
parameters.gif.quality = params.gif_quality;
parameters.webp.quality = params.webp_quality;
parameters.width = params.width;
parameters.height = params.height;
parameters.jpeg.chroma_subsampling = match params.jpeg_chroma_subsampling {
444 => ChromaSubsampling::CS444,
422 => ChromaSubsampling::CS422,
420 => ChromaSubsampling::CS420,
411 => ChromaSubsampling::CS411,
_ => ChromaSubsampling::Auto,
};
parameters
}

View File

@ -10,8 +10,8 @@ use img_parts::{ImageEXIF, ImageICC};
use libc::free;
use mozjpeg_sys::*;
use crate::error::CaesiumError;
use crate::resize::resize;
use crate::utils::CaesiumError;
use crate::CSParameters;
static mut JPEG_ERROR: c_int = 0;

View File

@ -1,47 +1,30 @@
extern crate alloc;
use crate::jpeg::ChromaSubsampling;
use crate::tiff::TiffCompression;
use crate::tiff::TiffCompression::Deflate;
use crate::utils::{get_filetype_from_memory, get_filetype_from_path, SupportedFileTypes};
use alloc::ffi::CString;
use std::ffi::CStr;
use ::tiff::encoder::compression::DeflateLevel;
use error::CaesiumError;
use std::fs::File;
use std::io::Write;
use std::os::raw::c_char;
use std::{cmp, fs};
use utils::CaesiumError;
mod error;
#[cfg(feature = "gif")]
mod gif;
mod interface;
#[cfg(feature = "jpg")]
pub mod jpeg;
#[cfg(feature = "png")]
mod png;
mod resize;
#[cfg(feature = "tiff")]
mod tiff;
mod utils;
#[cfg(feature = "webp")]
mod webp;
#[repr(C)]
pub struct CCSParameters {
pub keep_metadata: bool,
pub jpeg_quality: u32,
pub jpeg_chroma_subsampling: u32,
pub png_quality: u32,
pub png_force_zopfli: bool,
pub gif_quality: u32,
pub webp_quality: u32,
pub optimize: bool,
pub width: u32,
pub height: u32,
}
#[repr(C)]
pub struct CCSResult {
pub success: bool,
pub code: u32,
pub error_message: *const c_char,
}
#[derive(Copy, Clone)]
pub struct JpegParameters {
pub quality: u32,
@ -64,12 +47,19 @@ pub struct WebPParameters {
pub quality: u32,
}
#[derive(Copy, Clone)]
pub struct TiffParameters {
pub algorithm: TiffCompression,
pub deflate_level: DeflateLevel,
}
#[derive(Copy, Clone)]
pub struct CSParameters {
pub jpeg: JpegParameters,
pub png: PngParameters,
pub gif: GifParameters,
pub webp: WebPParameters,
pub tiff: TiffParameters,
pub keep_metadata: bool,
pub optimize: bool,
pub width: u32,
@ -82,21 +72,23 @@ pub fn initialize_parameters() -> CSParameters {
quality: 80,
chroma_subsampling: ChromaSubsampling::Auto,
};
let png = PngParameters {
quality: 80,
force_zopfli: false,
};
let gif = GifParameters { quality: 80 };
let webp = WebPParameters { quality: 80 };
let tiff = TiffParameters {
algorithm: Deflate,
deflate_level: DeflateLevel::Balanced,
};
CSParameters {
jpeg,
png,
gif,
webp,
tiff,
keep_metadata: false,
optimize: false,
width: 0,
@ -105,97 +97,11 @@ pub fn initialize_parameters() -> CSParameters {
}
}
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn c_compress(
input_path: *const c_char,
output_path: *const c_char,
params: CCSParameters,
) -> CCSResult {
let parameters = c_set_parameters(params);
c_return_result(compress(
CStr::from_ptr(input_path).to_str().unwrap().to_string(),
CStr::from_ptr(output_path).to_str().unwrap().to_string(),
&parameters,
))
}
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn c_compress_to_size(
input_path: *const c_char,
output_path: *const c_char,
params: CCSParameters,
max_output_size: usize,
return_smallest: bool,
) -> CCSResult {
let mut parameters = c_set_parameters(params);
c_return_result(compress_to_size(
CStr::from_ptr(input_path).to_str().unwrap().to_string(),
CStr::from_ptr(output_path).to_str().unwrap().to_string(),
&mut parameters,
max_output_size,
return_smallest,
))
}
fn c_return_result(result: utils::Result<()>) -> CCSResult {
let mut error_message = CString::new("").unwrap();
match result {
Ok(_) => {
let em_pointer = error_message.as_ptr();
std::mem::forget(error_message);
CCSResult {
success: true,
code: 0,
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,
code: e.code,
error_message: em_pointer,
}
}
}
}
fn c_set_parameters(params: CCSParameters) -> CSParameters {
let mut parameters = initialize_parameters();
parameters.jpeg.quality = params.jpeg_quality;
parameters.png.quality = params.png_quality;
parameters.optimize = params.optimize;
parameters.keep_metadata = params.keep_metadata;
parameters.png.force_zopfli = params.png_force_zopfli;
parameters.gif.quality = params.gif_quality;
parameters.webp.quality = params.webp_quality;
parameters.width = params.width;
parameters.height = params.height;
parameters.jpeg.chroma_subsampling = match params.jpeg_chroma_subsampling {
444 => ChromaSubsampling::CS444,
422 => ChromaSubsampling::CS422,
420 => ChromaSubsampling::CS420,
411 => ChromaSubsampling::CS411,
_ => ChromaSubsampling::Auto,
};
parameters
}
pub fn compress(
input_path: String,
output_path: String,
parameters: &CSParameters,
) -> utils::Result<()> {
) -> error::Result<()> {
validate_parameters(parameters)?;
let file_type = get_filetype_from_path(&input_path);
@ -216,11 +122,15 @@ pub fn compress(
SupportedFileTypes::Gif => {
gif::compress(input_path, output_path, parameters)?;
}
#[cfg(feature = "tiff")]
SupportedFileTypes::Tiff => {
tiff::compress(input_path, output_path, parameters)?;
}
_ => {
return Err(CaesiumError {
message: "Unknown file type".into(),
code: 10000,
})
});
}
}
@ -230,7 +140,7 @@ pub fn compress(
pub fn compress_in_memory(
in_file: Vec<u8>,
parameters: &mut CSParameters,
) -> utils::Result<Vec<u8>> {
) -> error::Result<Vec<u8>> {
let file_type = get_filetype_from_memory(in_file.as_slice());
let compressed_file = match file_type {
#[cfg(feature = "jpg")]
@ -239,11 +149,13 @@ pub fn compress_in_memory(
SupportedFileTypes::Png => png::compress_to_memory(in_file, parameters)?,
#[cfg(feature = "webp")]
SupportedFileTypes::WebP => webp::compress_to_memory(in_file, parameters)?,
#[cfg(feature = "tiff")]
SupportedFileTypes::Tiff => tiff::compress_to_memory(in_file, parameters)?,
_ => {
return Err(CaesiumError {
message: "Format not supported for compression to size".into(),
code: 10200,
})
});
}
};
@ -255,7 +167,7 @@ pub fn compress_to_size_in_memory(
parameters: &mut CSParameters,
max_output_size: usize,
return_smallest: bool,
) -> utils::Result<Vec<u8>> {
) -> error::Result<Vec<u8>> {
let file_type = get_filetype_from_memory(&in_file);
let tolerance_percentage = 2;
@ -290,11 +202,12 @@ pub fn compress_to_size_in_memory(
parameters.webp.quality = quality;
webp::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
//TODO Tiff
_ => {
return Err(CaesiumError {
message: "Format not supported for compression to size".into(),
code: 10200,
})
});
}
};
@ -340,7 +253,7 @@ pub fn compress_to_size(
parameters: &mut CSParameters,
max_output_size: usize,
return_smallest: bool,
) -> utils::Result<()> {
) -> error::Result<()> {
let in_file = fs::read(input_path.clone()).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10201,
@ -369,7 +282,7 @@ pub fn compress_to_size(
Ok(())
}
fn validate_parameters(parameters: &CSParameters) -> utils::Result<()> {
fn validate_parameters(parameters: &CSParameters) -> error::Result<()> {
if parameters.jpeg.quality == 0 || parameters.jpeg.quality > 100 {
return Err(CaesiumError {
message: "Invalid JPEG quality value".into(),

View File

@ -6,8 +6,8 @@ use std::num::NonZeroU8;
use image::ImageOutputFormat;
use oxipng::Deflaters::{Libdeflater, Zopfli};
use crate::error::CaesiumError;
use crate::resize::resize;
use crate::utils::CaesiumError;
use crate::CSParameters;
pub fn compress(

View File

@ -1,6 +1,6 @@
use std::io::Cursor;
use crate::utils::CaesiumError;
use crate::error::CaesiumError;
use image::imageops::FilterType;
use image::io::Reader as ImageReader;
use image::DynamicImage;

164
src/tiff.rs Normal file
View File

@ -0,0 +1,164 @@
use crate::error::CaesiumError;
use crate::CSParameters;
use image::ImageFormat::Tiff;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use tiff::encoder::colortype::{RGB8, RGBA8};
use tiff::encoder::compression::{Deflate, Lzw, Packbits, Uncompressed};
use tiff::encoder::TiffEncoder;
use crate::resize::resize_image;
#[derive(Copy, Clone, PartialEq)]
pub enum TiffCompression {
Lzw,
Deflate,
Packbits,
Uncompressed,
}
pub fn compress(
input_path: String,
output_path: String,
parameters: &CSParameters,
) -> Result<(), CaesiumError> {
let mut input_file = File::open(input_path).map_err(|e| CaesiumError {
message: e.to_string(),
code: 20500,
})?;
let mut input_data = Vec::new();
input_file
.read_to_end(&mut input_data)
.map_err(|e| CaesiumError {
message: e.to_string(),
code: 20501,
})?;
let mut output_file = File::create(output_path).map_err(|e| CaesiumError {
message: e.to_string(),
code: 20502,
})?;
let compressed_image = compress_to_memory(input_data, parameters)?;
output_file
.write_all(&compressed_image)
.map_err(|e| CaesiumError {
message: e.to_string(),
code: 20503,
})?;
Ok(())
}
pub fn compress_to_memory(
in_file: Vec<u8>,
parameters: &CSParameters,
) -> Result<Vec<u8>, CaesiumError> {
let mut image = image::load_from_memory_with_format(in_file.as_slice(), Tiff).map_err(|e| {
CaesiumError {
message: e.to_string(),
code: 20504,
}
})?;
if parameters.width > 0 || parameters.height > 0 {
image = resize_image(image, parameters.width, parameters.height);
}
let color_type = image.color();
let output_buff = vec![];
let mut output_stream = Cursor::new(output_buff);
let mut encoder = TiffEncoder::new(&mut output_stream).map_err(|e| CaesiumError {
message: e.to_string(),
code: 20505,
})?;
let compression_result = match parameters.tiff.algorithm {
TiffCompression::Deflate => match color_type {
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Deflate>(
image.width(),
image.height(),
Deflate::with_level(parameters.tiff.deflate_level),
image.as_bytes(),
),
image::ColorType::Rgba8 => encoder.write_image_with_compression::<RGBA8, Deflate>(
image.width(),
image.height(),
Deflate::with_level(parameters.tiff.deflate_level),
image.as_bytes(),
),
_ => {
return Err(CaesiumError {
message: format!("Unsupported TIFF color type ({:?})", color_type).to_string(),
code: 20506,
});
}
},
TiffCompression::Lzw => match color_type {
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Lzw>(
image.width(),
image.height(),
Lzw,
image.as_bytes(),
),
image::ColorType::Rgba8 => encoder.write_image_with_compression::<RGBA8, Lzw>(
image.width(),
image.height(),
Lzw,
image.as_bytes(),
),
_ => {
return Err(CaesiumError {
message: format!("Unsupported TIFF color type ({:?})", color_type).to_string(),
code: 20506,
});
}
},
TiffCompression::Packbits => match color_type {
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Packbits>(
image.width(),
image.height(),
Packbits,
image.as_bytes(),
),
image::ColorType::Rgba8 => encoder.write_image_with_compression::<RGBA8, Packbits>(
image.width(),
image.height(),
Packbits,
image.as_bytes(),
),
_ => {
return Err(CaesiumError {
message: format!("Unsupported TIFF color type ({:?})", color_type).to_string(),
code: 20506,
});
}
},
TiffCompression::Uncompressed => match color_type {
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Uncompressed>(
image.width(),
image.height(),
Uncompressed,
image.as_bytes(),
),
image::ColorType::Rgba8 => encoder.write_image_with_compression::<RGBA8, Uncompressed>(
image.width(),
image.height(),
Uncompressed,
image.as_bytes(),
),
_ => {
return Err(CaesiumError {
message: format!("Unsupported TIFF color type ({:?})", color_type).to_string(),
code: 20506,
});
}
},
};
match compression_result {
Ok(_) => Ok(output_stream.get_ref().to_vec()),
Err(e) => Err(CaesiumError {
message: e.to_string(),
code: 20507,
}),
}
}

View File

@ -1,4 +1,3 @@
use core::fmt;
use infer::Type;
pub enum SupportedFileTypes {
@ -6,6 +5,7 @@ pub enum SupportedFileTypes {
Png,
Gif,
WebP,
Tiff,
Unkn,
}
@ -32,20 +32,7 @@ fn match_supported_filetypes(ft: Type) -> SupportedFileTypes {
"image/png" => SupportedFileTypes::Png,
"image/gif" => SupportedFileTypes::Gif,
"image/webp" => SupportedFileTypes::WebP,
"image/tiff" => SupportedFileTypes::Tiff,
_ => SupportedFileTypes::Unkn,
}
}
pub type Result<T> = std::result::Result<T, CaesiumError>;
#[derive(Debug, Clone)]
pub struct CaesiumError {
pub message: String,
pub code: u32,
}
impl fmt::Display for CaesiumError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} [{}]", self.message, self.code)
}
}

View File

@ -2,8 +2,8 @@ use std::fs::File;
use std::io::{Read, Write};
use std::ops::Deref;
use crate::error::CaesiumError;
use crate::resize::resize_image;
use crate::utils::CaesiumError;
use crate::CSParameters;
pub fn compress(

Binary file not shown.