use crate::tiles::*;
use image::{DynamicImage, GenericImage, GenericImageView, Pixel, RgbImage};
#[allow(missing_debug_implementations)]
pub struct Mosaic {
img: RgbImage,
tiles: TileSet,
inner: Inner,
}
impl Mosaic {
pub fn new(
img: DynamicImage,
tiles: &Vec<DynamicImage>,
img_scaling: f32,
tile_size: u8,
) -> Self {
if img_scaling < 0.1 {
panic!("Scaling factor must be at least 0.1.");
}
let img = if img_scaling != 1.0 {
let (x, y) = img.dimensions();
let x = (x as f32 * img_scaling) as u32;
let y = (y as f32 * img_scaling) as u32;
if x == 0 || y == 0 {
panic!(
"Scaling factor results in an image with at least one dimension with zero px"
);
}
img.resize_exact(x, y, image::imageops::FilterType::Triangle)
} else {
img
}
.to_rgb8();
let mut tiles = TileSet::from(tiles);
let tile_size = tile_size as u32;
if tiles.tile_side_len() != tile_size {
tiles.scale_tiles(tile_size);
}
let (img_x, img_y) = img.dimensions();
let (mos_x, mos_y) = (img_x * tile_size, img_y * tile_size);
let inner = Inner(DynamicImage::new_rgb8(mos_x, mos_y));
Self { img, tiles, inner }
}
pub fn output_size(&self) -> (u32, u32) {
let (img_x, img_y) = self.img.dimensions();
let tile_size = self.tiles.tile_side_len();
let (mos_x, mos_y) = (img_x * tile_size, img_y * tile_size);
(mos_x, mos_y)
}
pub fn to_image(self) -> RgbImage {
let map = self.tiles.map_to(&self.img);
let (img_x, img_y) = self.img.dimensions();
let tile_size = self.tiles.tile_side_len();
let mut mosaic = self.inner;
let mut mos_x = 0;
for x in 0..img_x {
let mut mos_y = 0;
for y in 0..img_y {
let cur_px = y + (x * img_y) + 1;
eprint!(
"\rProcessing source px {:04}/{:04}: src loc ({:03}, {:03}) -- dst loc ({:04}, {:04})... ",
cur_px,
img_x * img_y,
x,
y,
mos_x,
mos_y
);
let tile_for_px = map.get(&self.img.get_pixel(x, y)).expect("No tile for px");
mosaic.add_tile(tile_for_px, (mos_x, mos_y));
mos_y += tile_size;
}
mos_x += tile_size;
}
eprintln!();
mosaic.0.into_rgb8()
}
}
struct Inner(DynamicImage);
impl Inner {
pub fn add_tile(&mut self, tile: &Tile, start_coords: (u32, u32)) {
let s = tile.side_len();
let (start_x, start_y) = start_coords;
let mut tile_px = tile.img().pixels();
for x in start_x..(start_x + s) {
for y in start_y..(start_y + s) {
let px = tile_px
.next()
.expect("Unable to get next tile px")
.to_rgba();
self.0.put_pixel(x, y, px);
}
}
}
}