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