1use color::Color;
6use gl;
7use gl::types::*;
8use image;
9use image::{DynamicImage, ImageBuffer};
10use std::error::Error;
11use std::os::raw::c_void;
12use std::path::Path;
13use Vector;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Texture {
30 pub id: u32,
31 width: u32,
32 height: u32,
33 rgb_mode: RgbMode,
34}
35
36impl Texture {
37 pub fn new() -> Texture {
41 let mut id = 0;
42 unsafe {
43 gl::GenTextures(1, &mut id);
44 };
45 Texture {
46 id,
47 width: 0,
48 height: 0,
49 rgb_mode: RgbMode::RGBA,
50 }
51 }
52
53 pub unsafe fn from_data(data: *mut c_void, mode: RgbMode, width: u32, height: u32) -> Texture {
56 Texture {
57 id: Self::create(data, mode.as_gl(), width as i32, height as i32),
58 width: width as u32,
59 height: height as u32,
60 rgb_mode: mode,
61 }
62 }
63
64 pub fn from_slice(data: &mut [u8], mode: RgbMode, width: u32, height: u32) -> Texture {
66 Texture {
67 id: Self::create(
68 data.as_mut_ptr() as *mut c_void,
69 mode.as_gl(),
70 width as i32,
71 height as i32,
72 ),
73 width: width as u32,
74 height: height as u32,
75 rgb_mode: mode,
76 }
77 }
78
79 pub fn from_color(color: Color, sizes: Vector<u32>) -> Texture {
80 let length: usize = (sizes.x * sizes.y * 4) as usize;
81 let mut data: Vec<u8> = Vec::with_capacity(length);
82 let mut i: usize = 0;
83 let color_u8: (u8, u8, u8, u8) = color.into();
84
85 while i < length {
86 data.push(color_u8.0);
87 data.push(color_u8.1);
88 data.push(color_u8.2);
89 data.push(color_u8.3);
90 i += 4;
91 }
92 Self::from_slice(data.as_mut_slice(), RgbMode::RGBA, sizes.x, sizes.y)
93 }
94
95 pub fn from_size(sizes: Vector<u32>) -> Texture {
97 let mut ve: Vec<u8> = vec![255; sizes.x as usize * sizes.y as usize * 4];
98 Self::from_slice(ve.as_mut_slice(), RgbMode::RGBA, sizes.x, sizes.y)
99 }
100
101 pub fn from_image(img: DynamicImage) -> Result<Texture, TextureError> {
103 let id;
104 let mut size = (0, 0);
105 let mode;
106
107 match img {
108 DynamicImage::ImageRgba8(data) => {
109 size.0 = data.width();
110 size.1 = data.height();
111 id = Self::create(
112 &data.into_raw()[0] as *const u8 as *mut std::ffi::c_void,
113 gl::RGBA,
114 size.0 as i32,
115 size.1 as i32,
116 );
117 mode = RgbMode::RGBA;
118 }
119 DynamicImage::ImageRgb8(data) => {
120 size.0 = data.width();
121 size.1 = data.height();
122 id = Self::create(
123 &data.into_raw()[0] as *const u8 as *mut std::ffi::c_void,
124 gl::RGB,
125 size.0 as i32,
126 size.1 as i32,
127 );
128 mode = RgbMode::RGB;
129 }
130 _ => {
131 return Err(TextureError::ImageLoading);
132 }
133 }
134
135 Ok(Texture {
136 id,
137 width: size.0,
138 height: size.1,
139 rgb_mode: mode,
140 })
141 }
142
143 pub fn from_path<P: AsRef<Path>>(path_to_file: P) -> Result<Texture, TextureError> {
145 if let Ok(img) = image::open(path_to_file) {
146 Texture::from_image(img)
147 } else {
148 Err(TextureError::FileError)
149 }
150 }
151
152 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
153 let data = self.get_data();
154
155 match self.rgb_mode {
156 RgbMode::RGBA => {
157 let image: Option<image::RgbaImage> =
158 ImageBuffer::from_vec(self.width, self.height, data);
159
160 if let Some(img) = image {
161 img.save(path)?;
162 } else {
163 return Err(Box::new(TextureError::WriteFile));
164 }
165 }
166 RgbMode::RGB => {
167 let image: Option<image::RgbImage> =
168 ImageBuffer::from_vec(self.width, self.height, data);
169
170 if let Some(img) = image {
171 img.save(path)?;
172 } else {
173 return Err(Box::new(TextureError::WriteFile));
174 }
175 }
176 _ => unimplemented!(),
177 }
178 Ok(())
179 }
180
181 fn create(data: *mut c_void, rgb_mode: GLenum, width: i32, height: i32) -> u32 {
183 let mut id = 0;
184 unsafe {
185 gl::GenTextures(1, &mut id);
186 gl::BindTexture(gl::TEXTURE_2D, id);
187 gl::TexStorage2D(
188 gl::TEXTURE_2D,
190 1,
191 if rgb_mode == gl::RGBA {
192 gl::RGBA8
193 } else {
194 gl::RGB8
195 },
196 width,
197 height,
198 );
199 gl::TexSubImage2D(
200 gl::TEXTURE_2D,
202 0,
203 0,
204 0,
205 width,
206 height,
207 rgb_mode,
208 gl::UNSIGNED_BYTE,
209 data,
210 );
211 Texture::default_param();
212 gl::GenerateMipmap(gl::TEXTURE_2D);
213 gl::BindTexture(gl::TEXTURE_2D, 0);
214 }
215 id
216 }
217
218 pub fn update_block<T, U>(
221 &mut self,
222 data: &[u8],
223 sizes: Vector<u32>,
224 pos: T,
225 rgb_mode: U,
226 ) -> Result<(), TextureError>
227 where
228 T: Into<Option<Vector<u32>>>,
229 U: Into<Option<RgbMode>>,
230 {
231 let pos = pos.into().unwrap_or_else(|| Vector::new(0, 0));
232 let rgb_mode = rgb_mode.into().unwrap_or(self.rgb_mode);
233 if data.is_empty() {
235 Ok(())
236 } else if pos.x + sizes.x > self.width || pos.y + sizes.y > self.height {
237 Err(TextureError::UpdateSize(
238 self.width,
239 pos.x + sizes.x,
240 self.height,
241 pos.y + sizes.y,
242 ))
243 } else {
244 unsafe {
246 gl::BindTexture(gl::TEXTURE_2D, self.id);
247 gl::TexSubImage2D(
248 gl::TEXTURE_2D,
249 0,
250 pos.x as i32,
251 pos.y as i32,
252 sizes.x as i32,
253 sizes.y as i32,
254 rgb_mode.as_gl(),
255 gl::UNSIGNED_BYTE,
256 data as *const _ as *const c_void, );
258 gl::BindTexture(gl::TEXTURE_2D, 0);
259 gl::Flush();
260 }
261 Ok(())
262 }
263 }
264
265 pub fn get_rawsize(&self) -> usize {
266 match self.rgb_mode {
267 RgbMode::RGBA => (self.height * self.width * 4) as usize,
268 RgbMode::RGB => (self.height * self.width * 3) as usize,
269 RgbMode::RED => (self.height * self.width) as usize,
270 }
271 }
272
273 pub fn get_data(&self) -> Vec<u8> {
275 let size = self.get_rawsize();
276 let mut data: Vec<u8> = Vec::with_capacity(size);
277
278 if size == 0 || self.id == 0 {
279 Vec::new()
280 } else {
281 unsafe {
282 data.set_len(size);
283 gl::BindTexture(gl::TEXTURE_2D, self.id);
284 gl::GetTexImage(
285 gl::TEXTURE_2D,
286 0,
287 self.rgb_mode.as_gl(),
288 gl::UNSIGNED_BYTE,
289 data.as_mut_ptr() as *mut c_void,
290 );
291 gl::BindTexture(gl::TEXTURE_2D, 0);
292 };
293 data
294 }
295 }
296
297 pub fn update_from_texture(
299 &mut self,
300 texture: &Texture,
301 pos: Vector<u32>,
302 ) -> Result<(), TextureError> {
303 let size = texture.get_rawsize();
304 let mut data: Vec<u8> = Vec::with_capacity(size);
305
306 if self.rgb_mode != texture.rgb_mode {
307 return Err(TextureError::UpdateMode(self.rgb_mode, texture.rgb_mode));
308 }
309 unsafe {
310 data.set_len(size);
311 gl::BindTexture(gl::TEXTURE_2D, texture.id);
312 gl::GetTexImage(
313 gl::TEXTURE_2D,
314 0,
315 self.rgb_mode.as_gl(),
316 gl::UNSIGNED_BYTE,
317 data.as_mut_ptr() as *mut c_void,
318 );
319 gl::BindTexture(gl::TEXTURE_2D, 0);
320 }
321 let h = texture.height;
322 let w = texture.width;
323 let mode = texture.rgb_mode;
324 self.update_block(data.as_slice(), Vector::new(w, h), pos, mode)
326 }
327
328 pub fn update<T>(&mut self, data: &[u8], mode: T) -> Result<(), TextureError>
330 where
331 T: Into<Option<RgbMode>>,
332 {
333 let mode = mode.into().unwrap_or(self.rgb_mode);
334 let w = self.width;
335 let h = self.height;
336
337 self.update_block(data, Vector::new(w, h), Vector::new(0, 0), mode)?;
338 Ok(())
339 }
340
341 pub fn repeat_mode(&self) {
345 self.active(0);
346 unsafe {
347 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
348
349 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
350 }
351 self.unbind();
352 }
353
354 pub fn linear_mode(&self) {
356 self.active(0);
357 unsafe {
358 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
359 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
360 }
361 self.unbind();
362 }
363
364 #[inline]
365 pub fn unbind(&self) {
367 unsafe {
368 gl::BindTexture(gl::TEXTURE_2D, 0);
369 }
370 }
371
372 #[inline]
373 pub fn active(&self, num: i32) {
375 unsafe {
376 gl::ActiveTexture(gl::TEXTURE0 + num as u32);
377 gl::BindTexture(gl::TEXTURE_2D, self.id);
378 }
379 }
380
381 unsafe fn default_param() {
382 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
383 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
384 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
385 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
386 }
387
388 pub fn rgb_mode(&self) -> &RgbMode {
392 &self.rgb_mode
393 }
394
395 pub fn width(&self) -> u32 {
397 self.width
398 }
399
400 pub fn height(&self) -> u32 {
402 self.height
403 }
404}
405
406impl Default for Texture {
407 fn default() -> Texture {
409 let mut id = 0;
410 unsafe {
411 let data = vec![255, 255, 255, 255];
412 gl::GenTextures(1, &mut id);
413 gl::BindTexture(gl::TEXTURE_2D, id);
414 Texture::default_param();
415 gl::TexImage2D(
416 gl::TEXTURE_2D,
417 0,
418 gl::RGBA as i32,
419 1,
420 1,
421 0,
422 gl::RGBA,
423 gl::UNSIGNED_BYTE,
424 data.as_ptr() as *const c_void,
425 );
426 };
427
428 Texture {
429 id,
430 width: 1,
431 height: 1,
432 rgb_mode: RgbMode::RGBA,
433 }
434 }
435}
436
437#[derive(PartialEq, Clone, Copy, Debug, Eq)]
439pub enum RgbMode {
440 RGBA,
441 RGB,
442 RED,
443}
444
445impl RgbMode {
446 pub fn as_gl(self) -> GLenum {
447 match self {
448 RgbMode::RGBA => gl::RGBA,
449 RgbMode::RGB => gl::RGB,
450 RgbMode::RED => gl::RED,
451 }
452 }
453}
454
455#[derive(Debug)]
456pub enum TextureError {
457 UpdateSize(u32, u32, u32, u32),
458 UpdateMode(RgbMode, RgbMode),
459 FileError,
460 ImageLoading,
461 WriteFile,
462}
463
464use std::fmt;
465
466impl fmt::Display for TextureError {
467 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468 match self {
469 TextureError::UpdateSize(x, new_x, y, new_y) => write!(
470 f,
471 "
472Error while updating texture: Sizes are not
473okay with this texture. x: {}
474< new_x: {} | y: {} new_y: {}",
475 x, new_x, y, new_y
476 ),
477 TextureError::FileError => write!(f, "Error while openning given file."),
478 TextureError::ImageLoading => write!(f, "Error While loading image."),
479 TextureError::WriteFile => write!(f, "Error while writing texture to file."),
480 TextureError::UpdateMode(a, b) => write!(
481 f,
482 "You were trying to update a {:?} with a {:?} Texture",
483 a, b
484 ),
485 }
486 }
487}
488
489impl Error for TextureError {
490 fn cause(&self) -> Option<&Error> {
491 None
492 }
493}
494
495impl Drop for Texture {
496 fn drop(&mut self) {
497 unsafe {
498 println!("Texture {} deleted", self.id);
499 gl::DeleteTextures(1, &[self.id] as *const _);
500 }
501 }
502}
503
504#[cfg(test)]
505mod test {
506 extern crate test;
507
508 use self::test::Bencher;
509 use super::Vector;
510 use color::Color;
511 use texture::RgbMode;
512 use texture::Texture;
513 use window::Window;
514
515 #[bench]
516 fn from_color(b: &mut Bencher) {
517 let _ = Window::new(200, 200, "Loader");
518
519 b.iter(|| {
520 Texture::from_color(Color::new(1.0, 1.0, 1.0), Vector::new(100, 100));
521 });
522 }
523
524 #[bench]
525 fn from_slice(b: &mut Bencher) {
526 let _ = Window::new(200, 200, "Loader");
527
528 b.iter(|| {
529 let mut slice = vec![255; 10000];
530
531 Texture::from_slice(slice.as_mut_slice(), RgbMode::RGBA, 100, 100);
532 });
533 }
534
535 #[bench]
536 fn update_block(b: &mut Bencher) {
537 let _ = Window::new(200, 200, "Loader");
538
539 let mut text_host = Texture::from_color(Color::new(0.0, 1.0, 0.0), Vector::new(100, 100));
540 let text_guest = Texture::from_color(Color::new(0.0, 0.0, 1.0), Vector::new(10, 10));
541
542 b.iter(|| {
543 text_host
544 .update_block(
545 text_guest.get_data().as_slice(),
546 Vector::new(10, 10),
547 Vector::new(10, 10),
548 None,
549 )
550 .unwrap();
551 });
552 }
553}