🚧 Add a first version of the mozaik builder service

This commit is contained in:
2024-05-10 18:32:05 +02:00
parent 79e8dea622
commit 4f9e5c538e
3 changed files with 94 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
use std::io::Cursor;
use image::imageops::FilterType;
use image::io::Reader;
use image::{DynamicImage, ImageFormat};
use image::{GenericImage, RgbImage};
use tracing::{error, warn};
fn from_raw_to_image(raw: &Vec<u8>) -> Option<DynamicImage> {
match Reader::new(Cursor::new(raw)).with_guessed_format() {
Ok(reader) => match reader.decode() {
Ok(image) => return Some(image),
Err(err) => error!("Unable to decode the image: {}", err),
},
Err(err) => {
error!("Unable to read the image: {}", err)
}
}
None
}
pub fn create_mozaik(
width_px: u32,
height_px: u32,
images: &[Vec<u8>],
padding_image: &Option<Vec<u8>>,
) -> Vec<u8> {
let placeholder = DynamicImage::new_rgb8(128, 128);
let images: Vec<Option<DynamicImage>> = images.iter().map(from_raw_to_image).collect();
let padding_image = if let Some(padding_image) = padding_image {
from_raw_to_image(padding_image)
} else {
None
};
let mut bytes: Vec<u8> = Vec::new();
let mut allocations: Vec<&Option<DynamicImage>> = vec![];
let mut images_per_row = 1;
let mut images_per_col = 1;
match images.len() {
0 => {
allocations.push(&padding_image);
}
1 => {
allocations.push(&images[0]);
}
2 => {
allocations.extend_from_slice(&[&images[0], &images[1], &images[1], &images[0]]);
images_per_row = 2;
images_per_col = 2;
}
_ => {
// TODO: Manage other cases
warn!("For now, we only manage the rendering of mozaic with less than 3 images");
return bytes;
}
}
let image_width_px = width_px / images_per_row;
let image_height_px = height_px / images_per_col;
let mut output = RgbImage::new(width_px, height_px);
let mut row_pos = 0;
for (index, image) in allocations.iter().enumerate() {
if index > 0 && index % images_per_row as usize == 0 {
row_pos += 1;
}
let col_pos = index - (images_per_row as usize * row_pos);
let image = *image;
let scaled = image
.as_ref()
.unwrap_or(&placeholder)
.resize_to_fill(image_width_px, image_height_px, FilterType::Nearest)
.into_rgb8();
let output_image_pos_x = col_pos as u32 * image_width_px;
let output_image_pos_y = row_pos as u32 * image_height_px;
let _ = output.copy_from(&scaled, output_image_pos_x, output_image_pos_y);
}
let _ = output.write_to(&mut Cursor::new(&mut bytes), ImageFormat::Jpeg);
bytes
}