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