use glium::glutin::surface::WindowSurface;
use std::path::Path;
use std::vec::Vec;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use image::{DynamicImage, RgbaImage};
use glium::Display;
use glium::texture::{RawImage2d, SrgbTexture2d, MipmapsOption};
use lru::LruCache;
use std::num::NonZeroUsize;
use crate::logging::EnigmaWarning;
thread_local! {
static IMAGE_CACHE: RefCell<LruCache<Vec<u8>, RgbaImage>> = RefCell::new(LruCache::new(NonZeroUsize::new(20).unwrap()));
}
#[derive(Serialize, Deserialize, Clone)]
pub struct TextureSerializer {
path: String,
width: u32,
height: u32,
tileable: bool,
binary_data: Option<Vec<u8>>,
name: Option<String>
}
pub struct Texture {
pub path: String,
pub texture: glium::texture::SrgbTexture2d,
pub width: u32,
pub height: u32,
pub tileable: bool,
pub binary_data: Option<Vec<u8>>,
pub name: Option<String>
}
impl Texture {
pub fn new(display: &glium::Display<WindowSurface>, path: &str) -> Self {
let image = image::open(path).unwrap().to_rgba8();
let image_dimensions = image.dimensions();
let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
let texture = glium::texture::SrgbTexture2d::new(display, image).unwrap();
let path_obj = Path::new(path);
let filename = path_obj.file_name().expect("failed to get filename from path. it might be a folder");
Self {
texture,
path: String::from(path),
width: image_dimensions.0,
height: image_dimensions.1,
binary_data: None,
tileable: false,
name: Some(filename.to_string_lossy().into_owned())
}
}
pub fn set_tileable(&mut self, tileable: bool){
self.tileable = tileable;
}
pub fn from_serializer(serializer: TextureSerializer, display: &glium::Display<WindowSurface>) -> Self {
let path = Path::new(&serializer.path);
if !path.is_file() {
match &serializer.binary_data {
Some(data) => {
return Texture::from_resource(display, data.as_slice());
}
None => {}
}
} else {
return Texture::new(display, path.to_str().unwrap());
}
EnigmaWarning::new(Some("could not create texture from serializer, returned texture is empty"), true).log();
let empty_tex = glium::texture::SrgbTexture2d::empty(display, serializer.width, serializer.height);
return Self {
texture: empty_tex.expect("Could not create Empty Texture"),
path: String::from("RESOURCE"),
width: serializer.width,
height: serializer.height,
binary_data: None,
tileable: serializer.tileable,
name: serializer.name
};
}
pub fn to_serializer(&self) -> TextureSerializer {
TextureSerializer {
path: self.path.clone(),
width: self.width,
height: self.height,
binary_data: self.binary_data.clone(),
tileable: self.tileable,
name: self.name.clone()
}
}
pub fn from_resource(display: &Display<WindowSurface>, data: &[u8]) -> Self {
let image = IMAGE_CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
cache.get_or_insert(data.to_vec(), || {
let img = image::load_from_memory(data).unwrap_or_else(|_| {
DynamicImage::ImageRgba8(RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 255, 255])))
});
img.to_rgba8()
}).clone()
});
let dimensions = image.dimensions();
let raw_image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dimensions);
let texture = SrgbTexture2d::with_mipmaps(display, raw_image, MipmapsOption::AutoGeneratedMipmaps).unwrap();
Self {
texture,
path: String::from("INTERNAL ENIGMA RESOURCE"),
width: dimensions.0,
height: dimensions.1,
binary_data: Some(data.to_vec()),
tileable: false,
name: None,
}
}
pub fn get_texture_clone(&self, display: &glium::Display<WindowSurface>) -> Self {
let path_str = self.path.clone();
let path = Path::new(&path_str);
if !path.is_file() {
match &self.binary_data {
Some(data) => {
return Texture::from_resource(display, data.as_slice());
}
None => {
EnigmaWarning::new(Some("could not clone texture , returned texture is empty"), true).log();
let empty_tex = glium::texture::SrgbTexture2d::empty(display, self.width, self.height);
return Self {
texture: empty_tex.expect("Could not create Empty Texture"),
path: String::from("RESOURCE"),
width: self.width,
height: self.height,
binary_data: None,
tileable: self.tileable,
name: self.name.clone()
};
}
}
} else {
return Texture::new(display, path_str.as_str());
}
}
pub fn pink_texture(display: &glium::Display<WindowSurface>) -> Self {
let image = image::RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 255, 255]));
let image_dimensions = image.dimensions();
let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
let texture = glium::texture::SrgbTexture2d::new(display, image).unwrap();
Self {
texture,
path: String::from("PINK"),
width: image_dimensions.0,
height: image_dimensions.1,
binary_data: None,
tileable: false,
name: Some("PinkTexture".to_string())
}
}
pub fn colored_texture(display: &glium::Display<WindowSurface>, color: [u8; 4], name: Option<String>) -> Self {
let image = image::RgbaImage::from_pixel(1, 1, image::Rgba(color));
let image_dimensions = image.dimensions();
let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
let texture = glium::texture::SrgbTexture2d::new(display, image).unwrap();
let n = name.unwrap_or_else(|| "COLORED".to_string());
Self {
texture,
path: n.clone(),
width: image_dimensions.0,
height: image_dimensions.1,
binary_data: None,
tileable: false,
name: Some(n)
}
}
}