use anyhow::{anyhow,bail,ensure};
use image::codecs::png::PngEncoder;
use image::codecs::jpeg::JpegEncoder;
use std::io::BufWriter;
use image::ImageEncoder;
use image::ColorType;
use fast_image_resize as fir;
use fir::{
Image,
Resizer,
ResizeAlg,
FilterType,
PixelType,
};
use std::num::NonZeroU32;
pub(crate) const ALBUM_ART_MAX_SIZE: u32 = 600;
pub(crate) const ALBUM_ART_MAX_SIZE_NUM: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(ALBUM_ART_MAX_SIZE) };
pub(crate) const ALBUM_ART_MAX_SIZE_ARRAY: [usize; 2] = [ALBUM_ART_MAX_SIZE as usize; 2];
#[inline(always)]
pub(crate) fn art_from_raw(bytes: &[u8], resizer: &mut fir::Resizer) -> Result<egui_extras::RetainedImage, anyhow::Error> {
Ok(color_img_to_retained(
rgb_bytes_to_color_img(
resize_dyn_image(
bytes_to_dyn_image(bytes)?, resizer)?.buffer()
)
)
)
}
#[inline(always)]
pub(crate) fn art_from_known(bytes: &[u8]) -> egui_extras::RetainedImage {
color_img_to_retained(
rgb_bytes_to_color_img(bytes)
)
}
#[inline(always)]
pub(crate) fn create_resizer() -> fir::Resizer {
fir::Resizer::new(ResizeAlg::Convolution(FilterType::Lanczos3))
}
#[inline(always)]
fn bytes_to_dyn_image(bytes: &[u8]) -> Result<image::DynamicImage, anyhow::Error> {
match image::load_from_memory(bytes) {
Ok(img) => Ok(img),
Err(e) => bail!(e),
}
}
#[inline(always)]
fn resize_dyn_image(img: image::DynamicImage, resizer: &mut fir::Resizer) -> Result<fir::Image<'static>, anyhow::Error> {
let width = match NonZeroU32::new(img.width()) {
Some(w) => w,
None => bail!("Album art width was 0"),
};
let height = match NonZeroU32::new(img.height()) {
Some(w) => w,
None => bail!("Album art height was 0"),
};
let old_img = Image::from_vec_u8(width, height, img.to_rgb8().into_raw(), PixelType::U8x3)?;
let mut new_img = Image::new(ALBUM_ART_MAX_SIZE_NUM, ALBUM_ART_MAX_SIZE_NUM, PixelType::U8x3);
if let Err(e) = resizer.resize(&old_img.view(), &mut new_img.view_mut()) {
bail!(e);
}
Ok(new_img)
}
#[inline(always)]
fn rgb_bytes_to_color_img(bytes: &[u8]) -> egui::ColorImage {
egui::ColorImage {
size: ALBUM_ART_MAX_SIZE_ARRAY,
pixels: bytes.chunks_exact(3).map(|p| egui::Color32::from_rgb(p[0], p[1], p[2])).collect(),
}
}
#[inline(always)]
fn color_img_to_retained(img: egui::ColorImage) -> egui_extras::RetainedImage {
egui_extras::RetainedImage::from_color_image("", img)
}
#[cfg(test)]
mod tests {
use super::*;
const IMG_BYTES: &[u8] = include_bytes!("../../assets/images/icon/1024.png");
#[test]
fn _art_from_raw() {
let mut resizer = super::create_resizer();
art_from_raw(IMG_BYTES, &mut resizer).unwrap();
}
#[test]
fn _art_from_known() {
let mut resizer = super::create_resizer();
let dyn_img = bytes_to_dyn_image(IMG_BYTES).unwrap();
let fir_img = resize_dyn_image(dyn_img, &mut resizer).unwrap();
assert!(fir_img.width() == ALBUM_ART_MAX_SIZE_NUM);
assert!(fir_img.height() == ALBUM_ART_MAX_SIZE_NUM);
assert!(fir_img.buffer().len() % 3 == 0);
let _ = art_from_known(&fir_img.buffer());
}
}