gl_helpers/
gl_texture.rs

1use std::{mem, ptr};
2
3use gl;
4use gl::types::*;
5
6use super::{DataFormat, DataKind, FilterMode, InternalFormat, TextureKind, Wrap};
7
8#[derive(Debug, Hash)]
9pub struct GLTexture {
10    id: GLuint,
11    kind: TextureKind,
12    width: usize,
13    height: usize,
14    internal_format: InternalFormat,
15    format: DataFormat,
16    data_kind: DataKind,
17    filter: FilterMode,
18    wrap: Wrap,
19    mipmap: bool,
20}
21
22impl GLTexture {
23    #[inline(always)]
24    pub fn new_2d<T>(
25        width: usize,
26        height: usize,
27        internal_format: InternalFormat,
28        format: DataFormat,
29        data_kind: DataKind,
30        filter: FilterMode,
31        wrap: Wrap,
32        generate_mipmap: bool,
33        data: &[T],
34    ) -> Self {
35        let id = new_gl_texture();
36        let mipmap = unsafe {
37            gl::BindTexture(gl::TEXTURE_2D, id);
38            Self::gl_2d(width, height, internal_format, format, data_kind, data);
39            Self::gl_wrap_filter_mipmap(width, height, filter, wrap, generate_mipmap)
40        };
41
42        GLTexture {
43            id: id,
44            kind: TextureKind::Texture2D,
45            width: width,
46            height: height,
47            internal_format: internal_format,
48            format: format,
49            data_kind: data_kind,
50            filter: filter,
51            wrap: wrap,
52            mipmap: mipmap,
53        }
54    }
55
56    #[inline(always)]
57    pub fn new_null_2d(
58        width: usize,
59        height: usize,
60        internal_format: InternalFormat,
61        format: DataFormat,
62        data_kind: DataKind,
63        filter: FilterMode,
64        wrap: Wrap,
65        generate_mipmap: bool,
66    ) -> Self {
67        let id = new_gl_texture();
68        let mipmap = unsafe {
69            gl::BindTexture(gl::TEXTURE_2D, id);
70            Self::gl_null_2d(width, height, internal_format, format, data_kind);
71            Self::gl_wrap_filter_mipmap(width, height, filter, wrap, generate_mipmap)
72        };
73
74        GLTexture {
75            id: id,
76            kind: TextureKind::Texture2D,
77            width: width,
78            height: height,
79            internal_format: internal_format,
80            format: format,
81            data_kind: data_kind,
82            filter: filter,
83            wrap: wrap,
84            mipmap: mipmap,
85        }
86    }
87
88    #[inline(always)]
89    pub fn resize_null_2d(&mut self, width: usize, height: usize) -> &Self {
90        self.width = width;
91        self.height = height;
92
93        unsafe {
94            gl::BindTexture(gl::TEXTURE_2D, self.id);
95            Self::gl_null_2d(
96                self.width,
97                self.height,
98                self.internal_format,
99                self.format,
100                self.data_kind,
101            );
102            Self::gl_wrap_filter_mipmap(
103                self.width,
104                self.height,
105                self.filter,
106                self.wrap,
107                self.mipmap,
108            );
109        }
110        self
111    }
112
113    #[inline(always)]
114    pub fn id(&self) -> GLuint {
115        self.id
116    }
117    #[inline(always)]
118    pub fn kind(&self) -> TextureKind {
119        self.kind
120    }
121    #[inline(always)]
122    pub fn width(&self) -> usize {
123        self.width
124    }
125    #[inline(always)]
126    pub fn height(&self) -> usize {
127        self.height
128    }
129    #[inline(always)]
130    pub fn data_kind(&self) -> DataKind {
131        self.data_kind
132    }
133    #[inline(always)]
134    pub fn format(&self) -> DataFormat {
135        self.format
136    }
137    #[inline(always)]
138    pub fn filter(&self) -> FilterMode {
139        self.filter
140    }
141    #[inline(always)]
142    pub fn wrap(&self) -> Wrap {
143        self.wrap
144    }
145    #[inline(always)]
146    pub fn mipmap(&self) -> bool {
147        self.mipmap
148    }
149
150    #[inline]
151    pub fn bind(&self) -> &Self {
152        unsafe {
153            gl::BindTexture(self.kind.into(), self.id);
154        }
155        self
156    }
157    #[inline]
158    pub fn unbind(&self) -> &Self {
159        unsafe {
160            gl::BindTexture(self.kind.into(), 0);
161        }
162        self
163    }
164
165    #[inline]
166    pub fn gl_wrap_filter_mipmap(
167        width: usize,
168        height: usize,
169        filter: FilterMode,
170        wrap: Wrap,
171        generate_mipmap: bool,
172    ) -> bool {
173        let is_power_of_two = width.is_power_of_two() && height.is_power_of_two();
174        let (mag_filter, min_filter) =
175            Self::mag_filter_min_filter(filter, is_power_of_two, generate_mipmap);
176
177        unsafe {
178            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag_filter as GLint);
179            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min_filter as GLint);
180
181            let gl_wrap = GLenum::from(wrap) as GLint;
182            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl_wrap);
183            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl_wrap);
184
185            if generate_mipmap && is_power_of_two {
186                gl::GenerateMipmap(gl::TEXTURE_2D);
187            }
188        }
189
190        generate_mipmap && is_power_of_two
191    }
192
193    #[inline]
194    pub fn gl_2d<T>(
195        width: usize,
196        height: usize,
197        internal_format: InternalFormat,
198        format: DataFormat,
199        data_kind: DataKind,
200        data: &[T],
201    ) {
202        unsafe {
203            gl::TexImage2D(
204                gl::TEXTURE_2D,
205                0,
206                GLenum::from(internal_format) as GLint,
207                width as GLsizei,
208                height as GLsizei,
209                0,
210                format.into(),
211                data_kind.into(),
212                mem::transmute(data.as_ptr()),
213            );
214        }
215    }
216
217    #[inline]
218    pub fn gl_null_2d(
219        width: usize,
220        height: usize,
221        internal_format: InternalFormat,
222        format: DataFormat,
223        data_kind: DataKind,
224    ) {
225        unsafe {
226            gl::TexImage2D(
227                gl::TEXTURE_2D,
228                0,
229                GLenum::from(internal_format) as GLint,
230                width as GLsizei,
231                height as GLsizei,
232                0,
233                format.into(),
234                data_kind.into(),
235                ptr::null(),
236            );
237        }
238    }
239
240    // (mag_filter, min_filter)
241    #[inline]
242    pub fn mag_filter_min_filter(
243        filter: FilterMode,
244        is_power_of_two: bool,
245        generate_mipmap: bool,
246    ) -> (GLuint, GLuint) {
247        match filter {
248            FilterMode::None => (
249                gl::NEAREST,
250                if is_power_of_two && generate_mipmap {
251                    gl::LINEAR_MIPMAP_NEAREST
252                } else {
253                    gl::NEAREST
254                },
255            ),
256            _ => (
257                gl::LINEAR,
258                if is_power_of_two && generate_mipmap {
259                    gl::LINEAR_MIPMAP_LINEAR
260                } else {
261                    gl::LINEAR
262                },
263            ),
264        }
265    }
266}
267
268impl Drop for GLTexture {
269    #[inline]
270    fn drop(&mut self) {
271        if self.id != 0 {
272            unsafe {
273                gl::DeleteTextures(1, &self.id);
274            }
275        }
276    }
277}
278
279#[inline(always)]
280fn new_gl_texture() -> GLuint {
281    let mut id = 0;
282    unsafe {
283        gl::GenTextures(1, &mut id);
284    }
285    id
286}