[go: up one dir, main page]

glium 0.3.3

High-level and safe OpenGL wrapper.
use std::ptr;

use pixel_buffer::{self, PixelBuffer};
use texture::ClientFormat;

use context::Context;
use ContextExt;

use fbo;
use texture;

use GlObject;
use libc;
use context::CommandContext;
use gl;

pub fn read_attachment<P, T>(attachment: &fbo::Attachment, dimensions: (u32, u32),
                             context: &Context) -> T          // TODO: remove Clone for P
                             where P: texture::PixelValue + Clone + Send,
                             T: texture::Texture2dDataSink<Data = P>
{
    let mut ctxt = context.make_current();

    let (fbo, atch) = context.framebuffer_objects.as_ref().unwrap()
                            .get_framebuffer_for_reading(attachment, &mut ctxt);

    read_impl(fbo, atch, dimensions, None, &mut ctxt).unwrap()
}

/// Panics if the pixel buffer is not big enough.
pub fn read_attachment_to_pb<P, T>(attachment: &fbo::Attachment, dimensions: (u32, u32),
                                   dest: &mut PixelBuffer<T>, context: &Context)          // TODO: remove Clone for P
                                   where P: texture::PixelValue + Clone + Send,
                                   T: texture::Texture2dDataSink<Data = P>
{
    let mut ctxt = context.make_current();

    let (fbo, atch) = context.framebuffer_objects.as_ref().unwrap()
                            .get_framebuffer_for_reading(attachment, &mut ctxt);

    read_impl(fbo, atch, dimensions, Some(dest), &mut ctxt);
}

pub fn read_from_default_fb<P, T>(attachment: gl::types::GLenum, context: &Context) -> T          // TODO: remove Clone for P
                                  where P: texture::PixelValue + Clone + Send,
                                  T: texture::Texture2dDataSink<Data = P>
{
    let mut ctxt = context.make_current();

    let (w, h) = context.get_framebuffer_dimensions();
    let (w, h) = (w as u32, h as u32);      // TODO: remove this conversion
    read_impl(0, attachment, (w, h), None, &mut ctxt).unwrap()
}

/// Panics if the pixel buffer is not big enough.
pub fn read_from_default_fb_to_pb<P, T>(attachment: gl::types::GLenum,
                                        dest: &mut PixelBuffer<T>, context: &Context)          // TODO: remove Clone for P
                                        where P: texture::PixelValue + Clone + Send,
                                        T: texture::Texture2dDataSink<Data = P>
{
    let mut ctxt = context.make_current();
    let (w, h) = context.get_framebuffer_dimensions();
    read_impl(0, attachment, (w, h), Some(dest), &mut ctxt);
}

fn read_impl<P, T>(fbo: gl::types::GLuint, readbuffer: gl::types::GLenum,
                   dimensions: (u32, u32), target: Option<&mut PixelBuffer<T>>,
                   mut ctxt: &mut CommandContext) -> Option<T>          // TODO: remove Clone for P
                   where P: texture::PixelValue + Clone + Send,
                   T: texture::Texture2dDataSink<Data = P>
{
    use std::mem;

    let pixels_count = dimensions.0 * dimensions.1;

    let chosen_format = <T as texture::Texture2dDataSink>::get_preferred_formats()[0];
    let pixels_size = chosen_format.get_size();
    let (format, gltype) = client_format_to_gl_enum(&chosen_format);

    let total_data_size = pixels_count as usize * pixels_size;

    let pixel_buffer = target.as_ref().map(|buf| buf.get_id()).unwrap_or(0);

    if let Some(pixel_buffer) = target {
        assert!(pixel_buffer.get_size() >= total_data_size);
        pixel_buffer::store_infos(pixel_buffer, dimensions, chosen_format);
    }

    let data = unsafe {
        // binding framebuffer
        fbo::bind_framebuffer(&mut ctxt, fbo, false, true);

        // adjusting glReadBuffer
        ctxt.gl.ReadBuffer(readbuffer);

        // adjusting data alignement
        if ctxt.state.pixel_store_pack_alignment != 1 {
            ctxt.state.pixel_store_pack_alignment = 1;
            ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 1);
        }

        // binding buffer
        if ctxt.state.pixel_pack_buffer_binding != pixel_buffer {
            ctxt.gl.BindBuffer(gl::PIXEL_PACK_BUFFER, pixel_buffer);
            ctxt.state.pixel_pack_buffer_binding = pixel_buffer;
        }

        // reading
        if pixel_buffer == 0 {
            let data_size = pixels_count as usize * pixels_size / mem::size_of::<P>();
            let mut data: Vec<P> = Vec::with_capacity(data_size);
            ctxt.gl.ReadPixels(0, 0, dimensions.0 as gl::types::GLint,
                               dimensions.1 as gl::types::GLint, format, gltype,
                               data.as_mut_ptr() as *mut libc::c_void);
            data.set_len(data_size);
            Some(data)

        } else {
            ctxt.gl.ReadPixels(0, 0, dimensions.0 as gl::types::GLint,
                               dimensions.1 as gl::types::GLint, format, gltype,
                               ptr::null_mut());
            None
        }
    };

    if let Some(data) = data {
        let data = texture::RawImage2d {
            data: ::std::borrow::Cow::Owned(data),
            width: dimensions.0 as u32,
            height: dimensions.1 as u32,
            format: chosen_format,
        };

        Some(texture::Texture2dDataSink::from_raw(data))

    } else {

        None
    }
}

