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 #[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}