use std::cmp::max;
use std::collections::HashMap;
use eframe::egui;
use log::debug;
use crate::image::{fit_image, to_rgba8};
const SIZES: [u32; 5] = [8000, 4000, 2000, 1000, 500];
#[derive(Default)]
pub struct ImagePyramid {
pub original: image::DynamicImage,
levels_by_size: HashMap<u32, image::DynamicImage>,
aspect_ratio: f32,
pub original_size: egui::Vec2,
pub original_has_alpha: bool,
}
impl ImagePyramid {
pub fn new(original: image::DynamicImage) -> ImagePyramid {
let original_has_alpha = original.color().has_alpha();
let original = to_rgba8(original);
let original_size = egui::Vec2::new(original.width() as f32, original.height() as f32);
ImagePyramid {
levels_by_size: {
let mut levels: HashMap<u32, image::DynamicImage> = HashMap::new();
let mut parent = None;
for size in SIZES {
let image_to_downscale = match levels.get(&parent.unwrap_or(0)) {
Some(parent_level) => parent_level,
None => &original,
};
if max(original.width(), original.height()) <= size {
continue;
}
debug!(
"Creating pyramid level for target size {} from {} image size {:?}",
size,
parent.map_or("original".to_string(), |_| "parent".to_string()),
(image_to_downscale.width(), image_to_downscale.height())
);
let level = fit_image(
image_to_downscale,
egui::Vec2::new(size as f32, size as f32),
);
levels.insert(size, level);
parent = Some(size);
}
levels
},
original,
aspect_ratio: original_size.x / original_size.y,
original_size,
original_has_alpha,
}
}
pub fn get_level(&self, size: egui::Vec2) -> &image::DynamicImage {
let scale = (size.x / self.original_size.x).min(size.y / self.original_size.y);
let dim = if self.aspect_ratio >= 1. {
scale * self.original_size.x
} else {
scale * self.original_size.y
};
match SIZES
.iter()
.rev()
.find(|&&s| s >= dim as u32 && self.levels_by_size.contains_key(&s))
{
Some(closest) => self
.levels_by_size
.get(closest)
.expect("non-existing pyramid level"),
None => &self.original,
}
}
pub fn num_levels(&self) -> usize {
self.levels_by_size.len()
}
}