1use crate::gl;
2use crate::gl::types::GLuint;
3use image::{self, DynamicImage, RgbaImage};
4
5use std::path::Path;
6
7use crate::{ops, ImageSize, CreateTexture, UpdateTexture, TextureSettings, Format, Filter};
8
9trait GlSettings {
10 fn get_gl_mag(&self) -> gl::types::GLenum;
11 fn get_gl_min(&self) -> gl::types::GLenum;
12 fn get_gl_mipmap(&self) -> gl::types::GLenum;
13}
14
15impl GlSettings for TextureSettings {
16 fn get_gl_mag(&self) -> gl::types::GLenum {
17 match self.get_mag() {
18 Filter::Linear => gl::LINEAR,
19 Filter::Nearest => gl::NEAREST,
20 }
21 }
22
23 fn get_gl_min(&self) -> gl::types::GLenum {
24 match self.get_min() {
25 Filter::Linear => {
26 if self.get_generate_mipmap() {
27 match self.get_mipmap() {
28 Filter::Linear => gl::LINEAR_MIPMAP_LINEAR,
29 Filter::Nearest => gl::LINEAR_MIPMAP_NEAREST,
30 }
31 } else {
32 gl::LINEAR
33 }
34 }
35 Filter::Nearest => {
36 if self.get_generate_mipmap() {
37 match self.get_mipmap() {
38 Filter::Linear => gl::NEAREST_MIPMAP_LINEAR,
39 Filter::Nearest => gl::NEAREST_MIPMAP_NEAREST,
40 }
41 } else {
42 gl::NEAREST
43 }
44 }
45 }
46 }
47
48 fn get_gl_mipmap(&self) -> gl::types::GLenum {
49 match self.get_mipmap() {
50 Filter::Linear => gl::LINEAR,
51 Filter::Nearest => gl::NEAREST,
52 }
53 }
54}
55
56pub struct Texture {
62 id: GLuint,
63 width: u32,
64 height: u32,
65}
66
67impl Texture {
68 #[inline(always)]
70 pub fn new(id: GLuint, width: u32, height: u32) -> Self {
71 Texture {
72 id: id,
73 width: width,
74 height: height,
75 }
76 }
77
78 #[inline(always)]
80 pub fn get_id(&self) -> GLuint {
81 self.id
82 }
83
84 pub fn empty() -> Result<Self, String> {
86 CreateTexture::create(&mut (),
87 Format::Rgba8,
88 &[0u8; 4],
89 [1, 1],
90 &TextureSettings::new())
91 }
92
93 pub fn from_memory_alpha(buf: &[u8],
95 width: u32,
96 height: u32,
97 settings: &TextureSettings)
98 -> Result<Self, String> {
99 let size = [width, height];
100 let buffer = ops::alpha_to_rgba8(buf, size);
101 CreateTexture::create(&mut (), Format::Rgba8, &buffer, size, settings)
102 }
103
104 pub fn from_path<P>(path: P) -> Result<Self, String>
106 where P: AsRef<Path>
107 {
108 let path = path.as_ref();
109
110 let img = match image::open(path) {
111 Ok(img) => img,
112 Err(e) => {
113 return Err(format!("Could not load '{:?}': {:?}", path.file_name().unwrap(), e))
114 }
115 };
116
117 let img = match img {
118 DynamicImage::ImageRgba8(img) => img,
119 x => x.to_rgba(),
120 };
121
122 Ok(Texture::from_image(&img, &TextureSettings::new()))
123 }
124
125 pub fn from_image(img: &RgbaImage, settings: &TextureSettings) -> Self {
127 let (width, height) = img.dimensions();
128 CreateTexture::create(&mut (), Format::Rgba8, img, [width, height], settings).unwrap()
129 }
130
131 pub fn update(&mut self, img: &RgbaImage) {
133 let (width, height) = img.dimensions();
134
135 UpdateTexture::update(self, &mut (), Format::Rgba8, img, [0, 0], [width, height]).unwrap();
136 }
137}
138
139impl Drop for Texture {
140 fn drop(&mut self) {
141 unsafe {
142 let ids = [self.id];
143 gl::DeleteTextures(1, ids.as_ptr());
144 drop(ids);
145 }
146 }
147}
148
149impl ImageSize for Texture {
150 fn get_size(&self) -> (u32, u32) {
151 (self.width, self.height)
152 }
153}
154
155impl CreateTexture<()> for Texture {
156 type Error = String;
157
158 fn create<S: Into<[u32; 2]>>(_factory: &mut (),
159 _format: Format,
160 memory: &[u8],
161 size: S,
162 settings: &TextureSettings)
163 -> Result<Self, Self::Error> {
164 let size = size.into();
165 let mut id: GLuint = 0;
166 unsafe {
167 gl::GenTextures(1, &mut id);
168 gl::BindTexture(gl::TEXTURE_2D, id);
169 gl::TexParameteri(gl::TEXTURE_2D,
170 gl::TEXTURE_MIN_FILTER,
171 settings.get_gl_min() as i32);
172 gl::TexParameteri(gl::TEXTURE_2D,
173 gl::TEXTURE_MAG_FILTER,
174 settings.get_gl_mag() as i32);
175 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
176 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
177 if settings.get_generate_mipmap() {
178 gl::GenerateMipmap(gl::TEXTURE_2D);
179 }
180 gl::TexImage2D(gl::TEXTURE_2D,
181 0,
182 gl::RGBA as i32,
183 size[0] as i32,
184 size[1] as i32,
185 0,
186 gl::RGBA,
187 gl::UNSIGNED_BYTE,
188 memory.as_ptr() as *const _);
189 }
190
191 Ok(Texture::new(id, size[0], size[1]))
192 }
193}
194
195impl UpdateTexture<()> for Texture {
196 type Error = String;
197
198 fn update<O: Into<[u32; 2]>, S: Into<[u32; 2]>>(&mut self,
199 _factory: &mut (),
200 _format: Format,
201 memory: &[u8],
202 offset: O,
203 size: S)
204 -> Result<(), Self::Error> {
205 let offset = offset.into();
206 let size = size.into();
207 unsafe {
208 gl::BindTexture(gl::TEXTURE_2D, self.id);
209 gl::TexSubImage2D(gl::TEXTURE_2D,
210 0,
211 offset[0] as i32,
212 offset[1] as i32,
213 size[0] as i32,
214 size[1] as i32,
215 gl::RGBA,
216 gl::UNSIGNED_BYTE,
217 memory.as_ptr() as *const _);
218 }
219
220 Ok(())
221 }
222}
223
224impl graphics::ImageSize for Texture {
225 fn get_size(&self) -> (u32, u32) {
226 (self.width, self.height)
227 }
228}