dear_imgui_rs/render/
draw_data.rs

1//! Draw data structures for Dear ImGui rendering
2//!
3//! This module provides safe Rust wrappers around Dear ImGui's draw data structures,
4//! which contain all the information needed to render a frame.
5
6use crate::internal::{RawCast, RawWrapper};
7use crate::sys;
8use crate::texture::TextureId;
9use std::marker::PhantomData;
10use std::rc::Rc;
11use std::slice;
12
13/// All draw data to render a Dear ImGui frame.
14#[repr(C)]
15pub struct DrawData {
16    /// Only valid after render() is called and before the next new frame() is called.
17    valid: bool,
18    /// Number of DrawList to render.
19    cmd_lists_count: i32,
20    /// For convenience, sum of all draw list index buffer sizes.
21    pub total_idx_count: i32,
22    /// For convenience, sum of all draw list vertex buffer sizes.
23    pub total_vtx_count: i32,
24    // Array of DrawList.
25    cmd_lists: crate::internal::ImVector<DrawList>,
26    /// Upper-left position of the viewport to render.
27    ///
28    /// (= upper-left corner of the orthogonal projection matrix to use)
29    pub display_pos: [f32; 2],
30    /// Size of the viewport to render.
31    ///
32    /// (= display_pos + display_size == lower-right corner of the orthogonal matrix to use)
33    pub display_size: [f32; 2],
34    /// Amount of pixels for each unit of display_size.
35    ///
36    /// Based on io.display_frame_buffer_scale. Typically [1.0, 1.0] on normal displays, and
37    /// [2.0, 2.0] on Retina displays, but fractional values are also possible.
38    pub framebuffer_scale: [f32; 2],
39
40    /// Viewport carrying the DrawData instance, might be of use to the renderer (generally not).
41    owner_viewport: *mut sys::ImGuiViewport,
42    /// Texture data (internal use)
43    textures: *mut crate::internal::ImVector<*mut sys::ImTextureData>,
44}
45
46unsafe impl RawCast<sys::ImDrawData> for DrawData {}
47
48impl RawWrapper for DrawData {
49    type Raw = sys::ImDrawData;
50
51    unsafe fn raw(&self) -> &Self::Raw {
52        unsafe { std::mem::transmute(self) }
53    }
54
55    unsafe fn raw_mut(&mut self) -> &mut Self::Raw {
56        unsafe { std::mem::transmute(self) }
57    }
58}
59
60impl DrawData {
61    /// Check if the draw data is valid
62    ///
63    /// Draw data is only valid after `Context::render()` is called and before
64    /// the next `Context::new_frame()` is called.
65    #[inline]
66    pub fn valid(&self) -> bool {
67        self.valid
68    }
69
70    /// Returns an iterator over the draw lists included in the draw data.
71    #[inline]
72    pub fn draw_lists(&self) -> DrawListIterator<'_> {
73        unsafe {
74            DrawListIterator {
75                iter: self.cmd_lists().iter(),
76            }
77        }
78    }
79    /// Returns the number of draw lists included in the draw data.
80    #[inline]
81    pub fn draw_lists_count(&self) -> usize {
82        self.cmd_lists_count.try_into().unwrap()
83    }
84
85    /// Returns an iterator over the textures that need to be updated
86    ///
87    /// This is used by renderer backends to process texture creation, updates, and destruction.
88    /// Each item is an `ImTextureData*` carrying a `Status` which can be one of:
89    /// - `OK`: nothing to do.
90    /// - `WantCreate`: create a GPU texture and upload all pixels.
91    /// - `WantUpdates`: upload specified `UpdateRect` regions.
92    /// - `WantDestroy`: destroy the GPU texture (may be delayed until unused).
93    /// Most of the time this list has only 1 texture and it doesn't need any update.
94    pub fn textures(&self) -> TextureIterator<'_> {
95        unsafe {
96            if self.textures.is_null() {
97                TextureIterator::new(std::ptr::null(), std::ptr::null())
98            } else {
99                let vector = &*self.textures;
100                TextureIterator::new(vector.data, vector.data.add(vector.size as usize))
101            }
102        }
103    }
104
105    /// Returns the number of textures in the texture list
106    pub fn textures_count(&self) -> usize {
107        unsafe {
108            if self.textures.is_null() {
109                0
110            } else {
111                (*self.textures).size as usize
112            }
113        }
114    }
115
116    /// Get a specific texture by index
117    ///
118    /// Returns None if the index is out of bounds or no textures are available.
119    pub fn texture(&self, index: usize) -> Option<&crate::texture::TextureData> {
120        unsafe {
121            if self.textures.is_null() {
122                return None;
123            }
124            let vector = &*self.textures;
125            if index >= vector.size as usize {
126                return None;
127            }
128            let texture_ptr = *vector.data.add(index);
129            if texture_ptr.is_null() {
130                return None;
131            }
132            Some(crate::texture::TextureData::from_raw(texture_ptr))
133        }
134    }
135
136    /// Get a mutable reference to a specific texture by index
137    ///
138    /// Returns None if the index is out of bounds or no textures are available.
139    pub fn texture_mut(&mut self, index: usize) -> Option<&mut crate::texture::TextureData> {
140        unsafe {
141            if self.textures.is_null() {
142                return None;
143            }
144            let vector = &*self.textures;
145            if index >= vector.size as usize {
146                return None;
147            }
148            let texture_ptr = *vector.data.add(index);
149            if texture_ptr.is_null() {
150                return None;
151            }
152            Some(crate::texture::TextureData::from_raw(texture_ptr))
153        }
154    }
155    /// Get the display position as an array
156    #[inline]
157    pub fn display_pos(&self) -> [f32; 2] {
158        self.display_pos
159    }
160
161    /// Get the display size as an array
162    #[inline]
163    pub fn display_size(&self) -> [f32; 2] {
164        self.display_size
165    }
166
167    /// Get the framebuffer scale as an array
168    #[inline]
169    pub fn framebuffer_scale(&self) -> [f32; 2] {
170        self.framebuffer_scale
171    }
172
173    #[inline]
174    pub(crate) unsafe fn cmd_lists(&self) -> &[*const DrawList] {
175        unsafe {
176            if self.cmd_lists_count <= 0 || self.cmd_lists.data.is_null() {
177                return &[];
178            }
179            slice::from_raw_parts(
180                self.cmd_lists.data as *const *const DrawList,
181                self.cmd_lists_count as usize,
182            )
183        }
184    }
185
186    /// Converts all buffers from indexed to non-indexed, in case you cannot render indexed buffers
187    ///
188    /// **This is slow and most likely a waste of resources. Always prefer indexed rendering!**
189    #[doc(alias = "DeIndexAllBuffers")]
190    pub fn deindex_all_buffers(&mut self) {
191        unsafe {
192            sys::ImDrawData_DeIndexAllBuffers(RawWrapper::raw_mut(self));
193        }
194    }
195
196    /// Scales the clip rect of each draw command
197    ///
198    /// Can be used if your final output buffer is at a different scale than Dear ImGui expects,
199    /// or if there is a difference between your window resolution and framebuffer resolution.
200    #[doc(alias = "ScaleClipRects")]
201    pub fn scale_clip_rects(&mut self, fb_scale: [f32; 2]) {
202        unsafe {
203            let scale = sys::ImVec2 {
204                x: fb_scale[0],
205                y: fb_scale[1],
206            };
207            sys::ImDrawData_ScaleClipRects(RawWrapper::raw_mut(self), scale);
208        }
209    }
210}
211
212/// Iterator over draw lists
213pub struct DrawListIterator<'a> {
214    iter: std::slice::Iter<'a, *const DrawList>,
215}
216
217impl<'a> Iterator for DrawListIterator<'a> {
218    type Item = &'a DrawList;
219
220    fn next(&mut self) -> Option<Self::Item> {
221        self.iter.next().map(|&ptr| unsafe { &*ptr })
222    }
223}
224
225impl<'a> ExactSizeIterator for DrawListIterator<'a> {
226    fn len(&self) -> usize {
227        self.iter.len()
228    }
229}
230
231/// Draw command list
232#[repr(transparent)]
233pub struct DrawList(sys::ImDrawList);
234
235impl RawWrapper for DrawList {
236    type Raw = sys::ImDrawList;
237    #[inline]
238    unsafe fn raw(&self) -> &sys::ImDrawList {
239        &self.0
240    }
241    #[inline]
242    unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList {
243        &mut self.0
244    }
245}
246
247impl DrawList {
248    #[inline]
249    pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
250        unsafe {
251            let cmd_buffer = &self.0.CmdBuffer;
252            if cmd_buffer.Size <= 0 || cmd_buffer.Data.is_null() {
253                return &[];
254            }
255            slice::from_raw_parts(cmd_buffer.Data, cmd_buffer.Size as usize)
256        }
257    }
258
259    /// Returns an iterator over the draw commands in this draw list
260    pub fn commands(&self) -> DrawCmdIterator<'_> {
261        unsafe {
262            DrawCmdIterator {
263                iter: self.cmd_buffer().iter(),
264            }
265        }
266    }
267
268    /// Get vertex buffer as slice
269    pub fn vtx_buffer(&self) -> &[DrawVert] {
270        unsafe {
271            let vtx_buffer = &self.0.VtxBuffer;
272            slice::from_raw_parts(vtx_buffer.Data as *const DrawVert, vtx_buffer.Size as usize)
273        }
274    }
275
276    /// Get index buffer as slice
277    pub fn idx_buffer(&self) -> &[DrawIdx] {
278        unsafe {
279            let idx_buffer = &self.0.IdxBuffer;
280            slice::from_raw_parts(idx_buffer.Data, idx_buffer.Size as usize)
281        }
282    }
283}
284
285/// Iterator over draw commands
286pub struct DrawCmdIterator<'a> {
287    iter: slice::Iter<'a, sys::ImDrawCmd>,
288}
289
290impl<'a> Iterator for DrawCmdIterator<'a> {
291    type Item = DrawCmd;
292
293    fn next(&mut self) -> Option<Self::Item> {
294        self.iter.next().map(|cmd| {
295            let cmd_params = DrawCmdParams {
296                clip_rect: [
297                    cmd.ClipRect.x,
298                    cmd.ClipRect.y,
299                    cmd.ClipRect.z,
300                    cmd.ClipRect.w,
301                ],
302                // Use raw field; backends may resolve effective TexID later
303                texture_id: TextureId::from(cmd.TexRef._TexID),
304                vtx_offset: cmd.VtxOffset as usize,
305                idx_offset: cmd.IdxOffset as usize,
306            };
307
308            // Check for special callback values
309            match cmd.UserCallback {
310                Some(raw_callback) if raw_callback as usize == (-1isize) as usize => {
311                    DrawCmd::ResetRenderState
312                }
313                Some(raw_callback) => DrawCmd::RawCallback {
314                    callback: raw_callback,
315                    raw_cmd: cmd,
316                },
317                None => DrawCmd::Elements {
318                    count: cmd.ElemCount as usize,
319                    cmd_params,
320                    raw_cmd: cmd as *const sys::ImDrawCmd,
321                },
322            }
323        })
324    }
325}
326
327/// Parameters for a draw command
328#[derive(Copy, Clone, Debug, PartialEq)]
329pub struct DrawCmdParams {
330    /// Clipping rectangle [left, top, right, bottom]
331    pub clip_rect: [f32; 4],
332    /// Texture ID to use for rendering
333    ///
334    /// Notes:
335    /// - For legacy paths (plain `TextureId`), this is the effective id.
336    /// - With the modern texture system (ImTextureRef/ImTextureData), this may be 0.
337    ///   Renderer backends should resolve the effective id at bind time using
338    ///   `ImDrawCmd_GetTexID` with the `raw_cmd` pointer and the backend render state.
339    pub texture_id: TextureId,
340    /// Vertex buffer offset
341    pub vtx_offset: usize,
342    /// Index buffer offset
343    pub idx_offset: usize,
344}
345
346/// A draw command
347#[derive(Clone, Debug)]
348pub enum DrawCmd {
349    /// Elements to draw
350    Elements {
351        /// The number of indices used for this draw command
352        count: usize,
353        cmd_params: DrawCmdParams,
354        /// Raw command pointer for backends
355        ///
356        /// Backend note: when using the modern texture system, resolve the effective
357        /// texture id at bind time via `ImDrawCmd_GetTexID(raw_cmd)` together with your
358        /// renderer state. This pointer is only valid during the `render_draw_data()`
359        /// call that produced it; do not store it.
360        raw_cmd: *const sys::ImDrawCmd,
361    },
362    /// Reset render state
363    ResetRenderState,
364    /// Raw callback
365    RawCallback {
366        callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
367        raw_cmd: *const sys::ImDrawCmd,
368    },
369}
370
371/// Vertex format used by Dear ImGui
372#[repr(C)]
373#[derive(Copy, Clone, Debug, PartialEq)]
374pub struct DrawVert {
375    /// Position (2D)
376    pub pos: [f32; 2],
377    /// UV coordinates
378    pub uv: [f32; 2],
379    /// Color (packed RGBA)
380    pub col: u32,
381}
382
383impl DrawVert {
384    /// Creates a new draw vertex with u32 color
385    pub fn new(pos: [f32; 2], uv: [f32; 2], col: u32) -> Self {
386        Self { pos, uv, col }
387    }
388
389    /// Creates a new draw vertex from RGBA bytes
390    pub fn from_rgba(pos: [f32; 2], uv: [f32; 2], rgba: [u8; 4]) -> Self {
391        let col = ((rgba[3] as u32) << 24)
392            | ((rgba[2] as u32) << 16)
393            | ((rgba[1] as u32) << 8)
394            | (rgba[0] as u32);
395        Self { pos, uv, col }
396    }
397
398    /// Extracts RGBA bytes from the packed color
399    pub fn rgba(&self) -> [u8; 4] {
400        [
401            (self.col & 0xFF) as u8,
402            ((self.col >> 8) & 0xFF) as u8,
403            ((self.col >> 16) & 0xFF) as u8,
404            ((self.col >> 24) & 0xFF) as u8,
405        ]
406    }
407}
408
409/// Index type used by Dear ImGui
410pub type DrawIdx = u16;
411
412/// A container for a heap-allocated deep copy of a `DrawData` struct.
413///
414/// Notes on thread-safety:
415/// - This type intentionally does NOT implement `Send`/`Sync` because it currently retains
416///   a pointer to the engine-managed textures list (`ImVector<ImTextureData*>`) instead of
417///   deep-copying it. That list can be mutated by the UI thread across frames.
418/// - You may move vertices/indices to another thread by extracting them into your own buffers
419///   or by implementing a custom deep copy which snapshots the textures list as well.
420///
421/// The underlying copy is released when this struct is dropped.
422pub struct OwnedDrawData {
423    draw_data: *mut sys::ImDrawData,
424    // Prevent Send/Sync: this struct retains a pointer to a shared textures list.
425    _no_send_sync: PhantomData<Rc<()>>,
426}
427
428impl OwnedDrawData {
429    /// If this struct contains a `DrawData` object, then this function returns a reference to it.
430    ///
431    /// Otherwise, this struct is empty and so this function returns `None`.
432    #[inline]
433    pub fn draw_data(&self) -> Option<&DrawData> {
434        if !self.draw_data.is_null() {
435            Some(unsafe { std::mem::transmute::<&sys::ImDrawData, &DrawData>(&*self.draw_data) })
436        } else {
437            None
438        }
439    }
440}
441
442impl Default for OwnedDrawData {
443    /// The default `OwnedDrawData` struct is empty.
444    #[inline]
445    fn default() -> Self {
446        Self {
447            draw_data: std::ptr::null_mut(),
448            _no_send_sync: PhantomData,
449        }
450    }
451}
452
453impl From<&DrawData> for OwnedDrawData {
454    /// Construct `OwnedDrawData` from `DrawData` by creating a heap-allocated deep copy of the given `DrawData`
455    fn from(value: &DrawData) -> Self {
456        unsafe {
457            // Allocate a new ImDrawData using the constructor
458            let result = sys::ImDrawData_ImDrawData();
459            if result.is_null() {
460                panic!("Failed to allocate ImDrawData for OwnedDrawData");
461            }
462
463            // Copy basic fields from the source
464            let source_ptr = RawWrapper::raw(value);
465            (*result).Valid = source_ptr.Valid;
466            (*result).TotalIdxCount = source_ptr.TotalIdxCount;
467            (*result).TotalVtxCount = source_ptr.TotalVtxCount;
468            (*result).DisplayPos = source_ptr.DisplayPos;
469            (*result).DisplaySize = source_ptr.DisplaySize;
470            (*result).FramebufferScale = source_ptr.FramebufferScale;
471            (*result).OwnerViewport = source_ptr.OwnerViewport;
472
473            // Copy draw lists by cloning each list to ensure OwnedDrawData owns its memory
474            (*result).CmdListsCount = 0;
475            if source_ptr.CmdListsCount > 0 && !source_ptr.CmdLists.Data.is_null() {
476                for i in 0..(source_ptr.CmdListsCount as usize) {
477                    let src_list = *source_ptr.CmdLists.Data.add(i);
478                    if !src_list.is_null() {
479                        // Clone the output of the draw list to own it
480                        let cloned = sys::ImDrawList_CloneOutput(src_list);
481                        if !cloned.is_null() {
482                            sys::ImDrawData_AddDrawList(result, cloned);
483                        }
484                    }
485                }
486            }
487
488            // Textures list is shared, do not duplicate (renderer treats it as read-only)
489            (*result).Textures = source_ptr.Textures;
490
491            OwnedDrawData {
492                draw_data: result,
493                _no_send_sync: PhantomData,
494            }
495        }
496    }
497}
498
499impl Drop for OwnedDrawData {
500    /// Releases any heap-allocated memory consumed by this `OwnedDrawData` object
501    fn drop(&mut self) {
502        unsafe {
503            if !self.draw_data.is_null() {
504                // Destroy cloned draw lists if any
505                if !(*self.draw_data).CmdLists.Data.is_null() {
506                    for i in 0..(*self.draw_data).CmdListsCount as usize {
507                        let ptr = *(*self.draw_data).CmdLists.Data.add(i);
508                        if !ptr.is_null() {
509                            sys::ImDrawList_destroy(ptr);
510                        }
511                    }
512                    // Free the CmdLists array storage
513                    sys::igMemFree((*self.draw_data).CmdLists.Data as *mut std::ffi::c_void);
514                }
515
516                // Destroy the ImDrawData itself
517                sys::ImDrawData_destroy(self.draw_data);
518                self.draw_data = std::ptr::null_mut();
519            }
520        }
521    }
522}
523
524/// Iterator over textures in draw data
525pub struct TextureIterator<'a> {
526    ptr: *const *mut sys::ImTextureData,
527    end: *const *mut sys::ImTextureData,
528    _phantom: std::marker::PhantomData<&'a crate::texture::TextureData>,
529}
530
531impl<'a> TextureIterator<'a> {
532    /// Create a new texture iterator from raw pointers
533    ///
534    /// # Safety
535    ///
536    /// The caller must ensure that the pointers are valid and that the range
537    /// [ptr, end) contains valid texture data pointers.
538    pub(crate) unsafe fn new(
539        ptr: *const *mut sys::ImTextureData,
540        end: *const *mut sys::ImTextureData,
541    ) -> Self {
542        Self {
543            ptr,
544            end,
545            _phantom: std::marker::PhantomData,
546        }
547    }
548}
549
550impl<'a> Iterator for TextureIterator<'a> {
551    type Item = &'a mut crate::texture::TextureData;
552
553    fn next(&mut self) -> Option<Self::Item> {
554        if self.ptr >= self.end {
555            None
556        } else {
557            unsafe {
558                let texture_ptr = *self.ptr;
559                self.ptr = self.ptr.add(1);
560                if texture_ptr.is_null() {
561                    self.next() // Skip null pointers
562                } else {
563                    Some(crate::texture::TextureData::from_raw(texture_ptr))
564                }
565            }
566        }
567    }
568}
569
570impl<'a> std::iter::FusedIterator for TextureIterator<'a> {}