glium/ops/
read.rs

1use std::ptr;
2use std::fmt;
3use std::error::Error;
4
5use crate::pixel_buffer::PixelBuffer;
6use crate::texture::ClientFormat;
7use crate::texture::PixelValue;
8use crate::image_format::{TextureFormatRequest, TextureFormat};
9
10use crate::fbo;
11use crate::fbo::FramebuffersContainer;
12
13use crate::buffer::BufferAny;
14use crate::BufferExt;
15use crate::Rect;
16use crate::context::CommandContext;
17use crate::gl;
18
19use crate::version::Version;
20use crate::version::Api;
21
22/// A source for reading pixels.
23pub enum Source<'a> {
24    /// A regular framebuffer attachment.
25    Attachment(&'a fbo::RegularAttachment<'a>),
26    // TODO: use a Rust enum
27    DefaultFramebuffer(gl::types::GLenum),
28}
29
30impl<'a> From<&'a fbo::RegularAttachment<'a>> for Source<'a> {
31    #[inline]
32    fn from(a: &'a fbo::RegularAttachment<'a>) -> Source<'a> {
33        Source::Attachment(a)
34    }
35}
36
37/// A destination for reading pixels.
38pub enum Destination<'a, P> where P: PixelValue {
39    Memory(&'a mut Vec<P>),
40    PixelBuffer(&'a PixelBuffer<P>),
41    // TODO: texture with glCopyTexSubImage2D
42}
43
44impl<'a, P> From<&'a mut Vec<P>> for Destination<'a, P> where P: PixelValue {
45    #[inline]
46    fn from(mem: &'a mut Vec<P>) -> Destination<'a, P> {
47        Destination::Memory(mem)
48    }
49}
50
51impl<'a, P> From<&'a PixelBuffer<P>> for Destination<'a, P> where P: PixelValue {
52    #[inline]
53    fn from(pb: &'a PixelBuffer<P>) -> Destination<'a, P> {
54        Destination::PixelBuffer(pb)
55    }
56}
57
58/// Error that can happen while reading.
59#[derive(Debug)]
60pub enum ReadError {
61    /// The implementation doesn't support converting to the requested output format.
62    ///
63    /// OpenGL supports every possible format, but OpenGL ES only supports `(u8, u8, u8, u8)` and
64    /// an implementation-defined format.
65    OutputFormatNotSupported,
66
67    /// The implementation doesn't support reading a depth, depth-stencil or stencil attachment.
68    ///
69    /// OpenGL ES only supports reading from color buffers by default. There are extensions that
70    /// allow reading other types of attachments.
71    AttachmentTypeNotSupported,
72
73    /// Clamping the values is not supported by the implementation.
74    ClampingNotSupported,
75
76    // TODO: context lost
77}
78
79impl fmt::Display for ReadError {
80    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
81        use self::ReadError::*;
82        let desc = match *self {
83            OutputFormatNotSupported =>
84                "The implementation doesn't support converting to the requested output format",
85            AttachmentTypeNotSupported =>
86                "The implementation doesn't support reading a depth, depth-stencil or stencil attachment",
87            ClampingNotSupported =>
88                "Clamping the values is not supported by the implementation",
89        };
90        fmt.write_str(desc)
91    }
92}
93
94impl Error for ReadError {}
95
96/// Reads pixels from the source into the destination.
97///
98/// Panics if the destination is not large enough.
99///
100/// The `(u8, u8, u8, u8)` format is guaranteed to be supported.
101// TODO: differentiate between GL_* and GL_*_INTEGER
102#[inline]
103pub fn read<'a, S, D, T>(mut ctxt: &mut CommandContext<'_>, source: S, rect: &Rect, dest: D,
104                         clamp: bool) -> Result<(), ReadError>
105                         where S: Into<Source<'a>>, D: Into<Destination<'a, T>>,
106                               T: PixelValue
107{
108    let source = source.into();
109    let dest = dest.into();
110    let output_pixel_format = <T as PixelValue>::get_format();
111
112    let pixels_to_read = rect.width * rect.height;
113
114    // checking that the output format is supported
115    // OpenGL supported everything, while OpenGL ES only supports U8U8U8U8 plus an additional
116    // implementation-defined format
117    if ctxt.version >= &Version(Api::GlEs, 2, 0) && output_pixel_format != ClientFormat::U8U8U8U8 {
118        // TODO: GLES is guaranteed to support GL_RGBA and an implementation-defined format
119        //       queried with GL_IMPLEMENTATION_COLOR_READ_FORMAT. We only handle GL_RGBA.
120        return Err(ReadError::OutputFormatNotSupported);
121    }
122
123    // handling clamping
124    if ctxt.version >= &Version(Api::Gl, 3, 0) {
125        unsafe {
126            if clamp && ctxt.state.clamp_color != gl::TRUE as gl::types::GLenum {
127                ctxt.gl.ClampColor(gl::CLAMP_READ_COLOR, gl::TRUE as gl::types::GLenum);
128                ctxt.state.clamp_color = gl::TRUE as gl::types::GLenum;
129
130            } else if !clamp && ctxt.state.clamp_color != gl::FALSE as gl::types::GLenum {
131                ctxt.gl.ClampColor(gl::CLAMP_READ_COLOR, gl::FALSE as gl::types::GLenum);
132                ctxt.state.clamp_color = gl::FALSE as gl::types::GLenum;
133            }
134        }
135    } else if clamp {
136        return Err(ReadError::ClampingNotSupported);
137    }
138
139    // TODO: check dimensions?
140
141    // binding framebuffer
142    match source {
143        Source::Attachment(attachment) => {
144            unsafe { FramebuffersContainer::bind_framebuffer_for_reading(&mut ctxt, attachment) };
145        },
146        Source::DefaultFramebuffer(read_buffer) => {
147            FramebuffersContainer::bind_default_framebuffer_for_reading(&mut ctxt, read_buffer);
148        },
149    };
150
151    // determining what kind of data we are reading
152    enum ReadSourceType { Color, Depth, Stencil, DepthStencil }
153    let (integer, read_src_type) = match source {
154        Source::Attachment(attachment) => {
155            match attachment {
156                fbo::RegularAttachment::Texture(ref tex) => {
157                    let integer = match tex.get_texture().get_requested_format() {
158                        TextureFormatRequest::Specific(TextureFormat::UncompressedIntegral(_)) => true,
159                        TextureFormatRequest::Specific(TextureFormat::UncompressedUnsigned(_)) => true,
160                        TextureFormatRequest::AnyIntegral => true,
161                        TextureFormatRequest::AnyUnsigned => true,
162                        _ => false,
163                    };
164
165                    (integer, ReadSourceType::Color)       // FIXME: wrong
166                },
167                fbo::RegularAttachment::RenderBuffer(ref rb) => {
168                    (false, ReadSourceType::Color)       // FIXME: wrong
169                },
170            }
171        },
172        Source::DefaultFramebuffer(read_buffer) => {
173            (false, ReadSourceType::Color)       // FIXME: wrong
174        },
175    };
176
177    // OpenGL ES doesn't support reading from depth, stencil or depth-stencil attachments by default
178    if ctxt.version >= &Version(Api::GlEs, 2, 0) {
179        match read_src_type {
180            ReadSourceType::Color => (),
181            ReadSourceType::Depth => if !ctxt.extensions.gl_nv_read_depth {
182                return Err(ReadError::AttachmentTypeNotSupported);
183            },
184            ReadSourceType::DepthStencil => if !ctxt.extensions.gl_nv_read_depth_stencil {
185                return Err(ReadError::AttachmentTypeNotSupported);
186            },
187            ReadSourceType::Stencil => if !ctxt.extensions.gl_nv_read_stencil {
188                return Err(ReadError::AttachmentTypeNotSupported);
189            },
190        }
191    }
192
193    // obtaining the client format and client type to be passed to `glReadPixels`
194    let (format, gltype) = match read_src_type {
195        ReadSourceType::Color => {
196            client_format_to_gl_enum(&output_pixel_format, integer)
197        },
198        ReadSourceType::Depth => {
199            unimplemented!()        // TODO:
200            // TODO: NV_depth_buffer_float2
201            //(gl::DEPTH_COMPONENT, )
202        },
203        ReadSourceType::DepthStencil => unimplemented!(),        // FIXME: only 24_8 is possible and there's no client format in the enum that corresponds to 24_8
204        ReadSourceType::Stencil => {
205            unimplemented!()        // TODO:
206            //(gl::STENCIL_INDEX, )
207        },
208    };
209
210    // reading
211    unsafe {
212        match dest {
213            Destination::Memory(dest) => {
214                let mut buf = Vec::with_capacity(pixels_to_read as usize);
215
216                BufferAny::unbind_pixel_pack(ctxt);
217
218                // adjusting data alignement
219                let ptr = buf.as_mut_ptr() as *mut D;
220                let ptr = ptr as usize;
221                if (ptr % 8) == 0 {
222                } else if (ptr % 4) == 0 && ctxt.state.pixel_store_pack_alignment != 4 {
223                    ctxt.state.pixel_store_pack_alignment = 4;
224                    ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 4);
225                } else if (ptr % 2) == 0 && ctxt.state.pixel_store_pack_alignment > 2 {
226                    ctxt.state.pixel_store_pack_alignment = 2;
227                    ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 2);
228                } else if ctxt.state.pixel_store_pack_alignment != 1 {
229                    ctxt.state.pixel_store_pack_alignment = 1;
230                    ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 1);
231                }
232
233                ctxt.gl.ReadPixels(rect.left as gl::types::GLint, rect.bottom as gl::types::GLint,
234                                   rect.width as gl::types::GLsizei,
235                                   rect.height as gl::types::GLsizei, format, gltype,
236                                   buf.as_mut_ptr() as *mut _);
237                buf.set_len(pixels_to_read as usize);
238
239                *dest = buf;
240            },
241
242            Destination::PixelBuffer(pixel_buffer) => {
243                assert!(pixel_buffer.len() >= pixels_to_read as usize);
244
245                pixel_buffer.prepare_and_bind_for_pixel_pack(&mut ctxt);
246                ctxt.gl.ReadPixels(rect.left as gl::types::GLint, rect.bottom as gl::types::GLint,
247                                   rect.width as gl::types::GLsizei,
248                                   rect.height as gl::types::GLsizei, format, gltype,
249                                   ptr::null_mut());
250
251                crate::pixel_buffer::store_infos(pixel_buffer, (rect.width, rect.height));
252            }
253        }
254    };
255
256    Ok(())
257}
258
259fn client_format_to_gl_enum(format: &ClientFormat, integer: bool)
260                            -> (gl::types::GLenum, gl::types::GLenum)
261{
262    let (format, ty) = match *format {
263        ClientFormat::U8 => (gl::RED, gl::UNSIGNED_BYTE),
264        ClientFormat::U8U8 => (gl::RG, gl::UNSIGNED_BYTE),
265        ClientFormat::U8U8U8 => (gl::RGB, gl::UNSIGNED_BYTE),
266        ClientFormat::U8U8U8U8 => (gl::RGBA, gl::UNSIGNED_BYTE),
267        ClientFormat::I8 => (gl::RED, gl::BYTE),
268        ClientFormat::I8I8 => (gl::RG, gl::BYTE),
269        ClientFormat::I8I8I8 => (gl::RGB, gl::BYTE),
270        ClientFormat::I8I8I8I8 => (gl::RGBA, gl::BYTE),
271        ClientFormat::U16 => (gl::RED, gl::UNSIGNED_SHORT),
272        ClientFormat::U16U16 => (gl::RG, gl::UNSIGNED_SHORT),
273        ClientFormat::U16U16U16 => (gl::RGB, gl::UNSIGNED_SHORT),
274        ClientFormat::U16U16U16U16 => (gl::RGBA, gl::UNSIGNED_SHORT),
275        ClientFormat::I16 => (gl::RED, gl::SHORT),
276        ClientFormat::I16I16 => (gl::RG, gl::SHORT),
277        ClientFormat::I16I16I16 => (gl::RGB, gl::SHORT),
278        ClientFormat::I16I16I16I16 => (gl::RGBA, gl::SHORT),
279        ClientFormat::U32 => (gl::RED, gl::UNSIGNED_INT),
280        ClientFormat::U32U32 => (gl::RG, gl::UNSIGNED_INT),
281        ClientFormat::U32U32U32 => (gl::RGB, gl::UNSIGNED_INT),
282        ClientFormat::U32U32U32U32 => (gl::RGBA, gl::UNSIGNED_INT),
283        ClientFormat::I32 => (gl::RED, gl::INT),
284        ClientFormat::I32I32 => (gl::RG, gl::INT),
285        ClientFormat::I32I32I32 => (gl::RGB, gl::INT),
286        ClientFormat::I32I32I32I32 => (gl::RGBA, gl::INT),
287        ClientFormat::U3U3U2 => (gl::RGB, gl::UNSIGNED_BYTE_3_3_2),
288        ClientFormat::U5U6U5 => (gl::RGB, gl::UNSIGNED_SHORT_5_6_5),
289        ClientFormat::U4U4U4U4 => (gl::RGBA, gl::UNSIGNED_SHORT_4_4_4_4),
290        ClientFormat::U5U5U5U1 => (gl::RGBA, gl::UNSIGNED_SHORT_5_5_5_1),
291        ClientFormat::U1U5U5U5Reversed => (gl::RGBA, gl::UNSIGNED_SHORT_1_5_5_5_REV),
292        ClientFormat::U10U10U10U2 => (gl::RGBA, gl::UNSIGNED_INT_10_10_10_2),
293        ClientFormat::F16 => (gl::RED, gl::HALF_FLOAT),
294        ClientFormat::F16F16 => (gl::RG, gl::HALF_FLOAT),
295        ClientFormat::F16F16F16 => (gl::RGB, gl::HALF_FLOAT),
296        ClientFormat::F16F16F16F16 => (gl::RGBA, gl::HALF_FLOAT),
297        ClientFormat::F32 => (gl::RED, gl::FLOAT),
298        ClientFormat::F32F32 => (gl::RG, gl::FLOAT),
299        ClientFormat::F32F32F32 => (gl::RGB, gl::FLOAT),
300        ClientFormat::F32F32F32F32 => (gl::RGBA, gl::FLOAT),
301    };
302
303    let format = if integer {
304        match format {
305            gl::RED => gl::RED_INTEGER,
306            gl::RG => gl::RG_INTEGER,
307            gl::RGB => gl::RGB_INTEGER,
308            gl::RGBA => gl::RGBA_INTEGER,
309            _ => unreachable!()
310        }
311    } else {
312        format
313    };
314
315    (format, ty)
316}