use super::{ProcessingError, ProcessingOutput, QueuedUpload};
use bytes::Bytes;
use docbox_database::models::generated_file::GeneratedFileType;
use image::{DynamicImage, ImageDecoder, ImageFormat, ImageReader, ImageResult};
use std::io::Cursor;
pub fn create_img_bytes(image: &DynamicImage, format: ImageFormat) -> ImageResult<Vec<u8>> {
let mut buffer = Cursor::new(Vec::new());
image.write_to(&mut buffer, format)?;
Ok(buffer.into_inner())
}
pub async fn process_image_async(
file_bytes: Bytes,
format: ImageFormat,
) -> Result<ProcessingOutput, ProcessingError> {
tokio::task::spawn_blocking(move || process_image(file_bytes, format)).await?
}
fn process_image(
file_bytes: Bytes,
format: ImageFormat,
) -> Result<ProcessingOutput, ProcessingError> {
let mut decoder = ImageReader::with_format(Cursor::new(&file_bytes), format)
.into_decoder()
.map_err(ProcessingError::DecodeImage)?;
let orientation = decoder
.orientation()
.map_err(ProcessingError::DecodeImage)?;
let mut img = DynamicImage::from_decoder(decoder).map_err(ProcessingError::DecodeImage)?;
img.apply_orientation(orientation);
tracing::debug!("generated image thumbnails");
let generated =
generate_image_preview(img, format).map_err(ProcessingError::GenerateThumbnail)?;
let upload_queue = vec![
QueuedUpload::new(
mime::IMAGE_JPEG,
GeneratedFileType::LargeThumbnail,
generated.large_thumbnail_jpeg.into(),
),
QueuedUpload::new(
mime::IMAGE_JPEG,
GeneratedFileType::SmallThumbnail,
generated.thumbnail_jpeg.into(),
),
];
Ok(ProcessingOutput {
upload_queue,
..Default::default()
})
}
struct GeneratedPreviewImages {
thumbnail_jpeg: Vec<u8>,
large_thumbnail_jpeg: Vec<u8>,
}
fn generate_image_preview(
image: DynamicImage,
format: ImageFormat,
) -> ImageResult<GeneratedPreviewImages> {
tracing::debug!("rendering image preview variants");
let thumbnail_jpeg = create_thumbnail(&image, format)?;
let large_thumbnail_jpeg = create_thumbnail_large(&image, format)?;
Ok(GeneratedPreviewImages {
thumbnail_jpeg,
large_thumbnail_jpeg,
})
}
fn create_thumbnail(image: &DynamicImage, format: ImageFormat) -> ImageResult<Vec<u8>> {
let thumbnail = image.thumbnail(64, 64);
create_img_bytes(&thumbnail, format)
}
fn create_thumbnail_large(image: &DynamicImage, format: ImageFormat) -> ImageResult<Vec<u8>> {
let (width, height) = match format {
ImageFormat::Ico => (256, 256),
_ => (512, 512),
};
let cover_page_preview = image.resize(width, height, image::imageops::FilterType::Triangle);
create_img_bytes(&cover_page_preview, format)
}