use core::ptr;
use core::mem;
use core::ops::Drop;
use gl;
use gl::types::*;
use super::{DataFormat, DataKind, Wrap, TextureKind, FilterMode};
#[derive(Debug, Clone, Hash)]
pub struct GLTexture {
id: GLuint,
kind: TextureKind,
width: usize,
height: usize,
format: DataFormat,
data_kind: DataKind,
filter: FilterMode,
wrap: Wrap,
mipmap: bool,
}
impl GLTexture {
#[inline(always)]
pub fn new_2d<T>(
width: usize,
height: usize,
format: DataFormat,
data_kind: DataKind,
filter: FilterMode,
wrap: Wrap,
generate_mipmap: bool,
data: &[T]
) -> Self {
let id = new_gl_texture();
let mipmap = unsafe {
gl::BindTexture(gl::TEXTURE_2D, id);
Self::gl_2d(width, height, format, data_kind, data);
Self::gl_wrap_filter_mipmap(width, height, filter, wrap, generate_mipmap)
};
GLTexture {
id: id,
kind: TextureKind::Texture2D,
width: width,
height: height,
format: format,
data_kind: data_kind,
filter: filter,
wrap: wrap,
mipmap: mipmap,
}
}
#[inline(always)]
pub fn new_null_2d(
width: usize,
height: usize,
format: DataFormat,
data_kind: DataKind,
filter: FilterMode,
wrap: Wrap,
generate_mipmap: bool
) -> Self {
let id = new_gl_texture();
let mipmap = unsafe {
gl::BindTexture(gl::TEXTURE_2D, id);
Self::gl_null_2d(width, height, format, data_kind);
Self::gl_wrap_filter_mipmap(width, height, filter, wrap, generate_mipmap)
};
GLTexture {
id: id,
kind: TextureKind::Texture2D,
width: width,
height: height,
format: format,
data_kind: data_kind,
filter: filter,
wrap: wrap,
mipmap: mipmap,
}
}
#[inline(always)]
pub fn resize_null_2d(&mut self, width: usize, height: usize) -> &Self {
self.width = width;
self.height = height;
unsafe {
gl::BindTexture(gl::TEXTURE_2D, self.id);
Self::gl_null_2d(self.width, self.height, self.format, self.data_kind);
if self.mipmap {
Self::gl_wrap_filter_mipmap(self.width, self.height, self.filter, self.wrap, true);
}
}
self
}
#[inline(always)]
pub fn id(&self) -> GLuint { self.id }
#[inline(always)]
pub fn kind(&self) -> TextureKind { self.kind }
#[inline(always)]
pub fn width(&self) -> usize { self.width }
#[inline(always)]
pub fn height(&self) -> usize { self.height }
#[inline(always)]
pub fn data_kind(&self) -> DataKind { self.data_kind }
#[inline(always)]
pub fn format(&self) -> DataFormat { self.format }
#[inline(always)]
pub fn filter(&self) -> FilterMode { self.filter }
#[inline(always)]
pub fn wrap(&self) -> Wrap { self.wrap }
#[inline(always)]
pub fn mipmap(&self) -> bool { self.mipmap }
#[inline]
pub fn bind(&self) -> &Self {
unsafe { gl::BindTexture(self.kind.to_gl(), self.id); }
self
}
#[inline]
pub fn unbind(&self) -> &Self {
unsafe { gl::BindTexture(self.kind.to_gl(), 0); }
self
}
#[inline]
pub fn gl_wrap_filter_mipmap(
width: usize,
height: usize,
filter: FilterMode,
wrap: Wrap,
generate_mipmap: bool
) -> bool {
let is_power_of_two = width.is_power_of_two() && height.is_power_of_two();
let (mag_filter, min_filter) = Self::mag_filter_min_filter(filter, is_power_of_two, generate_mipmap);
unsafe {
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag_filter as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min_filter as GLint);
let gl_wrap = wrap as GLint;
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl_wrap);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl_wrap);
if generate_mipmap && is_power_of_two {
gl::GenerateMipmap(gl::TEXTURE_2D);
}
}
generate_mipmap && is_power_of_two
}
#[inline]
pub fn gl_2d<T>(
width: usize,
height: usize,
format: DataFormat,
data_kind: DataKind,
data: &[T],
) {
unsafe {
gl::TexImage2D(
gl::TEXTURE_2D,
0,
format.to_gl() as GLint,
width as GLsizei,
height as GLsizei,
0,
format.to_gl(),
data_kind.to_gl(),
mem::transmute(data.as_ptr())
);
}
}
#[inline]
pub fn gl_null_2d(
width: usize,
height: usize,
format: DataFormat,
data_kind: DataKind
) {
unsafe {
gl::TexImage2D(
gl::TEXTURE_2D,
0,
format.to_gl() as GLint,
width as GLsizei,
height as GLsizei,
0,
format.to_gl(),
data_kind.to_gl(),
ptr::null()
);
}
}
#[inline]
pub fn mag_filter_min_filter(
filter: FilterMode,
is_power_of_two: bool,
generate_mipmap: bool
) -> (GLuint, GLuint) {
match filter {
FilterMode::None => (
gl::NEAREST,
if is_power_of_two && generate_mipmap {gl::LINEAR_MIPMAP_NEAREST} else {gl::NEAREST}
),
_ => (
gl::LINEAR,
if is_power_of_two && generate_mipmap {gl::LINEAR_MIPMAP_LINEAR} else {gl::LINEAR}
),
}
}
}
impl Drop for GLTexture {
#[inline]
fn drop(&mut self) {
if self.id != 0 {
unsafe { gl::DeleteTextures(1, &self.id); }
}
}
}
#[inline(always)]
fn new_gl_texture() -> GLuint {
let mut id = 0;
unsafe { gl::GenTextures(1, &mut id); }
id
}