enigma_3d/
texture.rs

1use glium::glutin::surface::WindowSurface;
2use std::path::Path;
3use std::vec::Vec;
4use serde::{Deserialize, Serialize};
5
6use std::cell::RefCell;
7use image::{DynamicImage, RgbaImage};
8use glium::Display;
9use glium::texture::{RawImage2d, SrgbTexture2d, MipmapsOption};
10use lru::LruCache;
11use std::num::NonZeroUsize;
12use crate::logging::EnigmaWarning;
13
14thread_local! {
15    static IMAGE_CACHE: RefCell<LruCache<Vec<u8>, RgbaImage>> = RefCell::new(LruCache::new(NonZeroUsize::new(20).unwrap()));
16}
17
18#[derive(Serialize, Deserialize, Clone)]
19pub struct TextureSerializer {
20    path: String,
21    width: u32,
22    height: u32,
23    tileable: bool,
24    binary_data: Option<Vec<u8>>,
25    name: Option<String>
26}
27
28pub struct Texture {
29    pub path: String,
30    pub texture: glium::texture::SrgbTexture2d,
31    pub width: u32,
32    pub height: u32,
33    pub tileable: bool,
34    pub binary_data: Option<Vec<u8>>,
35    pub name: Option<String>
36}
37
38impl Texture {
39    pub fn new(display: &glium::Display<WindowSurface>, path: &str) -> Self {
40        let image = image::open(path).unwrap().to_rgba8();
41        let image_dimensions = image.dimensions();
42        let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
43        let texture = glium::texture::SrgbTexture2d::new(display, image).unwrap();
44        let path_obj = Path::new(path);
45        let filename = path_obj.file_name().expect("failed to get filename from path. it might be a folder");
46        Self {
47            texture,
48            path: String::from(path),
49            width: image_dimensions.0,
50            height: image_dimensions.1,
51            binary_data: None,
52            tileable: false,
53            name: Some(filename.to_string_lossy().into_owned())
54        }
55    }
56
57    pub fn set_tileable(&mut self, tileable: bool){
58        self.tileable = tileable;
59    }
60
61    pub fn from_serializer(serializer: TextureSerializer, display: &glium::Display<WindowSurface>) -> Self {
62        let path = Path::new(&serializer.path);
63        if !path.is_file() {
64            match &serializer.binary_data {
65                Some(data) => {
66                    return Texture::from_resource(display, data.as_slice());
67                }
68                None => {}
69            }
70        } else {
71            return Texture::new(display, path.to_str().unwrap());
72        }
73        EnigmaWarning::new(Some("could not create texture from serializer, returned texture is empty"), true).log();
74        let empty_tex = glium::texture::SrgbTexture2d::empty(display, serializer.width, serializer.height);
75        return Self {
76            texture: empty_tex.expect("Could not create Empty Texture"),
77            path: String::from("RESOURCE"),
78            width: serializer.width,
79            height: serializer.height,
80            binary_data: None,
81            tileable: serializer.tileable,
82            name: serializer.name
83        };
84    }
85
86    pub fn to_serializer(&self) -> TextureSerializer {
87        TextureSerializer {
88            path: self.path.clone(),
89            width: self.width,
90            height: self.height,
91            binary_data: self.binary_data.clone(),
92            tileable: self.tileable,
93            name: self.name.clone()
94        }
95    }
96
97    pub fn from_resource(display: &Display<WindowSurface>, data: &[u8]) -> Self {
98
99        let image = IMAGE_CACHE.with(|cache| {
100            let mut cache = cache.borrow_mut();
101            cache.get_or_insert(data.to_vec(), || {
102                let img = image::load_from_memory(data).unwrap_or_else(|_| {
103                    DynamicImage::ImageRgba8(RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 255, 255])))
104                });
105                img.to_rgba8()
106            }).clone()
107        });
108
109        let dimensions = image.dimensions();
110        let raw_image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dimensions);
111        let texture = SrgbTexture2d::with_mipmaps(display, raw_image, MipmapsOption::AutoGeneratedMipmaps).unwrap();
112
113        Self {
114            texture,
115            path: String::from("INTERNAL ENIGMA RESOURCE"),
116            width: dimensions.0,
117            height: dimensions.1,
118            binary_data: Some(data.to_vec()),
119            tileable: false,
120            name: None,
121        }
122    }
123
124    pub fn get_texture_clone(&self, display: &glium::Display<WindowSurface>) -> Self {
125        let path_str = self.path.clone();
126        let path = Path::new(&path_str);
127        if !path.is_file() {
128            match &self.binary_data {
129                Some(data) => {
130                    return Texture::from_resource(display, data.as_slice());
131                }
132                None => {
133                    EnigmaWarning::new(Some("could not clone texture , returned texture is empty"), true).log();
134                    let empty_tex = glium::texture::SrgbTexture2d::empty(display, self.width, self.height);
135                    return Self {
136                        texture: empty_tex.expect("Could not create Empty Texture"),
137                        path: String::from("RESOURCE"),
138                        width: self.width,
139                        height: self.height,
140                        binary_data: None,
141                        tileable: self.tileable,
142                        name: self.name.clone()
143                    };
144                }
145            }
146        } else {
147            return Texture::new(display, path_str.as_str());
148        }
149    }
150
151    pub fn pink_texture(display: &glium::Display<WindowSurface>) -> Self {
152        let image = image::RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 255, 255]));
153        let image_dimensions = image.dimensions();
154        let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
155        let texture = glium::texture::SrgbTexture2d::new(display, image).unwrap();
156        Self {
157            texture,
158            path: String::from("PINK"),
159            width: image_dimensions.0,
160            height: image_dimensions.1,
161            binary_data: None,
162            tileable: false,
163            name: Some("PinkTexture".to_string())
164        }
165    }
166
167    pub fn colored_texture(display: &glium::Display<WindowSurface>, color: [u8; 4], name: Option<String>) -> Self {
168        let image = image::RgbaImage::from_pixel(1, 1, image::Rgba(color));
169        let image_dimensions = image.dimensions();
170        let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
171        let texture = glium::texture::SrgbTexture2d::new(display, image).unwrap();
172        let n = name.unwrap_or_else(|| "COLORED".to_string());
173        Self {
174            texture,
175            path: n.clone(),
176            width: image_dimensions.0,
177            height: image_dimensions.1,
178            binary_data: None,
179            tileable: false,
180            name: Some(n)
181        }
182    }
183
184}
185