fere_resources/
texture.rs1use fere_common::*;
2use gl::types::*;
3use image::{io::Reader as IReader, DynamicImage};
4use std::io::{BufRead, Seek};
5
6#[derive(Debug)]
7pub struct TextureData {
8 pub name: String,
9 pub data: Vec<u8>,
10 pub size: IVec2,
11 pub channel: u8,
12}
13
14pub fn import<T: BufRead + Seek>(name: &str, source: T) -> Result<TextureData, String> {
15 let source = IReader::new(source).with_guessed_format().unwrap();
16 let result = source.decode().map_err(|e| format!("{:?}", e))?;
17 match result {
18 DynamicImage::ImageRgb8(img) => {
19 let size = IVec2::new(img.dimensions().0 as i32, img.dimensions().1 as i32);
20 let data = img.into_raw();
21 assert_eq!(data.len(), (size.x * size.y * 3) as usize);
22 Ok(TextureData {
23 name: name.to_owned(),
24 data,
25 size,
26 channel: 3,
27 })
28 }
29 DynamicImage::ImageRgba8(img) => {
30 let size = IVec2::new(img.dimensions().0 as i32, img.dimensions().1 as i32);
31 let data = img.into_raw();
32 assert_eq!(data.len(), (size.x * size.y * 4) as usize);
33 Ok(TextureData {
34 name: name.to_owned(),
35 data,
36 size,
37 channel: 4,
38 })
39 }
40 _ => Err("Unsupported image format".to_owned()),
41 }
42}
43
44#[derive(Debug)]
45pub struct Texture {
46 pub name: String,
47 pub path: Option<String>,
49 pub size: IVec2,
50
51 data: Option<TextureData>,
53
54 tex: GLuint,
56}
57
58impl Texture {
59 pub fn new(path: Option<String>, data: TextureData) -> Self {
60 Texture {
61 name: data.name.clone(),
62 path,
63 size: data.size,
64 data: Some(data),
65 tex: 0,
66 }
67 }
68
69 pub fn buffer(&mut self) {
70 unsafe {
71 gl::GenTextures(1, &mut self.tex);
72 gl::BindTexture(gl::TEXTURE_2D, self.tex);
73
74 let data = self.data.take().unwrap();
75 let format = match data.channel {
76 1 => gl::RED,
77 3 => gl::RGB,
78 4 => gl::RGBA,
79 _ => panic!("Invalid Image channel set"),
80 };
81 gl::TexImage2D(
82 gl::TEXTURE_2D,
83 0,
84 format as i32,
85 self.size.x,
86 self.size.y,
87 0,
88 format,
89 gl::UNSIGNED_BYTE,
90 data.data.as_ptr().cast(),
91 );
92
93 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
94 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
95 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
96 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
97 }
98 }
99
100 pub fn bind(&self) {
101 debug_assert!(self.data.is_none(), "bind() on an unbufferd texure");
102 unsafe { gl::BindTexture(gl::TEXTURE_2D, self.tex) }
103 }
104
105 pub fn bind_at(&self, index: u32) {
106 debug_assert!(self.data.is_none(), "bind() on an unbufferd texure");
107 unsafe {
108 gl::ActiveTexture(gl::TEXTURE0 + index);
109 gl::BindTexture(gl::TEXTURE_2D, self.tex);
110 }
111 }
112
113 pub fn bind_or_buffer(&mut self) {
114 if self.data.is_some() {
115 self.buffer();
116 }
117 unsafe { gl::BindTexture(gl::TEXTURE_2D, self.tex) }
118 }
119
120 pub fn get_raw(&self) -> GLuint {
121 self.tex
122 }
123}
124
125impl Drop for Texture {
126 fn drop(&mut self) {
127 if self.data.is_none() {
128 unsafe {
129 gl::DeleteTextures(1, &self.tex);
130 }
131 }
132 }
133}