use super::error::*;
use super::render_target::*;
use gl;
use std::ptr;
use std::rc::*;
use std::ops::{Deref};
struct TextureRef {
texture_id: gl::types::GLuint,
}
#[derive(Clone)]
pub struct Texture {
texture: Rc<TextureRef>,
pub (super) texture_target: gl::types::GLuint,
pub (super) texture_format: gl::types::GLuint,
num_samples: usize,
pub (super) width: gl::types::GLsizei,
pub (super) height: gl::types::GLsizei,
}
impl Texture {
pub fn new() -> Texture {
unsafe {
let mut new_texture = 0;
gl::GenTextures(1, &mut new_texture);
Texture {
texture: Rc::new(TextureRef { texture_id: new_texture }),
texture_target: gl::TEXTURE_2D,
texture_format: gl::RGBA,
num_samples: 0,
width: 0,
height: 0
}
}
}
pub fn create_empty(&mut self, width: u16, height: u16) {
unsafe {
let texture_id = self.texture.texture_id;
self.texture_target = gl::TEXTURE_2D;
self.texture_format = gl::RGBA;
self.width = width as _;
self.height = height as _;
gl::BindTexture(gl::TEXTURE_2D, texture_id);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as _, width as _, height as _, 0, gl::RGBA, gl::UNSIGNED_BYTE, ptr::null());
panic_on_gl_error("Create texture");
}
}
pub fn create_empty_multisampled(&mut self, width: u16, height: u16, samples: usize) {
unsafe {
let texture_id = self.texture.texture_id;
self.texture_target = gl::TEXTURE_2D_MULTISAMPLE;
self.texture_format = gl::RGBA;
self.width = width as _;
self.height = height as _;
let mut max_samples = 1;
gl::GetIntegerv(gl::MAX_COLOR_TEXTURE_SAMPLES, &mut max_samples);
let samples = max_samples.min(samples as i32);
self.num_samples = samples as _;
gl::BindTexture(gl::TEXTURE_2D_MULTISAMPLE, texture_id);
gl::TexImage2DMultisample(gl::TEXTURE_2D_MULTISAMPLE, samples, gl::RGBA, width as _, height as _, gl::FALSE);
panic_on_gl_error("Create multisampled texture");
}
}
pub fn create_monochrome(&mut self, width: u16, height: u16) {
unsafe {
let texture_id = self.texture.texture_id;
self.texture_target = gl::TEXTURE_2D;
self.texture_format = gl::RED;
self.width = width as _;
self.height = height as _;
gl::BindTexture(gl::TEXTURE_2D, texture_id);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RED as _, width as _, height as _, 0, gl::RED, gl::UNSIGNED_BYTE, ptr::null());
panic_on_gl_error("Create monochrome texture");
}
}
pub fn create_monochrome_multisampled(&mut self, width: u16, height: u16, samples: usize) {
unsafe {
let texture_id = self.texture.texture_id;
self.texture_target = gl::TEXTURE_2D_MULTISAMPLE;
self.texture_format = gl::RED;
self.width = width as _;
self.height = height as _;
let mut max_samples = 1;
gl::GetIntegerv(gl::MAX_COLOR_TEXTURE_SAMPLES, &mut max_samples);
let samples = max_samples.min(samples as i32);
self.num_samples = samples as _;
gl::BindTexture(gl::TEXTURE_2D_MULTISAMPLE, texture_id);
gl::TexImage2DMultisample(gl::TEXTURE_2D_MULTISAMPLE, samples, gl::RED, width as _, height as _, gl::FALSE);
panic_on_gl_error("Create monochrome multisampled texture");
}
}
pub fn create_empty_1d(&mut self, width: u16) {
unsafe {
let texture_id = self.texture.texture_id;
self.texture_target = gl::TEXTURE_1D;
self.texture_format = gl::RGBA;
self.width = width as _;
self.height = 1;
gl::BindTexture(gl::TEXTURE_1D, texture_id);
gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage1D(gl::TEXTURE_1D, 0, gl::RGBA as _, width as _, 0, gl::RGBA, gl::UNSIGNED_BYTE, ptr::null());
panic_on_gl_error("Create 1D texture");
}
}
pub fn create_monochrome_1d(&mut self, width: u16) {
unsafe {
let texture_id = self.texture.texture_id;
self.texture_target = gl::TEXTURE_1D;
self.texture_format = gl::RED;
self.width = width as _;
self.height = 1;
gl::BindTexture(gl::TEXTURE_1D, texture_id);
gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage1D(gl::TEXTURE_1D, 0, gl::RED as _, width as _, 0, gl::RED, gl::UNSIGNED_BYTE, ptr::null());
panic_on_gl_error("Create 1D mono texture");
}
}
pub fn generate_mipmaps(&mut self) {
unsafe {
gl::BindTexture(self.texture_target, self.texture.texture_id);
gl::GenerateMipmap(self.texture_target);
panic_on_gl_error("Generate texture mip map");
}
}
pub fn set_data_rgba(&mut self, x: usize, y: usize, width: usize, height: usize, pixels: &[u8]) {
if pixels.len() != (width * height * 4) {
panic!("set_data_bgra called with incorrect sized pixel array")
}
unsafe {
gl::BindTexture(self.texture_target, self.texture.texture_id);
gl::TexSubImage2D(gl::TEXTURE_2D, 0, x as _, y as _, width as _, height as _, gl::RGBA, gl::UNSIGNED_BYTE, pixels.as_ptr() as _);
panic_on_gl_error("Set rgba data");
}
}
pub fn set_data_mono(&mut self, x: usize, y: usize, width: usize, height: usize, pixels: &[u8]) {
if pixels.len() != width * height {
panic!("set_data_mono called with incorrect sized pixel array")
}
unsafe {
gl::BindTexture(self.texture_target, self.texture.texture_id);
gl::TexSubImage2D(gl::TEXTURE_2D, 0, x as _, y as _, width as _, height as _, gl::RED, gl::UNSIGNED_BYTE, pixels.as_ptr() as _);
panic_on_gl_error("Set mono data");
}
}
pub fn set_data_rgba_1d(&mut self, x: usize, width: usize, pixels: &[u8]) {
if pixels.len() != width * 4 {
panic!("set_data_bgra_1d called with incorrect sized pixel array")
}
unsafe {
gl::BindTexture(self.texture_target, self.texture.texture_id);
gl::TexSubImage1D(gl::TEXTURE_1D, 0, x as _, width as _, gl::RGBA, gl::UNSIGNED_BYTE, pixels.as_ptr() as _);
panic_on_gl_error("Set rgba 1D data");
}
}
pub fn set_data_mono_1d(&mut self, x: usize, width: usize, pixels: &[u8]) {
if pixels.len() != width {
panic!("set_data_mono_1d called with incorrect sized pixel array")
}
unsafe {
gl::BindTexture(self.texture_target, self.texture.texture_id);
gl::TexSubImage1D(gl::TEXTURE_1D, 0, x as _, width as _, gl::RED, gl::UNSIGNED_BYTE, pixels.as_ptr() as _);
panic_on_gl_error("Set mono 1D data");
}
}
pub fn make_copy(&self) -> Option<Texture> {
unsafe {
let copy = Texture::new();
let texture_id = copy.texture.texture_id;
let format = self.texture_format;
let width = self.width;
let height = self.height;
let existing_texture = RenderTarget::from_texture(self)?;
match self.texture_target {
gl::TEXTURE_1D => {
gl::BindTexture(gl::TEXTURE_1D, texture_id);
gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage1D(gl::TEXTURE_1D, 0, format as _, width, 0, format, gl::UNSIGNED_BYTE, ptr::null());
panic_on_gl_error("Create 1D copy target");
}
gl::TEXTURE_2D => {
gl::BindTexture(gl::TEXTURE_2D, texture_id);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, format as _, width, height, 0, format, gl::UNSIGNED_BYTE, ptr::null());
panic_on_gl_error("Create 2D copy target");
}
gl::TEXTURE_2D_MULTISAMPLE => {
let mut max_samples = 1;
let samples = self.num_samples;
gl::GetIntegerv(gl::MAX_COLOR_TEXTURE_SAMPLES, &mut max_samples);
let samples = max_samples.min(samples as i32);
gl::BindTexture(gl::TEXTURE_2D_MULTISAMPLE, texture_id);
gl::TexImage2DMultisample(gl::TEXTURE_2D_MULTISAMPLE, samples, gl::RGBA, width as _, height as _, gl::FALSE);
panic_on_gl_error("Create multisampled copy target");
}
_ => {
return None;
}
}
let mut old_frame_buffer = 0;
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut old_frame_buffer);
panic_on_gl_error("Get old framebuffer");
gl::BindFramebuffer(gl::FRAMEBUFFER, *existing_texture);
gl::ReadBuffer(gl::COLOR_ATTACHMENT0);
panic_on_gl_error("Bind new framebuffer");
match self.texture_target {
gl::TEXTURE_1D => {
gl::CopyTexImage1D(gl::TEXTURE_1D, 0, format, 0, 0, width, 0);
panic_on_gl_error("Copy 1D");
}
gl::TEXTURE_2D => {
gl::CopyTexImage2D(gl::TEXTURE_2D, 0, format, 0, 0, width, height, 0);
panic_on_gl_error("Copy 2D");
}
_ => { }
}
gl::BindFramebuffer(gl::FRAMEBUFFER, old_frame_buffer as _);
Some(copy)
}
}
pub fn is_mono(&self) -> bool {
self.texture_format == gl::RED
}
}
impl Drop for TextureRef {
fn drop(&mut self) {
unsafe {
gl::DeleteTextures(1, &mut self.texture_id);
}
}
}
impl Deref for Texture {
type Target = gl::types::GLuint;
fn deref(&self) -> &gl::types::GLuint {
&self.texture.texture_id
}
}