1
use std::convert::TryFrom;
2
use std::path::Path;
3
use std::sync::atomic::{AtomicU64, Ordering};
4
use std::sync::Arc;
5

            
6
use image::{DynamicImage, RgbaImage};
7
use lazy_static::lazy_static;
8
use winit::window::Icon;
9

            
10
use crate::math::Size;
11

            
12
lazy_static! {
13
    static ref GLOBAL_ID_CELL: AtomicU64 = AtomicU64::new(0);
14
}
15

            
16
/// Embeds a texture in the binary.
17
#[macro_export]
18
macro_rules! include_texture {
19
    ($image_path:expr) => {{
20
        let image_bytes = std::include_bytes!($image_path);
21
        <$crate::texture::Texture as std::convert::TryFrom<&[u8]>>::try_from(image_bytes)
22
    }};
23
}
24

            
25
/// An image that can be used as a sprite. Cheap to clone.
26
#[derive(Debug, Clone)]
27
pub struct Texture {
28
    id: u64,
29
    /// The image behind the texture.
30
    pub image: Arc<RgbaImage>,
31
}
32

            
33
impl Texture {
34
    /// The unique ID of this texture. This depends on load order and is not
35
    /// related to the image data in any way.
36
    #[must_use]
37
    pub const fn id(&self) -> u64 {
38
        self.id
39
    }
40

            
41
    /// Creates a new texture from an image.
42
    #[must_use]
43
    pub fn new(image: Arc<RgbaImage>) -> Self {
44
        let id = GLOBAL_ID_CELL.fetch_add(1, Ordering::SeqCst);
45
        Self { id, image }
46
    }
47

            
48
    /// Loads a texture from an image at `path`
49
    pub fn load<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
50
        let img = image::open(path)?;
51

            
52
        Ok(Self::from(&img))
53
    }
54

            
55
    /// Returns the size of the image.
56
    #[must_use]
57
    pub fn size(&self) -> Size<u32> {
58
        let (w, h) = self.image.dimensions();
59
        Size::new(w, h)
60
    }
61

            
62
    /// Returns the raw image data.
63
    #[must_use]
64
    pub fn rgba_pixels(&self) -> Vec<u8> {
65
        (*self.image).clone().into_vec()
66
    }
67

            
68
    /// Converts the underlying image into a format compatible with `winit` for
69
    /// use as a window icon.
70
    pub fn window_icon(&self) -> Result<Icon, winit::window::BadIcon> {
71
        Icon::from_rgba(self.rgba_pixels(), self.image.width(), self.image.height())
72
    }
73
}
74

            
75
impl<'a> TryFrom<&'a [u8]> for Texture {
76
    type Error = crate::Error;
77

            
78
    fn try_from(bytes: &[u8]) -> crate::Result<Self> {
79
        let img = image::load_from_memory(bytes)?;
80

            
81
        Ok(Self::from(&img))
82
    }
83
}
84

            
85
impl<'a> From<&'a DynamicImage> for Texture {
86
    fn from(img: &'a DynamicImage) -> Self {
87
        let img = img.to_rgba8();
88
        Self::new(Arc::new(img))
89
    }
90
}