pel 0.1.0

OpenGL backed framebuffer
Documentation
use std::{
    marker::PhantomData,
    mem::{self, size_of},
    ops::{Deref, DerefMut},
    ptr,
};

use ogl33::{
    consts::{
        GL_ARRAY_BUFFER, GL_BGRA, GL_CLAMP_TO_EDGE, GL_FALSE, GL_FLOAT, GL_NEAREST, GL_RGBA, GL_STATIC_DRAW,
        GL_TEXTURE0, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T,
        GL_TRIANGLES, GL_UNSIGNED_INT_8_8_8_8,
    },
    functions::{
        glActiveTexture, glBindBuffer, glBindTexture, glBindVertexArray, glBufferData, glDeleteBuffers,
        glDeleteTextures, glDeleteVertexArrays, glDrawArrays, glEnableVertexAttribArray, glGenBuffers, glGenTextures,
        glGenVertexArrays, glTexImage2D, glTexParameteri, glTexSubImage2D, glVertexAttribPointer,
    },
    types::{GLint, GLsizei, GLsizeiptr, GLuint, GLvoid},
};

use crate::{Pel, RawSurface, Surface};

static VERTICES: [f32; QUAD_VERTEX_BUFFER_LEN] = [
    -1.0, 1.0, 0.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0,
    1.0, 0.0,
];

const QUAD_VERTEX_BUFFER_LEN: usize = 24;
const QUAD_VERTEX_ELEMENTS: usize = 4;
const QUAD_VERTEX_COUNT: usize = QUAD_VERTEX_BUFFER_LEN / QUAD_VERTEX_ELEMENTS;

pub struct Texture {
    vao: GLuint,
    vbo: GLuint,
    tex: GLuint,
    inner: Surface,
}

impl Texture {
    pub(crate) fn new(w: usize, h: usize) -> Self {
        let mut vao: GLuint = 0;
        let mut vbo: GLuint = 0;
        let mut tex: GLuint = 0;

        unsafe {
            glGenVertexArrays(1, &mut vao);
            glBindVertexArray(vao);

            glGenBuffers(1, &mut vbo);
            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBufferData(
                GL_ARRAY_BUFFER,
                (VERTICES.len() * size_of::<f32>()) as GLsizeiptr,
                VERTICES.as_ptr() as *const GLvoid,
                GL_STATIC_DRAW,
            );

            glEnableVertexAttribArray(0);
            glVertexAttribPointer(0, QUAD_VERTEX_ELEMENTS as GLint, GL_FLOAT, GL_FALSE, 0, ptr::null());

            glGenTextures(1, &mut tex);

            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, tex);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE as GLint);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE as GLint);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST as GLint);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST as GLint);

            format_texture(w as GLsizei, h as GLsizei);
        }

        Self {
            vao,
            vbo,
            tex,
            inner: Surface::new(w, h),
        }
    }

    pub(crate) fn resize(&mut self, w: usize, h: usize) {
        mem::replace(&mut self.inner, Surface::new(w, h));
        unsafe {
            format_texture(w as GLsizei, h as GLsizei);
        }
    }

    pub(crate) fn texture(&mut self) -> TextureHandle {
        TextureHandle::new(&mut self.inner)
    }

    pub(crate) fn draw(&self) {
        unsafe {
            glDrawArrays(GL_TRIANGLES, 0, QUAD_VERTEX_COUNT as GLsizei);
        }
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            glDeleteVertexArrays(1, &self.vao);
            glDeleteBuffers(1, &self.vbo);
            glDeleteTextures(1, &self.tex);
        }
    }
}

/// GPU texture handle.
///
/// All pels are buffered and sent to GPU only once, when this temporary handle is dropped.
///
/// It is backed by a normal [`Surface`][1], its API is exposed using [`Deref`][2] and
/// [`DerefMut`][3] traits.
///
/// [1]: struct.Surface.html
/// [2]: https://doc.rust-lang.org/std/ops/trait.Deref.html
/// [3]: https://doc.rust-lang.org/std/ops/trait.DerefMut.html
pub struct TextureHandle<'s> {
    inner: &'s mut Surface,
    phantom: PhantomData<*const ()>, // !Send + !Sync
}

impl<'s> TextureHandle<'s> {
    fn new(inner: &'s mut Surface) -> Self {
        Self {
            inner,
            phantom: PhantomData,
        }
    }
}

impl<'s> Deref for TextureHandle<'s> {
    type Target = RawSurface<Pel>;

    fn deref(&self) -> &Self::Target {
        self.inner
    }
}

impl<'s> DerefMut for TextureHandle<'s> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.inner
    }
}

impl<'s> Drop for TextureHandle<'s> {
    fn drop(&mut self) {
        unsafe {
            update_texture(
                self.inner.width() as GLsizei,
                self.inner.height() as GLsizei,
                self.inner.as_ptr() as *const GLvoid,
            );
        }
    }
}

unsafe fn format_texture(w: GLsizei, h: GLsizei) {
    glTexImage2D(
        GL_TEXTURE_2D,
        0,
        GL_RGBA as GLint,
        w,
        h,
        0,
        GL_BGRA,
        GL_UNSIGNED_INT_8_8_8_8,
        ptr::null(),
    );
}

unsafe fn update_texture(w: GLsizei, h: GLsizei, data: *const GLvoid) {
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, data);
}