opengles_graphics/
texture.rs

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
56/// Wraps OpenGL texture data.
57/// The texture gets deleted when running out of scope.
58///
59/// In order to create a texture the function `GenTextures` must be loaded.
60/// This is done automatically by the window back-ends in Piston.
61pub struct Texture {
62    id: GLuint,
63    width: u32,
64    height: u32,
65}
66
67impl Texture {
68    /// Creates a new texture.
69    #[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    /// Gets the OpenGL id of the texture.
79    #[inline(always)]
80    pub fn get_id(&self) -> GLuint {
81        self.id
82    }
83
84    /// Returns empty texture.
85    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    /// Loads image from memory, the format is 8-bit greyscale.
94    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    /// Loads image by relative file name to the asset root.
105    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    /// Creates a texture from image.
126    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    /// Updates image with a new one.
132    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}