cgl_rs/graphics/
framebuffer.rs

1//! The framebuffer module for CGL
2
3#![allow(non_camel_case_types)]
4use libc::{c_void, c_int};
5
6/// The internal window handle used by CGL
7#[repr(C)]
8pub(crate) struct CGL_framebuffer {
9    _private: c_void
10}
11
12extern {
13    fn CGL_framebuffer_create_from_default(window: *mut crate::window::CGL_window) -> *mut CGL_framebuffer;
14    fn CGL_framebuffer_create(width: c_int, height: c_int) -> *mut CGL_framebuffer;
15    fn CGL_framebuffer_create_basic(width: c_int, height: c_int) -> *mut CGL_framebuffer;
16    fn CGL_framebuffer_add_color_attachment(framebuffer: *mut CGL_framebuffer, texture: *mut super::texture::CGL_texture) -> ();
17    fn CGL_framebuffer_destroy(framebuffer: *mut CGL_framebuffer) -> ();
18    fn CGL_framebuffer_get_color_attachment(framebuffer: *mut CGL_framebuffer, index: c_int) -> *mut super::texture::CGL_texture;
19    fn CGL_framebuffer_bind(framebuffer: *mut CGL_framebuffer) -> ();
20    fn CGL_framebuffer_get_size(framebuffer: *mut CGL_framebuffer, width: *mut c_int, height: *mut c_int) -> ();
21    fn CGL_framebuffer_set_user_data(framebuffer: *mut CGL_framebuffer, user_data: *mut c_void) -> ();
22    fn CGL_framebuffer_get_user_data(framebuffer: *mut CGL_framebuffer) -> *mut c_void;
23    fn CGL_framebuffer_read_pixels(framebuffer: *mut CGL_framebuffer, x: c_int, y: c_int, width: c_int, height: c_int, pixels: *mut c_void) -> ();
24    fn CGL_framebuffer_get_mouse_pick_id(framebuffer: *mut CGL_framebuffer, x: c_int, y: c_int, index: c_int) -> c_int;
25    fn CGL_framebuffer_get_color_texture(framebuffer: *mut CGL_framebuffer) -> *mut super::texture::CGL_texture;
26    fn CGL_framebuffer_get_depth_texture(framebuffer: *mut CGL_framebuffer) -> *mut super::texture::CGL_texture;
27}
28
29
30/// A framebuffer object that can be used for offscreen rendering.
31#[derive(Debug)]
32pub struct Framebuffer {
33    pub(crate) handle: *mut CGL_framebuffer,
34    pub(crate) has_been_destroyed: bool
35}
36
37
38impl Framebuffer {
39
40    /// Creates a new framebuffer object from the default window.
41    ///
42    /// # Arguments
43    ///
44    /// * `window` - A reference to the window object to create the framebuffer from.
45    ///
46    /// # Returns
47    ///
48    /// Returns a `Result` containing the newly created `Framebuffer` object or an error message if the creation failed.
49    /// 
50    /// # Example
51    /// 
52    /// ```
53    /// cgl_rs::init();
54    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
55    /// cgl_rs::graphics::init();
56    /// {
57    ///     let framebuffer = cgl_rs::graphics::Framebuffer::from_default(&window).unwrap();
58    /// }
59    /// cgl_rs::graphics::shutdown();
60    /// window.destroy();
61    /// cgl_rs::shutdown();
62    pub fn from_default(window: &crate::window::Window) -> Result<Framebuffer, &'static str> {
63        unsafe {
64            let result = CGL_framebuffer_create_from_default(window.get_cgl_handle());
65            if result.is_null() {
66                Err("Failed to create framebuffer from default")
67            } else {
68                Ok(Framebuffer {
69                    handle: result,
70                    has_been_destroyed: false
71                })
72            }
73        }
74    }
75
76    /// Creates a new framebuffer object with the specified width and height.
77    ///
78    /// # Arguments
79    ///
80    /// * `width` - The width of the framebuffer.
81    /// * `height` - The height of the framebuffer.
82    ///
83    /// # Returns
84    ///
85    /// Returns a `Result` containing the newly created `Framebuffer` object or an error message if the creation failed.
86    ///
87    /// # Example
88    ///
89    /// ```
90    /// cgl_rs::init();
91    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
92    /// cgl_rs::graphics::init();
93    /// {
94    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
95    /// }
96    /// cgl_rs::graphics::shutdown();
97    /// window.destroy();
98    /// cgl_rs::shutdown();
99    /// ```
100    pub fn new(width: i32, height: i32) -> Result<Framebuffer, &'static str> {
101        unsafe {
102            let result = CGL_framebuffer_create(width, height);
103            if result.is_null() {
104                Err("Failed to create framebuffer")
105            } else {
106                Ok(Framebuffer {
107                    handle: result,
108                    has_been_destroyed: false
109                })
110            }
111        }
112    }
113
114    /// Creates a new basic framebuffer object with the specified width and height.
115    ///
116    /// # Arguments
117    ///
118    /// * `width` - The width of the framebuffer.
119    /// * `height` - The height of the framebuffer.
120    ///
121    /// # Returns
122    ///
123    /// Returns a `Result` containing the newly created `Framebuffer` object or an error message if the creation failed.
124    ///
125    /// # Example
126    ///
127    /// ```
128    /// cgl_rs::init();
129    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
130    /// cgl_rs::graphics::init();
131    /// {
132    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new_basic(800, 600).unwrap();
133    /// }
134    /// cgl_rs::graphics::shutdown();
135    /// window.destroy();
136    /// cgl_rs::shutdown();
137    /// ```
138    pub fn new_basic(width: i32, height: i32) -> Result<Framebuffer, &'static str> {
139        unsafe {
140            let result = CGL_framebuffer_create_basic(width, height);
141            if result.is_null() {
142                Err("Failed to create framebuffer")
143            } else {
144                Ok(Framebuffer {
145                    handle: result,
146                    has_been_destroyed: false
147                })
148            }
149        }
150    }
151
152    /// Destroys the framebuffer object.
153    /// 
154    /// Note: This function is called automatically when the framebuffer object goes out of scope.
155    ///       But this can also be called manually to destroy the framebuffer object before it goes out of scope.
156    ///
157    /// # Example
158    ///
159    /// ```
160    /// cgl_rs::init();
161    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
162    /// cgl_rs::graphics::init();
163    /// {
164    ///     let mut framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
165    ///     framebuffer.destroy(); // This is not necessary, but can be called manually.
166    /// }
167    /// cgl_rs::graphics::shutdown();
168    /// window.destroy();
169    /// cgl_rs::shutdown();
170    /// ```
171    pub fn destroy(&mut self) {
172        if !self.has_been_destroyed {
173            unsafe {
174                CGL_framebuffer_destroy(self.handle);
175            }
176            self.has_been_destroyed = true;
177        }
178    }
179
180    /// Adds a color attachment to the framebuffer object.
181    ///
182    /// Note: The texture object passed in will be destroyed automatically after being added to the framebuffer object.
183    ///       So even if you clone the parent texture 
184    ///
185    /// # Arguments
186    ///
187    /// * `texture` - The texture object to be added as a color attachment to the framebuffer object.
188    ///
189    /// # Example
190    ///
191    /// ```
192    /// cgl_rs::init();
193    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
194    /// cgl_rs::graphics::init();
195    /// {
196    ///     let mut framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
197    ///     let texture = cgl_rs::graphics::Texture::dummy().unwrap();
198    ///     framebuffer.add_color_attachment(texture);
199    /// }
200    /// cgl_rs::graphics::shutdown();
201    /// window.destroy();
202    /// cgl_rs::shutdown();
203    /// ```
204    pub fn add_color_attachment(&mut self, mut texture: crate::graphics::texture::Texture) {
205        texture.has_been_destroyed = true;
206        unsafe {
207            CGL_framebuffer_add_color_attachment(self.handle, texture.handle);
208        }
209    }
210
211    /// Gets the color attachment at the specified index.
212    ///
213    /// # Arguments
214    ///
215    /// * `index` - The index of the color attachment to retrieve.
216    ///
217    /// # Returns
218    ///
219    /// Returns `Some(Texture)` if the color attachment exists, otherwise returns `None`.
220    /// This returned texture object is managed by the framebuffer object and will be 
221    /// destroyed automatically when the framebuffer object is destroyed.
222    ///
223    /// # Example
224    ///
225    /// ```
226    /// cgl_rs::init();
227    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
228    /// cgl_rs::graphics::init();
229    /// {
230    ///     let mut framebuffer = cgl_rs::graphics::Framebuffer::new_basic(800, 600).unwrap();
231    ///     let texture = cgl_rs::graphics::Texture::dummy().unwrap();
232    ///     framebuffer.add_color_attachment(texture);
233    ///     let color_attachment = framebuffer.get_color_atttachment(0);
234    ///     assert!(color_attachment.is_some());
235    /// }
236    /// cgl_rs::graphics::shutdown();
237    /// window.destroy();
238    /// cgl_rs::shutdown();
239    /// ```
240    pub fn get_color_atttachment(&self, index: i32) -> Option<crate::graphics::texture::Texture> {
241        unsafe {
242            let result = CGL_framebuffer_get_color_attachment(self.handle, index);
243            if result.is_null() {
244                None
245            } else {
246                Some(crate::graphics::texture::Texture {
247                    handle: result,
248                    has_been_destroyed: true
249                })
250            }
251        }
252    }
253
254
255    /// Binds the framebuffer object for rendering.
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// cgl_rs::init();
261    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
262    /// cgl_rs::graphics::init();
263    /// {
264    ///     let mut framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
265    ///     framebuffer.bind();
266    /// }
267    /// cgl_rs::graphics::shutdown();
268    /// window.destroy();
269    /// cgl_rs::shutdown();
270    /// ```
271    pub fn bind(&self) {
272        unsafe {
273            CGL_framebuffer_bind(self.handle);
274        }
275    }
276
277
278    // Gets the size of the framebuffer object.
279    ///
280    /// # Returns
281    ///
282    /// Returns a tuple containing the width and height of the framebuffer object.
283    ///
284    /// # Example
285    ///
286    /// ```
287    /// cgl_rs::init();
288    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
289    /// cgl_rs::graphics::init();
290    /// {
291    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
292    ///     let (width, height) = framebuffer.get_size();
293    ///     assert_eq!(width, 800);
294    ///     assert_eq!(height, 600);
295    /// }
296    /// cgl_rs::graphics::shutdown();
297    /// window.destroy();
298    /// cgl_rs::shutdown();
299    /// ```
300    pub fn get_size(&self) -> (i32, i32) {
301        unsafe {
302            let mut width = 0;
303            let mut height = 0;
304            CGL_framebuffer_get_size(self.handle, &mut width, &mut height);
305            (width, height)
306        }
307    }
308
309    /// Reads the pixels from the framebuffer object.
310    ///
311    /// Note: This function currently has some issues and may crash the program.
312    /// 
313    /// # Arguments
314    ///
315    /// * `x` - The x coordinate of the lower left corner of the rectangle of pixels to read.
316    /// * `y` - The y coordinate of the lower left corner of the rectangle of pixels to read.
317    /// * `width` - The width of the rectangle of pixels to read.
318    /// * `height` - The height of the rectangle of pixels to read.
319    /// 
320    ///
321    /// # Returns
322    ///
323    /// Returns a vector of bytes containing the pixel data.
324    ///
325    /// # Example
326    ///
327    /// ```no_run
328    /// cgl_rs::init();
329    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
330    /// cgl_rs::graphics::init();
331    /// {
332    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
333    ///     let pixels = framebuffer.read_pixels(0, 0, 100, 100);
334    ///     assert_eq!(pixels.len(), 100 * 100 * 4 * 4);
335    /// }
336    /// cgl_rs::graphics::shutdown();
337    /// window.destroy();
338    /// cgl_rs::shutdown();
339    /// ```
340    pub fn read_pixels(&self, x: i32, y: i32, width: i32, height: i32) -> Vec<u8> {
341        unsafe {
342            let mut buffer = Vec::with_capacity((width * height * 4) as usize);
343            buffer.set_len((width * height * 4 * 4) as usize);
344            CGL_framebuffer_read_pixels(self.handle, x, y, width, height, buffer.as_mut_ptr() as *mut std::ffi::c_void);
345            buffer          
346        }
347    }
348
349
350    /// Gets the mouse pick ID at the specified screen coordinates and index.
351    ///
352    /// # Arguments
353    ///
354    /// * `x` - The x coordinate of the screen position to check.
355    /// * `y` - The y coordinate of the screen position to check.
356    /// * `index` - The index of the mouse pick ID to retrieve.
357    ///
358    /// # Returns
359    ///
360    /// Returns the mouse pick ID at the specified screen coordinates and index.
361    ///
362    /// # Safety
363    ///
364    /// This function is marked as unsafe because it directly calls an unsafe C function.
365    ///
366    /// # Example
367    ///
368    /// ```
369    /// cgl_rs::init();
370    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
371    /// cgl_rs::graphics::init();
372    /// {
373    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
374    ///     let mouse_pick_id = framebuffer.get_mouse_pick_id(400, 300, 0);
375    /// }
376    /// cgl_rs::graphics::shutdown();
377    /// window.destroy();
378    /// cgl_rs::shutdown();
379    /// ```
380    pub fn get_mouse_pick_id(&self, x: i32, y: i32, index: i32) -> i32 {
381        unsafe {
382            CGL_framebuffer_get_mouse_pick_id(self.handle, x, y, index)
383        }
384    }
385
386    /// Gets the color texture of the framebuffer.
387    ///
388    /// # Returns
389    ///
390    /// Returns a `Texture` object representing the color texture of the framebuffer.
391    ///
392    /// # Safety
393    ///
394    /// This function is marked as unsafe because it directly calls an unsafe C function.
395    ///
396    /// # Example
397    ///
398    /// ```
399    /// cgl_rs::init();
400    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
401    /// cgl_rs::graphics::init();
402    /// {
403    ///    let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
404    ///    let texture = framebuffer.get_color_texture();
405    /// }
406    /// cgl_rs::graphics::shutdown();
407    /// window.destroy();
408    /// cgl_rs::shutdown();
409    /// ```
410    pub fn get_color_texture(&self) -> crate::graphics::Texture {
411        unsafe {
412            let handle = CGL_framebuffer_get_color_texture(self.handle);
413            crate::graphics::Texture {
414                handle,
415                has_been_destroyed: true
416            }
417        }
418    }
419
420    /// Gets the depth texture of the framebuffer.
421    ///
422    /// # Returns
423    ///
424    /// Returns a `Texture` object representing the depth texture of the framebuffer.
425    ///
426    /// # Safety
427    ///
428    /// This function is marked as unsafe because it directly calls an unsafe C function.
429    ///
430    /// # Example
431    ///
432    /// ```
433    /// cgl_rs::init();
434    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
435    /// cgl_rs::graphics::init();
436    /// {
437    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
438    ///     let depth_texture = framebuffer.get_depth_texture();
439    /// }
440    /// cgl_rs::graphics::shutdown();
441    /// window.destroy();
442    /// cgl_rs::shutdown();
443    /// ```
444    pub fn get_depth_texture(&self) -> crate::graphics::Texture {
445        unsafe {
446            let handle = CGL_framebuffer_get_depth_texture(self.handle);
447            crate::graphics::Texture {
448                handle,
449                has_been_destroyed: true
450            }
451        }
452    }
453}
454
455impl std::ops::Drop for Framebuffer {
456    fn drop(&mut self) {
457        self.destroy();
458    }
459}
460
461
462impl Clone for Framebuffer {
463    /// Clones the framebuffer object.
464    ///
465    /// NOTE: The new instance will have the same handle, `has_been_destroyed` flag.
466    ///       This means that the new instance will not be able to receive events nor will the internal window handle be
467    ///       destroyed when the new instance is dropped. The internal window handle will be destroyed when the original
468    ///       instance is dropped.
469    /// 
470    /// 
471    /// # Example
472    ///
473    /// ```
474    /// cgl_rs::init();
475    /// let mut window =  cgl_rs::Window::new("Hello World", 600, 800).unwrap();
476    /// cgl_rs::graphics::init();
477    /// {
478    ///     let framebuffer = cgl_rs::graphics::Framebuffer::new(800, 600).unwrap();
479    ///     let cloned_framebuffer = framebuffer.clone();
480    /// }
481    /// cgl_rs::graphics::shutdown();
482    /// window.destroy();
483    /// cgl_rs::shutdown();
484    /// ```
485    fn clone(&self) -> Self {
486        Framebuffer {
487            handle: self.handle.clone(),
488            has_been_destroyed: true
489        }
490    }
491}