caesium/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
extern crate alloc;
use std::fs;
use std::fs::File;
use std::io::Write;
use error::CaesiumError;
use crate::parameters::{CSParameters, TiffDeflateLevel};
use crate::parameters::TiffCompression::{Deflate, Lzw, Packbits};
use crate::utils::{get_filetype_from_memory, get_filetype_from_path};
pub mod error;
#[cfg(feature = "gif")]
mod gif;
mod interface;
#[cfg(feature = "jpg")]
mod jpeg;
#[cfg(feature = "png")]
mod png;
mod resize;
#[cfg(feature = "tiff")]
mod tiff;
mod utils;
#[cfg(feature = "webp")]
mod webp;
mod convert;
pub mod parameters;
/// Compresses an image file from the input path and writes the compressed image to the output path.
///
/// # Arguments
///
/// * `input_path` - A string representing the path to the input image file.
/// * `output_path` - A string representing the path to the output compressed image file.
/// * `parameters` - A reference to `CSParameters` containing compression settings.
///
/// # Returns
///
/// * `Result<(), CaesiumError>` - Returns `Ok(())` if compression is successful, otherwise returns a `CaesiumError`.
pub fn compress(
input_path: String,
output_path: String,
parameters: &CSParameters,
) -> error::Result<()> {
validate_parameters(parameters)?;
let file_type = get_filetype_from_path(&input_path);
match file_type {
#[cfg(feature = "jpg")]
SupportedFileTypes::Jpeg => {
jpeg::compress(input_path, output_path, parameters)?;
}
#[cfg(feature = "png")]
SupportedFileTypes::Png => {
png::compress(input_path, output_path, parameters)?;
}
#[cfg(feature = "webp")]
SupportedFileTypes::WebP => {
webp::compress(input_path, output_path, parameters)?;
}
#[cfg(feature = "gif")]
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 or file not found".into(),
code: 10000,
});
}
}
Ok(())
}
/// Compresses an image file in memory and returns the compressed image as a byte vector.
///
/// # Arguments
///
/// * `in_file` - A vector of bytes representing the input image file.
/// * `parameters` - A reference to `CSParameters` containing compression settings.
///
/// # Returns
///
/// * `Result<Vec<u8>, CaesiumError>` - Returns a vector of bytes representing the compressed image if successful, otherwise returns a `CaesiumError`.
pub fn compress_in_memory(
in_file: Vec<u8>,
parameters: &CSParameters,
) -> error::Result<Vec<u8>> {
let file_type = get_filetype_from_memory(in_file.as_slice());
let compressed_file = match file_type {
#[cfg(feature = "jpg")]
SupportedFileTypes::Jpeg => jpeg::compress_in_memory(in_file, parameters)?,
#[cfg(feature = "png")]
SupportedFileTypes::Png => png::compress_in_memory(in_file, parameters)?,
#[cfg(feature = "webp")]
SupportedFileTypes::WebP => webp::compress_in_memory(in_file, parameters)?,
#[cfg(feature = "tiff")]
SupportedFileTypes::Tiff => tiff::compress_in_memory(in_file, parameters)?,
_ => {
return Err(CaesiumError {
message: "Format not supported for compression in memory".into(),
code: 10200,
});
}
};
Ok(compressed_file)
}
/// Compresses an image file in memory up to a specified size and returns the compressed image as a byte vector.
///
/// # Arguments
///
/// * `in_file` - A vector of bytes representing the input image file.
/// * `parameters` - A mutable reference to `CSParameters` containing compression settings.
/// * `max_output_size` - The maximum size of the output compressed image in bytes.
/// * `return_smallest` - A boolean indicating whether to return the smallest compressed image if the desired size is not achieved.
///
/// # Returns
///
/// * `Result<Vec<u8>, CaesiumError>` - Returns a vector of bytes representing the compressed image if successful, otherwise returns a `CaesiumError`.
pub fn compress_to_size_in_memory(
in_file: Vec<u8>,
parameters: &mut CSParameters,
max_output_size: usize,
return_smallest: bool,
) -> error::Result<Vec<u8>> {
let file_type = get_filetype_from_memory(&in_file);
let tolerance_percentage = 2;
let tolerance = max_output_size * tolerance_percentage / 100;
let mut quality = 80;
let mut last_less = 1;
let mut last_high = 101;
let max_tries: u32 = 10;
let mut tries: u32 = 0;
let compressed_file = match file_type {
#[cfg(feature = "tiff")]
SupportedFileTypes::Tiff => {
let algorithms = [
Lzw,
Packbits
];
parameters.tiff.deflate_level = TiffDeflateLevel::Best;
parameters.tiff.algorithm = Deflate;
let mut smallest_result = tiff::compress_in_memory(in_file.clone(), parameters)?; //TODO clone
for tc in algorithms {
parameters.tiff.algorithm = tc;
let result = tiff::compress_in_memory(in_file.clone(), parameters)?; //TODO clone
if result.len() < smallest_result.len() {
smallest_result = result;
}
}
return if return_smallest {
Ok(smallest_result)
} else {
Err(CaesiumError {
message: "Cannot compress to desired quality".into(),
code: 10202,
})
};
}
_ => loop {
if tries >= max_tries {
return Err(CaesiumError {
message: "Max tries reached".into(),
code: 10201,
});
}
let compressed_file = match file_type {
#[cfg(feature = "jpg")]
SupportedFileTypes::Jpeg => {
parameters.jpeg.quality = quality;
jpeg::compress_in_memory(in_file.clone(), parameters)? //TODO clone
}
#[cfg(feature = "png")]
SupportedFileTypes::Png => {
parameters.png.quality = quality;
png::compress_in_memory(in_file.clone(), parameters)? //TODO clone
}
#[cfg(feature = "webp")]
SupportedFileTypes::WebP => {
parameters.webp.quality = quality;
webp::compress_in_memory(in_file.clone(), parameters)? //TODO clone
}
_ => {
return Err(CaesiumError {
message: "Format not supported for compression to size".into(),
code: 10200,
});
}
};
let compressed_file_size = compressed_file.len();
if compressed_file_size <= max_output_size
&& max_output_size - compressed_file_size < tolerance
{
break compressed_file;
}
if compressed_file_size <= max_output_size {
last_less = quality;
} else {
last_high = quality;
}
let last_quality = quality;
quality = ((last_high + last_less) / 2).clamp(1, 100);
if last_quality == quality {
if quality == 1 && last_high == 1 {
return if return_smallest {
Ok(compressed_file)
} else {
Err(CaesiumError {
message: "Cannot compress to desired quality".into(),
code: 10202,
})
};
}
break compressed_file;
}
tries += 1;
},
};
Ok(compressed_file)
}
/// Compresses an image file from the input path up to a specified size and writes the compressed image to the output path.
///
/// # Arguments
///
/// * `input_path` - A string representing the path to the input image file.
/// * `output_path` - A string representing the path to the output compressed image file.
/// * `parameters` - A mutable reference to `CSParameters` containing compression settings.
/// * `max_output_size` - The maximum size of the output compressed image in bytes.
/// * `return_smallest` - A boolean indicating whether to return the smallest compressed image if the desired size is not achieved.
///
/// # Returns
///
/// * `Result<(), CaesiumError>` - Returns `Ok(())` if compression is successful, otherwise returns a `CaesiumError`.
pub fn compress_to_size(
input_path: String,
output_path: String,
parameters: &mut CSParameters,
max_output_size: usize,
return_smallest: bool,
) -> error::Result<()> {
let in_file = fs::read(input_path.clone()).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10201,
})?;
let original_size = in_file.len();
if original_size <= max_output_size {
fs::copy(input_path, output_path).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10202,
})?;
return Ok(());
}
let compressed_file =
compress_to_size_in_memory(in_file, parameters, max_output_size, return_smallest)?;
let mut out_file = File::create(output_path).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10203,
})?;
out_file
.write_all(&compressed_file)
.map_err(|e| CaesiumError {
message: e.to_string(),
code: 10204,
})?;
Ok(())
}
/// Converts an image file from the input path to a specified format and writes the converted image to the output path.
///
/// # Arguments
///
/// * `input_path` - A string representing the path to the input image file.
/// * `output_path` - A string representing the path to the output converted image file.
/// * `parameters` - A reference to `CSParameters` containing conversion settings.
/// * `format` - The target format to convert the image to.
///
/// # Returns
///
/// * `Result<(), CaesiumError>` - Returns `Ok(())` if conversion is successful, otherwise returns a `CaesiumError`.
pub fn convert(input_path: String, output_path: String, parameters: &CSParameters, format: SupportedFileTypes) -> error::Result<()> {
let file_type = get_filetype_from_path(&input_path);
if file_type == format {
return Err(CaesiumError {
message: "Cannot convert to the same format".into(),
code: 10406,
});
}
let in_file = fs::read(input_path).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10410,
})?;
let output_buffer = convert_in_memory(in_file, parameters, format).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10411,
})?;
let mut out_file = File::create(output_path).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10412,
})?;
out_file.write_all(&output_buffer).map_err(|e| CaesiumError {
message: e.to_string(),
code: 10413,
})?;
Ok(())
}
/// Converts an image file in memory to a specified format and returns the converted image as a byte vector.
///
/// # Arguments
///
/// * `in_file` - A vector of bytes representing the input image file.
/// * `parameters` - A reference to `CSParameters` containing conversion settings.
/// * `format` - The target format to convert the image to.
///
/// # Returns
///
/// * `Result<Vec<u8>, CaesiumError>` - Returns a vector of bytes representing the converted image if successful, otherwise returns a `CaesiumError`.
pub fn convert_in_memory(in_file: Vec<u8>, parameters: &CSParameters, format: SupportedFileTypes) -> Result<Vec<u8>, CaesiumError> {
convert::convert_in_memory(in_file, format, parameters)
}
fn validate_parameters(parameters: &CSParameters) -> error::Result<()> {
if parameters.jpeg.quality > 100 {
return Err(CaesiumError {
message: "Invalid JPEG quality value".into(),
code: 10001,
});
}
if parameters.png.quality > 100 {
return Err(CaesiumError {
message: "Invalid PNG quality value".into(),
code: 10002,
});
}
if parameters.png.optimization_level > 6 {
return Err(CaesiumError {
message: "Invalid PNG optimization level".into(),
code: 10006,
});
}
if parameters.gif.quality > 100 {
return Err(CaesiumError {
message: "Invalid GIF quality value".into(),
code: 10003,
});
}
if parameters.webp.quality > 100 {
return Err(CaesiumError {
message: "Invalid WebP quality value".into(),
code: 10004,
});
}
Ok(())
}
#[repr(C)]
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum SupportedFileTypes {
Jpeg,
Png,
Gif,
WebP,
Tiff,
Unkn,
}