docbox_processing/
image.rs1use super::{ProcessingError, ProcessingOutput, QueuedUpload};
2use bytes::Bytes;
3use docbox_database::models::generated_file::GeneratedFileType;
4use image::{DynamicImage, ImageDecoder, ImageFormat, ImageReader, ImageResult};
5use std::io::Cursor;
6
7pub fn create_img_bytes(image: &DynamicImage, format: ImageFormat) -> ImageResult<Vec<u8>> {
10 let mut buffer = Cursor::new(Vec::new());
11 image.write_to(&mut buffer, format)?;
12 Ok(buffer.into_inner())
13}
14
15pub async fn process_image_async(
19 file_bytes: Bytes,
20 format: ImageFormat,
21) -> Result<ProcessingOutput, ProcessingError> {
22 tokio::task::spawn_blocking(move || process_image(file_bytes, format)).await?
23}
24
25fn process_image(
30 file_bytes: Bytes,
31 format: ImageFormat,
32) -> Result<ProcessingOutput, ProcessingError> {
33 let mut decoder = ImageReader::with_format(Cursor::new(&file_bytes), format)
34 .into_decoder()
35 .map_err(ProcessingError::DecodeImage)?;
36
37 let orientation = decoder
39 .orientation()
40 .map_err(ProcessingError::DecodeImage)?;
41
42 let mut img = DynamicImage::from_decoder(decoder).map_err(ProcessingError::DecodeImage)?;
43
44 img.apply_orientation(orientation);
46
47 tracing::debug!("generated image thumbnails");
48
49 let generated =
50 generate_image_preview(img, format).map_err(ProcessingError::GenerateThumbnail)?;
51
52 let upload_queue = vec![
53 QueuedUpload::new(
54 mime::IMAGE_JPEG,
55 GeneratedFileType::LargeThumbnail,
56 generated.large_thumbnail_jpeg.into(),
57 ),
58 QueuedUpload::new(
59 mime::IMAGE_JPEG,
60 GeneratedFileType::SmallThumbnail,
61 generated.thumbnail_jpeg.into(),
62 ),
63 ];
64
65 Ok(ProcessingOutput {
66 upload_queue,
67 ..Default::default()
68 })
69}
70
71struct GeneratedPreviewImages {
73 thumbnail_jpeg: Vec<u8>,
75 large_thumbnail_jpeg: Vec<u8>,
78}
79
80fn generate_image_preview(
81 image: DynamicImage,
82 format: ImageFormat,
83) -> ImageResult<GeneratedPreviewImages> {
84 tracing::debug!("rendering image preview variants");
85
86 let thumbnail_jpeg = create_thumbnail(&image, format)?;
87 let large_thumbnail_jpeg = create_thumbnail_large(&image, format)?;
88
89 Ok(GeneratedPreviewImages {
90 thumbnail_jpeg,
91 large_thumbnail_jpeg,
92 })
93}
94
95fn create_thumbnail(image: &DynamicImage, format: ImageFormat) -> ImageResult<Vec<u8>> {
96 let thumbnail = image.thumbnail(64, 64);
97 create_img_bytes(&thumbnail, format)
98}
99
100fn create_thumbnail_large(image: &DynamicImage, format: ImageFormat) -> ImageResult<Vec<u8>> {
101 let (width, height) = match format {
102 ImageFormat::Ico => (256, 256),
104 _ => (512, 512),
105 };
106
107 let cover_page_preview = image.resize(width, height, image::imageops::FilterType::Triangle);
108 create_img_bytes(&cover_page_preview, format)
109}