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