🚧 Add a first version of the mozaik builder service
This commit is contained in:
@@ -1 +1,2 @@
|
||||
pub(crate) mod mozaik_builder;
|
||||
pub(crate) mod random_svg_generators;
|
||||
|
92
src/infrastructure/services/mozaik_builder.rs
Normal file
92
src/infrastructure/services/mozaik_builder.rs
Normal 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
|
||||
}
|
Reference in New Issue
Block a user