1extern crate alloc;
2
3use std::fs;
4use std::fs::File;
5use std::io::Write;
6
7use crate::parameters::TiffCompression::{Deflate, Lzw, Packbits};
8use crate::parameters::{CSParameters, TiffDeflateLevel};
9use crate::utils::{get_filetype_from_memory, get_filetype_from_path};
10use error::CaesiumError;
11
12mod convert;
13pub mod error;
14#[cfg(feature = "gif")]
15mod gif;
16mod interface;
17#[cfg(feature = "jpg")]
18mod jpeg;
19pub mod parameters;
20#[cfg(feature = "png")]
21mod png;
22mod resize;
23#[cfg(feature = "tiff")]
24mod tiff;
25mod utils;
26#[cfg(feature = "webp")]
27mod webp;
28
29pub fn compress(input_path: String, output_path: String, parameters: &CSParameters) -> error::Result<()> {
41 validate_parameters(parameters)?;
42 let file_type = get_filetype_from_path(&input_path);
43
44 match file_type {
45 #[cfg(feature = "jpg")]
46 SupportedFileTypes::Jpeg => {
47 jpeg::compress(input_path, output_path, parameters)?;
48 }
49 #[cfg(feature = "png")]
50 SupportedFileTypes::Png => {
51 png::compress(input_path, output_path, parameters)?;
52 }
53 #[cfg(feature = "webp")]
54 SupportedFileTypes::WebP => {
55 webp::compress(input_path, output_path, parameters)?;
56 }
57 #[cfg(feature = "gif")]
58 SupportedFileTypes::Gif => {
59 gif::compress(input_path, output_path, parameters)?;
60 }
61 #[cfg(feature = "tiff")]
62 SupportedFileTypes::Tiff => {
63 tiff::compress(input_path, output_path, parameters)?;
64 }
65 _ => {
66 return Err(CaesiumError {
67 message: "Unknown file type or file not found".into(),
68 code: 10000,
69 });
70 }
71 }
72
73 Ok(())
74}
75
76pub fn compress_in_memory(in_file: Vec<u8>, parameters: &CSParameters) -> error::Result<Vec<u8>> {
87 let file_type = get_filetype_from_memory(in_file.as_slice());
88 let compressed_file = match file_type {
89 #[cfg(feature = "jpg")]
90 SupportedFileTypes::Jpeg => jpeg::compress_in_memory(in_file, parameters)?,
91 #[cfg(feature = "png")]
92 SupportedFileTypes::Png => png::compress_in_memory(in_file, parameters)?,
93 #[cfg(feature = "webp")]
94 SupportedFileTypes::WebP => webp::compress_in_memory(in_file, parameters)?,
95 #[cfg(feature = "tiff")]
96 SupportedFileTypes::Tiff => tiff::compress_in_memory(in_file, parameters)?,
97 _ => {
98 return Err(CaesiumError {
99 message: "Format not supported for compression in memory".into(),
100 code: 10200,
101 });
102 }
103 };
104
105 Ok(compressed_file)
106}
107
108pub fn compress_to_size_in_memory(
121 in_file: Vec<u8>,
122 parameters: &mut CSParameters,
123 max_output_size: usize,
124 return_smallest: bool,
125) -> error::Result<Vec<u8>> {
126 let file_type = get_filetype_from_memory(&in_file);
127
128 let tolerance_percentage = 2;
129 let tolerance = max_output_size * tolerance_percentage / 100;
130 let mut quality = 80;
131 let mut last_less = 0;
132 let mut last_high = 101;
133 let max_tries: u32 = 10;
134 let mut tries: u32 = 0;
135
136 let compressed_file = match file_type {
137 #[cfg(feature = "tiff")]
138 SupportedFileTypes::Tiff => {
139 let algorithms = [Lzw, Packbits];
140 parameters.tiff.deflate_level = TiffDeflateLevel::Best;
141 parameters.tiff.algorithm = Deflate;
142 let mut smallest_result = tiff::compress_in_memory(in_file.clone(), parameters)?; for tc in algorithms {
144 parameters.tiff.algorithm = tc;
145 let result = tiff::compress_in_memory(in_file.clone(), parameters)?; if result.len() < smallest_result.len() {
147 smallest_result = result;
148 }
149 }
150 return if return_smallest || smallest_result.len() <= max_output_size {
151 Ok(smallest_result)
152 } else {
153 Err(CaesiumError {
154 message: "Cannot compress to desired quality".into(),
155 code: 10202,
156 })
157 };
158 }
159 _ => loop {
160 if tries >= max_tries {
161 return Err(CaesiumError {
162 message: "Max tries reached".into(),
163 code: 10201,
164 });
165 }
166
167 let compressed_file = match file_type {
168 #[cfg(feature = "jpg")]
169 SupportedFileTypes::Jpeg => {
170 parameters.jpeg.quality = quality;
171 jpeg::compress_in_memory(in_file.clone(), parameters)? }
173 #[cfg(feature = "png")]
174 SupportedFileTypes::Png => {
175 parameters.png.quality = quality;
176 png::compress_in_memory(in_file.clone(), parameters)? }
178 #[cfg(feature = "webp")]
179 SupportedFileTypes::WebP => {
180 parameters.webp.quality = quality;
181 webp::compress_in_memory(in_file.clone(), parameters)? }
183 _ => {
184 return Err(CaesiumError {
185 message: "Format not supported for compression to size".into(),
186 code: 10200,
187 });
188 }
189 };
190
191 let compressed_file_size = compressed_file.len();
192
193 if compressed_file_size <= max_output_size && max_output_size - compressed_file_size < tolerance {
194 break compressed_file;
195 }
196
197 if compressed_file_size <= max_output_size {
198 last_less = quality;
199 } else {
200 last_high = quality;
201 }
202 let last_quality = quality;
203 quality = ((last_high + last_less) / 2).clamp(1, 100);
204 if last_quality == quality {
205 if quality == 1 && last_high == 1 {
206 return if return_smallest {
207 Ok(compressed_file)
208 } else {
209 Err(CaesiumError {
210 message: "Cannot compress to desired quality".into(),
211 code: 10202,
212 })
213 };
214 }
215
216 break compressed_file;
217 }
218
219 tries += 1;
220 },
221 };
222
223 Ok(compressed_file)
224}
225
226pub fn compress_to_size(
240 input_path: String,
241 output_path: String,
242 parameters: &mut CSParameters,
243 max_output_size: usize,
244 return_smallest: bool,
245) -> error::Result<()> {
246 let in_file = fs::read(input_path.clone()).map_err(|e| CaesiumError {
247 message: e.to_string(),
248 code: 10201,
249 })?;
250 let original_size = in_file.len();
251
252 if !(parameters.width > 0 || parameters.height > 0) && original_size <= max_output_size {
254 fs::copy(input_path, output_path).map_err(|e| CaesiumError {
255 message: e.to_string(),
256 code: 10202,
257 })?;
258 return Ok(());
259 }
260
261 let compressed_file = compress_to_size_in_memory(in_file, parameters, max_output_size, return_smallest)?;
262 let mut out_file = File::create(output_path).map_err(|e| CaesiumError {
263 message: e.to_string(),
264 code: 10203,
265 })?;
266 out_file.write_all(&compressed_file).map_err(|e| CaesiumError {
267 message: e.to_string(),
268 code: 10204,
269 })?;
270
271 Ok(())
272}
273
274pub fn convert(
287 input_path: String,
288 output_path: String,
289 parameters: &CSParameters,
290 format: SupportedFileTypes,
291) -> error::Result<()> {
292 let file_type = get_filetype_from_path(&input_path);
293
294 if file_type == format {
295 return Err(CaesiumError {
296 message: "Cannot convert to the same format".into(),
297 code: 10406,
298 });
299 }
300
301 let in_file = fs::read(input_path).map_err(|e| CaesiumError {
302 message: e.to_string(),
303 code: 10410,
304 })?;
305 let output_buffer = convert_in_memory(in_file, parameters, format).map_err(|e| CaesiumError {
306 message: e.to_string(),
307 code: 10411,
308 })?;
309
310 let mut out_file = File::create(output_path).map_err(|e| CaesiumError {
311 message: e.to_string(),
312 code: 10412,
313 })?;
314
315 out_file.write_all(&output_buffer).map_err(|e| CaesiumError {
316 message: e.to_string(),
317 code: 10413,
318 })?;
319
320 Ok(())
321}
322
323pub fn convert_in_memory(
335 in_file: Vec<u8>,
336 parameters: &CSParameters,
337 format: SupportedFileTypes,
338) -> Result<Vec<u8>, CaesiumError> {
339 convert::convert_in_memory(in_file, format, parameters)
340}
341
342fn validate_parameters(parameters: &CSParameters) -> error::Result<()> {
343 if parameters.jpeg.quality > 100 {
344 return Err(CaesiumError {
345 message: "Invalid JPEG quality value".into(),
346 code: 10001,
347 });
348 }
349
350 if parameters.png.quality > 100 {
351 return Err(CaesiumError {
352 message: "Invalid PNG quality value".into(),
353 code: 10002,
354 });
355 }
356
357 if parameters.png.optimization_level > 6 {
358 return Err(CaesiumError {
359 message: "Invalid PNG optimization level".into(),
360 code: 10006,
361 });
362 }
363
364 if parameters.gif.quality > 100 {
365 return Err(CaesiumError {
366 message: "Invalid GIF quality value".into(),
367 code: 10003,
368 });
369 }
370
371 if parameters.webp.quality > 100 {
372 return Err(CaesiumError {
373 message: "Invalid WebP quality value".into(),
374 code: 10004,
375 });
376 }
377
378 Ok(())
379}
380
381#[repr(C)]
382#[derive(PartialEq, Eq, Clone, Copy)]
383pub enum SupportedFileTypes {
384 Jpeg,
385 Png,
386 Gif,
387 WebP,
388 Tiff,
389 Unkn,
390}