use std::{
fmt::Debug,
hash::{Hash, Hasher},
ops::Deref,
sync::{Arc, Weak},
};
use crate::canvas::Color;
use super::ImageData;
#[macro_export]
#[cfg(feature = "image")]
macro_rules! include_image {
($path:literal) => {{
static IMAGE: ::std::sync::OnceLock<$crate::image::Image> = ::std::sync::OnceLock::new();
::std::sync::OnceLock::get_or_init(&IMAGE, || {
let bytes = <[::std::primitive::u8]>::to_vec(::std::include_bytes!(
::std::concat!(::std::env!("CARGO_MANIFEST_DIR"), "/", $path)
));
match $crate::image::Image::try_load_data(bytes) {
::std::result::Result::Ok(image) => image,
::std::result::Result::Err(err) => {
::std::panic!("Failed to load image:{}: {}", $path, err);
}
}
})
.clone()
}};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ImageId {
pub(crate) hash: u64,
}
#[derive(Clone, Debug)]
pub struct Image {
id: ImageId,
data: Arc<ImageData>,
}
impl Default for Image {
fn default() -> Self {
Self::from(ImageData::default())
}
}
impl Image {
pub fn new(pixels: Vec<u8>, width: u32, height: u32) -> Self {
Self::from(ImageData::new(pixels, width, height))
}
#[cfg(feature = "image")]
pub fn try_load_data(data: Vec<u8>) -> image::ImageResult<Self> {
Ok(Self::from(ImageData::try_load_data(data)?))
}
#[cfg(feature = "image")]
pub fn load_data(data: Vec<u8>) -> Self {
Self::from(ImageData::load_data(data))
}
#[cfg(feature = "image")]
pub fn try_load(path: impl AsRef<std::path::Path>) -> image::ImageResult<Self> {
Ok(Self::from(ImageData::try_load(path)?))
}
#[cfg(feature = "image")]
pub fn load(path: impl AsRef<std::path::Path>) -> Self {
Self::from(ImageData::load(path))
}
pub fn premultiplied(mut self) -> Self {
self.multiply_alpha();
self
}
pub fn id(&self) -> ImageId {
self.id
}
pub fn modify(&mut self, f: impl FnOnce(&mut ImageData)) {
f(Arc::make_mut(&mut self.data));
self.id = self.data.compute_id();
}
pub fn color(&mut self, color: Color) {
let [r, g, b, a] = color.to_rgba8();
self.modify(|data| {
for pixel in data.chunks_exact_mut(4) {
pixel[0] = (pixel[0] as u16 * r as u16 / 255) as u8;
pixel[1] = (pixel[1] as u16 * g as u16 / 255) as u8;
pixel[2] = (pixel[2] as u16 * b as u16 / 255) as u8;
pixel[3] = (pixel[3] as u16 * a as u16 / 255) as u8;
}
});
}
pub fn multiply_alpha(&mut self) {
self.modify(|data| {
for pixel in data.chunks_exact_mut(4) {
let alpha = pixel[3] as u16;
pixel[0] = (pixel[0] as u16 * alpha / 255) as u8;
pixel[1] = (pixel[1] as u16 * alpha / 255) as u8;
pixel[2] = (pixel[2] as u16 * alpha / 255) as u8;
}
});
}
pub fn downgrade(&self) -> WeakImage {
WeakImage {
id: self.id,
data: Arc::downgrade(&self.data),
}
}
}
impl Deref for Image {
type Target = ImageData;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl From<ImageData> for Image {
fn from(value: ImageData) -> Self {
let id = value.compute_id();
let data = Arc::new(value);
Self { id, data }
}
}
impl PartialEq for Image {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Image {}
impl Hash for Image {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
#[derive(Clone, Debug)]
pub struct WeakImage {
id: ImageId,
data: Weak<ImageData>,
}
impl WeakImage {
pub fn id(&self) -> ImageId {
self.id
}
pub fn strong_count(&self) -> usize {
self.data.strong_count()
}
pub fn weak_count(&self) -> usize {
self.data.weak_count()
}
}
impl PartialEq for WeakImage {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for WeakImage {}
impl Hash for WeakImage {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}