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}