fn client_format_to_gl_enum(format: &ClientFormat) -> (gl::types::GLenum, gl::types::GLenum) {
    match *format {
        ClientFormat::U8 => (gl::RED, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8 => (gl::RG, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8U8 => (gl::RGB, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8U8U8 => (gl::RGBA, gl::UNSIGNED_BYTE),
        ClientFormat::I8 => (gl::RED, gl::BYTE),
        ClientFormat::I8I8 => (gl::RG, gl::BYTE),
        ClientFormat::I8I8I8 => (gl::RGB, gl::BYTE),
        ClientFormat::I8I8I8I8 => (gl::RGBA, gl::BYTE),
        ClientFormat::U16 => (gl::RED, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16 => (gl::RG, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16U16 => (gl::RGB, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16U16U16 => (gl::RGBA, gl::UNSIGNED_SHORT),
        ClientFormat::I16 => (gl::RED, gl::SHORT),
        ClientFormat::I16I16 => (gl::RG, gl::SHORT),
        ClientFormat::I16I16I16 => (gl::RGB, gl::SHORT),
        ClientFormat::I16I16I16I16 => (gl::RGBA, gl::SHORT),
        ClientFormat::U32 => (gl::RED, gl::UNSIGNED_INT),
        ClientFormat::U32U32 => (gl::RG, gl::UNSIGNED_INT),
        ClientFormat::U32U32U32 => (gl::RGB, gl::UNSIGNED_INT),
        ClientFormat::U32U32U32U32 => (gl::RGBA, gl::UNSIGNED_INT),
        ClientFormat::I32 => (gl::RED, gl::INT),
        ClientFormat::I32I32 => (gl::RG, gl::INT),
        ClientFormat::I32I32I32 => (gl::RGB, gl::INT),
        ClientFormat::I32I32I32I32 => (gl::RGBA, gl::INT),
        ClientFormat::U3U3U2 => (gl::RGB, gl::UNSIGNED_BYTE_3_3_2),
        ClientFormat::U5U6U5 => (gl::RGB, gl::UNSIGNED_SHORT_5_6_5),
        ClientFormat::U4U4U4U4 => (gl::RGBA, gl::UNSIGNED_SHORT_4_4_4_4),
        ClientFormat::U5U5U5U1 => (gl::RGBA, gl::UNSIGNED_SHORT_5_5_5_1),
        ClientFormat::U10U10U10U2 => (gl::RGBA, gl::UNSIGNED_INT_10_10_10_2),
        ClientFormat::F16 => (gl::RED, gl::HALF_FLOAT),
        ClientFormat::F16F16 => (gl::RG, gl::HALF_FLOAT),
        ClientFormat::F16F16F16 => (gl::RGB, gl::HALF_FLOAT),
        ClientFormat::F16F16F16F16 => (gl::RGBA, gl::HALF_FLOAT),
        ClientFormat::F32 => (gl::RED, gl::FLOAT),
        ClientFormat::F32F32 => (gl::RG, gl::FLOAT),
        ClientFormat::F32F32F32 => (gl::RGB, gl::FLOAT),
        ClientFormat::F32F32F32F32 => (gl::RGBA, gl::FLOAT),
    }
}