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 = "gif")]
94 SupportedFileTypes::Gif => gif::compress_in_memory(in_file, parameters)?,
95 #[cfg(feature = "webp")]
96 SupportedFileTypes::WebP => webp::compress_in_memory(in_file, parameters)?,
97 #[cfg(feature = "tiff")]
98 SupportedFileTypes::Tiff => tiff::compress_in_memory(in_file, parameters)?,
99 _ => {
100 return Err(CaesiumError {
101 message: "Format not supported for compression in memory".into(),
102 code: 10200,
103 });
104 }
105 };
106
107 Ok(compressed_file)
108}
109
110pub fn compress_to_size_in_memory(
123 in_file: Vec<u8>,
124 parameters: &mut CSParameters,
125 max_output_size: usize,
126 return_smallest: bool,
127) -> error::Result<Vec<u8>> {
128 let file_type = get_filetype_from_memory(&in_file);
129
130 let tolerance_percentage = 2;
131 let tolerance = max_output_size * tolerance_percentage / 100;
132 let mut quality = 80;
133 let mut last_less = 0;
134 let mut last_high = 101;
135 let max_tries: u32 = 10;
136 let mut tries: u32 = 0;
137
138 let compressed_file = match file_type {
139 #[cfg(feature = "tiff")]
140 SupportedFileTypes::Tiff => {
141 let algorithms = [Lzw, Packbits];
142 parameters.tiff.deflate_level = TiffDeflateLevel::Best;
143 parameters.tiff.algorithm = Deflate;
144 let mut smallest_result = tiff::compress_in_memory(in_file.clone(), parameters)?; for tc in algorithms {
146 parameters.tiff.algorithm = tc;
147 let result = tiff::compress_in_memory(in_file.clone(), parameters)?; if result.len() < smallest_result.len() {
149 smallest_result = result;
150 }
151 }
152 return if return_smallest || smallest_result.len() <= max_output_size {
153 Ok(smallest_result)
154 } else {
155 Err(CaesiumError {
156 message: "Cannot compress to desired quality".into(),
157 code: 10202,
158 })
159 };
160 }
161 _ => loop {
162 if tries >= max_tries {
163 return Err(CaesiumError {
164 message: "Max tries reached".into(),
165 code: 10201,
166 });
167 }
168
169 let compressed_file = match file_type {
170 #[cfg(feature = "jpg")]
171 SupportedFileTypes::Jpeg => {
172 parameters.jpeg.quality = quality;
173 jpeg::compress_in_memory(in_file.clone(), parameters)? }
175 #[cfg(feature = "png")]
176 SupportedFileTypes::Png => {
177 parameters.png.quality = quality;
178 png::compress_in_memory(in_file.clone(), parameters)? }
180 #[cfg(feature = "gif")]
181 SupportedFileTypes::Gif => {
182 parameters.gif.quality = quality;
183 gif::compress_in_memory(in_file.clone(), parameters)? }
185 #[cfg(feature = "webp")]
186 SupportedFileTypes::WebP => {
187 parameters.webp.quality = quality;
188 webp::compress_in_memory(in_file.clone(), parameters)? }
190 _ => {
191 return Err(CaesiumError {
192 message: "Format not supported for compression to size".into(),
193 code: 10200,
194 });
195 }
196 };
197
198 let compressed_file_size = compressed_file.len();
199
200 if compressed_file_size <= max_output_size && max_output_size - compressed_file_size < tolerance {
201 break compressed_file;
202 }
203
204 if compressed_file_size <= max_output_size {
205 last_less = quality;
206 } else {
207 last_high = quality;
208 }
209 let last_quality = quality;
210 quality = ((last_high + last_less) / 2).clamp(1, 100);
211 if last_quality == quality {
212 if quality == 1 && last_high == 1 {
213 return if return_smallest {
214 Ok(compressed_file)
215 } else {
216 Err(CaesiumError {
217 message: "Cannot compress to desired quality".into(),
218 code: 10202,
219 })
220 };
221 }
222
223 break compressed_file;
224 }
225
226 tries += 1;
227 },
228 };
229
230 Ok(compressed_file)
231}
232
233pub fn compress_to_size(
247 input_path: String,
248 output_path: String,
249 parameters: &mut CSParameters,
250 max_output_size: usize,
251 return_smallest: bool,
252) -> error::Result<()> {
253 let in_file = fs::read(input_path.clone()).map_err(|e| CaesiumError {
254 message: e.to_string(),
255 code: 10201,
256 })?;
257 let original_size = in_file.len();
258
259 if !(parameters.width > 0 || parameters.height > 0) && original_size <= max_output_size {
261 fs::copy(input_path, output_path).map_err(|e| CaesiumError {
262 message: e.to_string(),
263 code: 10202,
264 })?;
265 return Ok(());
266 }
267
268 let compressed_file = compress_to_size_in_memory(in_file, parameters, max_output_size, return_smallest)?;
269 let mut out_file = File::create(output_path).map_err(|e| CaesiumError {
270 message: e.to_string(),
271 code: 10203,
272 })?;
273 out_file.write_all(&compressed_file).map_err(|e| CaesiumError {
274 message: e.to_string(),
275 code: 10204,
276 })?;
277
278 Ok(())
279}
280
281pub fn convert(
294 input_path: String,
295 output_path: String,
296 parameters: &CSParameters,
297 format: SupportedFileTypes,
298) -> error::Result<()> {
299 let file_type = get_filetype_from_path(&input_path);
300
301 if file_type == format {
302 return Err(CaesiumError {
303 message: "Cannot convert to the same format".into(),
304 code: 10406,
305 });
306 }
307
308 let in_file = fs::read(input_path).map_err(|e| CaesiumError {
309 message: e.to_string(),
310 code: 10410,
311 })?;
312 let output_buffer = convert_in_memory(in_file, parameters, format).map_err(|e| CaesiumError {
313 message: e.to_string(),
314 code: 10411,
315 })?;
316
317 let mut out_file = File::create(output_path).map_err(|e| CaesiumError {
318 message: e.to_string(),
319 code: 10412,
320 })?;
321
322 out_file.write_all(&output_buffer).map_err(|e| CaesiumError {
323 message: e.to_string(),
324 code: 10413,
325 })?;
326
327 Ok(())
328}
329
330pub fn convert_in_memory(
342 in_file: Vec<u8>,
343 parameters: &CSParameters,
344 format: SupportedFileTypes,
345) -> Result<Vec<u8>, CaesiumError> {
346 convert::convert_in_memory(in_file, format, parameters)
347}
348
349fn validate_parameters(parameters: &CSParameters) -> error::Result<()> {
350 if parameters.jpeg.quality > 100 {
351 return Err(CaesiumError {
352 message: "Invalid JPEG quality value".into(),
353 code: 10001,
354 });
355 }
356
357 if parameters.png.quality > 100 {
358 return Err(CaesiumError {
359 message: "Invalid PNG quality value".into(),
360 code: 10002,
361 });
362 }
363
364 if parameters.png.optimization_level > 6 {
365 return Err(CaesiumError {
366 message: "Invalid PNG optimization level".into(),
367 code: 10006,
368 });
369 }
370
371 if parameters.gif.quality > 100 || parameters.gif.quality < 1 {
372 return Err(CaesiumError {
373 message: "Invalid GIF quality value".into(),
374 code: 10003,
375 });
376 }
377
378 if parameters.webp.quality > 100 {
379 return Err(CaesiumError {
380 message: "Invalid WebP quality value".into(),
381 code: 10004,
382 });
383 }
384
385 Ok(())
386}
387
388#[repr(C)]
389#[derive(PartialEq, Eq, Clone, Copy)]
390pub enum SupportedFileTypes {
391 Jpeg,
392 Png,
393 Gif,
394 WebP,
395 Tiff,
396 Unkn,
397}