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;
12use std::sync::atomic::{AtomicBool, Ordering};
13
14static TEXTURE_DATA_BORROWED: AtomicBool = AtomicBool::new(false);
15
16pub(crate) fn assert_texture_data_not_borrowed() {
17    if TEXTURE_DATA_BORROWED.load(Ordering::Acquire) {
18        panic!(
19            "TextureData is already mutably borrowed; \
20             do not mix DrawData::textures()/PlatformIo::textures() with DrawData::texture()/PlatformIo::texture() calls"
21        );
22    }
23}
24
25/// All draw data to render a Dear ImGui frame.
26#[repr(C)]
27pub struct DrawData {
28    /// Only valid after render() is called and before the next new frame() is called.
29    valid: bool,
30    /// Number of DrawList to render.
31    cmd_lists_count: i32,
32    /// For convenience, sum of all draw list index buffer sizes.
33    pub total_idx_count: i32,
34    /// For convenience, sum of all draw list vertex buffer sizes.
35    pub total_vtx_count: i32,
36    // Array of DrawList.
37    cmd_lists: crate::internal::ImVector<*mut sys::ImDrawList>,
38    /// Upper-left position of the viewport to render.
39    ///
40    /// (= upper-left corner of the orthogonal projection matrix to use)
41    pub display_pos: [f32; 2],
42    /// Size of the viewport to render.
43    ///
44    /// (= display_pos + display_size == lower-right corner of the orthogonal matrix to use)
45    pub display_size: [f32; 2],
46    /// Amount of pixels for each unit of display_size.
47    ///
48    /// Based on io.display_frame_buffer_scale. Typically [1.0, 1.0] on normal displays, and
49    /// [2.0, 2.0] on Retina displays, but fractional values are also possible.
50    pub framebuffer_scale: [f32; 2],
51
52    /// Viewport carrying the DrawData instance, might be of use to the renderer (generally not).
53    owner_viewport: *mut sys::ImGuiViewport,
54    /// Texture data (internal use)
55    textures: *mut crate::internal::ImVector<*mut sys::ImTextureData>,
56}
57
58// Keep this struct layout-compatible with the sys bindings (`ImDrawData`).
59const _: [(); std::mem::size_of::<sys::ImDrawData>()] = [(); std::mem::size_of::<DrawData>()];
60const _: [(); std::mem::align_of::<sys::ImDrawData>()] = [(); std::mem::align_of::<DrawData>()];
61
62unsafe impl RawCast<sys::ImDrawData> for DrawData {}
63
64impl RawWrapper for DrawData {
65    type Raw = sys::ImDrawData;
66
67    unsafe fn raw(&self) -> &Self::Raw {
68        unsafe { <Self as RawCast<Self::Raw>>::raw(self) }
69    }
70
71    unsafe fn raw_mut(&mut self) -> &mut Self::Raw {
72        unsafe { <Self as RawCast<Self::Raw>>::raw_mut(self) }
73    }
74}
75
76impl DrawData {
77    /// Check if the draw data is valid
78    ///
79    /// Draw data is only valid after `Context::render()` is called and before
80    /// the next `Context::new_frame()` is called.
81    #[inline]
82    pub fn valid(&self) -> bool {
83        self.valid
84    }
85
86    /// Returns an iterator over the draw lists included in the draw data.
87    #[inline]
88    pub fn draw_lists(&self) -> DrawListIterator<'_> {
89        unsafe {
90            DrawListIterator {
91                iter: self.cmd_lists().iter(),
92            }
93        }
94    }
95    /// Returns the number of draw lists included in the draw data.
96    #[inline]
97    pub fn draw_lists_count(&self) -> usize {
98        unsafe { self.cmd_lists().len() }
99    }
100
101    /// Returns an iterator over the textures that need to be updated
102    ///
103    /// This is used by renderer backends to process texture creation, updates, and destruction.
104    /// Each item is an `ImTextureData*` carrying a `Status` which can be one of:
105    /// - `OK`: nothing to do.
106    /// - `WantCreate`: create a GPU texture and upload all pixels.
107    /// - `WantUpdates`: upload specified `UpdateRect` regions.
108    /// - `WantDestroy`: destroy the GPU texture (may be delayed until unused).
109    /// Most of the time this list has only 1 texture and it doesn't need any update.
110    ///
111    /// Note: items returned by this iterator provide a guarded mutable view; do not store them or
112    /// hold them across iterations.
113    pub fn textures(&self) -> TextureIterator<'_> {
114        unsafe {
115            if self.textures.is_null() {
116                TextureIterator::new(std::ptr::null(), std::ptr::null())
117            } else {
118                let vector = &*self.textures;
119                if vector.size <= 0 || vector.data.is_null() {
120                    TextureIterator::new(std::ptr::null(), std::ptr::null())
121                } else {
122                    TextureIterator::new(vector.data, vector.data.add(vector.size as usize))
123                }
124            }
125        }
126    }
127
128    /// Returns the number of textures in the texture list
129    pub fn textures_count(&self) -> usize {
130        unsafe {
131            if self.textures.is_null() {
132                0
133            } else {
134                let vector = &*self.textures;
135                if vector.size <= 0 || vector.data.is_null() {
136                    0
137                } else {
138                    vector.size as usize
139                }
140            }
141        }
142    }
143
144    /// Get a specific texture by index
145    ///
146    /// Returns None if the index is out of bounds or no textures are available.
147    pub fn texture(&self, index: usize) -> Option<&crate::texture::TextureData> {
148        unsafe {
149            assert_texture_data_not_borrowed();
150            if self.textures.is_null() {
151                return None;
152            }
153            let vector = &*self.textures;
154            let size = usize::try_from(vector.size).ok()?;
155            if size == 0 || vector.data.is_null() {
156                return None;
157            }
158            if index >= size {
159                return None;
160            }
161            let texture_ptr = *vector.data.add(index);
162            if texture_ptr.is_null() {
163                return None;
164            }
165            Some(crate::texture::TextureData::from_raw_ref(
166                texture_ptr as *const _,
167            ))
168        }
169    }
170
171    /// Get a mutable reference to a specific texture by index
172    ///
173    /// Returns None if the index is out of bounds or no textures are available.
174    pub fn texture_mut(&mut self, index: usize) -> Option<&mut crate::texture::TextureData> {
175        unsafe {
176            if self.textures.is_null() {
177                return None;
178            }
179            let vector = &*self.textures;
180            let size = usize::try_from(vector.size).ok()?;
181            if size == 0 || vector.data.is_null() {
182                return None;
183            }
184            if index >= size {
185                return None;
186            }
187            let texture_ptr = *vector.data.add(index);
188            if texture_ptr.is_null() {
189                return None;
190            }
191            Some(crate::texture::TextureData::from_raw(texture_ptr))
192        }
193    }
194    /// Get the display position as an array
195    #[inline]
196    pub fn display_pos(&self) -> [f32; 2] {
197        self.display_pos
198    }
199
200    /// Get the display size as an array
201    #[inline]
202    pub fn display_size(&self) -> [f32; 2] {
203        self.display_size
204    }
205
206    /// Get the framebuffer scale as an array
207    #[inline]
208    pub fn framebuffer_scale(&self) -> [f32; 2] {
209        self.framebuffer_scale
210    }
211
212    #[inline]
213    pub(crate) unsafe fn cmd_lists(&self) -> &[*mut sys::ImDrawList] {
214        unsafe {
215            if self.cmd_lists_count <= 0 || self.cmd_lists.data.is_null() {
216                return &[];
217            }
218            let len = match usize::try_from(self.cmd_lists_count) {
219                Ok(len) => len,
220                Err(_) => return &[],
221            };
222            slice::from_raw_parts(self.cmd_lists.data, len)
223        }
224    }
225
226    /// Converts all buffers from indexed to non-indexed, in case you cannot render indexed buffers
227    ///
228    /// **This is slow and most likely a waste of resources. Always prefer indexed rendering!**
229    #[doc(alias = "DeIndexAllBuffers")]
230    pub fn deindex_all_buffers(&mut self) {
231        unsafe {
232            sys::ImDrawData_DeIndexAllBuffers(RawWrapper::raw_mut(self));
233        }
234    }
235
236    /// Scales the clip rect of each draw command
237    ///
238    /// Can be used if your final output buffer is at a different scale than Dear ImGui expects,
239    /// or if there is a difference between your window resolution and framebuffer resolution.
240    #[doc(alias = "ScaleClipRects")]
241    pub fn scale_clip_rects(&mut self, fb_scale: [f32; 2]) {
242        unsafe {
243            let scale = sys::ImVec2 {
244                x: fb_scale[0],
245                y: fb_scale[1],
246            };
247            sys::ImDrawData_ScaleClipRects(RawWrapper::raw_mut(self), scale);
248        }
249    }
250}
251
252/// Iterator over draw lists
253pub struct DrawListIterator<'a> {
254    iter: std::slice::Iter<'a, *mut sys::ImDrawList>,
255}
256
257impl<'a> Iterator for DrawListIterator<'a> {
258    type Item = &'a DrawList;
259
260    fn next(&mut self) -> Option<Self::Item> {
261        self.iter.next().and_then(|&ptr| {
262            if ptr.is_null() {
263                None
264            } else {
265                Some(unsafe { DrawList::from_raw(ptr.cast_const()) })
266            }
267        })
268    }
269}
270
271impl<'a> ExactSizeIterator for DrawListIterator<'a> {
272    fn len(&self) -> usize {
273        self.iter.len()
274    }
275}
276
277/// Draw command list
278#[repr(transparent)]
279pub struct DrawList(sys::ImDrawList);
280
281// Ensure the wrapper stays layout-compatible with the sys bindings.
282const _: [(); std::mem::size_of::<sys::ImDrawList>()] = [(); std::mem::size_of::<DrawList>()];
283const _: [(); std::mem::align_of::<sys::ImDrawList>()] = [(); std::mem::align_of::<DrawList>()];
284
285impl RawWrapper for DrawList {
286    type Raw = sys::ImDrawList;
287    #[inline]
288    unsafe fn raw(&self) -> &sys::ImDrawList {
289        &self.0
290    }
291    #[inline]
292    unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList {
293        &mut self.0
294    }
295}
296
297impl DrawList {
298    #[inline]
299    pub(crate) unsafe fn from_raw<'a>(raw: *const sys::ImDrawList) -> &'a Self {
300        unsafe { &*(raw as *const Self) }
301    }
302
303    #[inline]
304    pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
305        unsafe {
306            let cmd_buffer = &self.0.CmdBuffer;
307            if cmd_buffer.Size <= 0 || cmd_buffer.Data.is_null() {
308                return &[];
309            }
310            let len = match usize::try_from(cmd_buffer.Size) {
311                Ok(len) => len,
312                Err(_) => return &[],
313            };
314            slice::from_raw_parts(cmd_buffer.Data, len)
315        }
316    }
317
318    /// Returns an iterator over the draw commands in this draw list
319    pub fn commands(&self) -> DrawCmdIterator<'_> {
320        unsafe {
321            DrawCmdIterator {
322                iter: self.cmd_buffer().iter(),
323            }
324        }
325    }
326
327    /// Get vertex buffer as slice
328    pub fn vtx_buffer(&self) -> &[DrawVert] {
329        unsafe {
330            let vtx_buffer = &self.0.VtxBuffer;
331            if vtx_buffer.Size <= 0 || vtx_buffer.Data.is_null() {
332                return &[];
333            }
334            let len = match usize::try_from(vtx_buffer.Size) {
335                Ok(len) => len,
336                Err(_) => return &[],
337            };
338            slice::from_raw_parts(vtx_buffer.Data as *const DrawVert, len)
339        }
340    }
341
342    /// Get index buffer as slice
343    pub fn idx_buffer(&self) -> &[DrawIdx] {
344        unsafe {
345            let idx_buffer = &self.0.IdxBuffer;
346            if idx_buffer.Size <= 0 || idx_buffer.Data.is_null() {
347                return &[];
348            }
349            let len = match usize::try_from(idx_buffer.Size) {
350                Ok(len) => len,
351                Err(_) => return &[],
352            };
353            slice::from_raw_parts(idx_buffer.Data, len)
354        }
355    }
356}
357
358/// Iterator over draw commands
359pub struct DrawCmdIterator<'a> {
360    iter: slice::Iter<'a, sys::ImDrawCmd>,
361}
362
363impl<'a> Iterator for DrawCmdIterator<'a> {
364    type Item = DrawCmd;
365
366    fn next(&mut self) -> Option<Self::Item> {
367        self.iter.next().map(|cmd| {
368            let cmd_params = DrawCmdParams {
369                clip_rect: [
370                    cmd.ClipRect.x,
371                    cmd.ClipRect.y,
372                    cmd.ClipRect.z,
373                    cmd.ClipRect.w,
374                ],
375                // Use raw field; backends may resolve effective TexID later
376                texture_id: TextureId::from(cmd.TexRef._TexID),
377                vtx_offset: cmd.VtxOffset as usize,
378                idx_offset: cmd.IdxOffset as usize,
379            };
380
381            // Check for special callback values
382            match cmd.UserCallback {
383                Some(raw_callback) if raw_callback as usize == (-1isize) as usize => {
384                    DrawCmd::ResetRenderState
385                }
386                Some(raw_callback) => DrawCmd::RawCallback {
387                    callback: raw_callback,
388                    raw_cmd: cmd,
389                },
390                None => DrawCmd::Elements {
391                    count: cmd.ElemCount as usize,
392                    cmd_params,
393                    raw_cmd: cmd as *const sys::ImDrawCmd,
394                },
395            }
396        })
397    }
398}
399
400/// Parameters for a draw command
401#[derive(Copy, Clone, Debug, PartialEq)]
402pub struct DrawCmdParams {
403    /// Clipping rectangle [left, top, right, bottom]
404    pub clip_rect: [f32; 4],
405    /// Texture ID to use for rendering
406    ///
407    /// Notes:
408    /// - For legacy paths (plain `TextureId`), this is the effective id.
409    /// - With the modern texture system (ImTextureRef/ImTextureData), this may be 0.
410    ///   Renderer backends should resolve the effective id at bind time using
411    ///   `ImDrawCmd_GetTexID` with the `raw_cmd` pointer and the backend render state.
412    pub texture_id: TextureId,
413    /// Vertex buffer offset
414    pub vtx_offset: usize,
415    /// Index buffer offset
416    pub idx_offset: usize,
417}
418
419/// A draw command
420#[derive(Clone, Debug)]
421pub enum DrawCmd {
422    /// Elements to draw
423    Elements {
424        /// The number of indices used for this draw command
425        count: usize,
426        cmd_params: DrawCmdParams,
427        /// Raw command pointer for backends
428        ///
429        /// Backend note: when using the modern texture system, resolve the effective
430        /// texture id at bind time via `ImDrawCmd_GetTexID(raw_cmd)` together with your
431        /// renderer state. This pointer is only valid during the `render_draw_data()`
432        /// call that produced it; do not store it.
433        raw_cmd: *const sys::ImDrawCmd,
434    },
435    /// Reset render state
436    ResetRenderState,
437    /// Raw callback
438    RawCallback {
439        callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
440        raw_cmd: *const sys::ImDrawCmd,
441    },
442}
443
444/// Vertex format used by Dear ImGui
445#[repr(C)]
446#[derive(Copy, Clone, Debug, PartialEq)]
447pub struct DrawVert {
448    /// Position (2D)
449    pub pos: [f32; 2],
450    /// UV coordinates
451    pub uv: [f32; 2],
452    /// Color (packed RGBA)
453    pub col: u32,
454}
455
456// Ensure our Rust-side vertex/index types stay layout-compatible with the raw sys bindings.
457const _: [(); std::mem::size_of::<sys::ImDrawVert>()] = [(); std::mem::size_of::<DrawVert>()];
458const _: [(); std::mem::align_of::<sys::ImDrawVert>()] = [(); std::mem::align_of::<DrawVert>()];
459
460impl DrawVert {
461    /// Creates a new draw vertex with u32 color
462    pub fn new(pos: [f32; 2], uv: [f32; 2], col: u32) -> Self {
463        Self { pos, uv, col }
464    }
465
466    /// Creates a new draw vertex from RGBA bytes
467    pub fn from_rgba(pos: [f32; 2], uv: [f32; 2], rgba: [u8; 4]) -> Self {
468        let col = ((rgba[3] as u32) << 24)
469            | ((rgba[2] as u32) << 16)
470            | ((rgba[1] as u32) << 8)
471            | (rgba[0] as u32);
472        Self { pos, uv, col }
473    }
474
475    /// Extracts RGBA bytes from the packed color
476    pub fn rgba(&self) -> [u8; 4] {
477        [
478            (self.col & 0xFF) as u8,
479            ((self.col >> 8) & 0xFF) as u8,
480            ((self.col >> 16) & 0xFF) as u8,
481            ((self.col >> 24) & 0xFF) as u8,
482        ]
483    }
484}
485
486/// Index type used by Dear ImGui
487pub type DrawIdx = u16;
488
489const _: [(); std::mem::size_of::<sys::ImDrawIdx>()] = [(); std::mem::size_of::<DrawIdx>()];
490const _: [(); std::mem::align_of::<sys::ImDrawIdx>()] = [(); std::mem::align_of::<DrawIdx>()];
491
492/// A container for a heap-allocated deep copy of a `DrawData` struct.
493///
494/// Notes on thread-safety:
495/// - This type intentionally does NOT implement `Send`/`Sync` because it currently retains
496///   a pointer to the engine-managed textures list (`ImVector<ImTextureData*>`) instead of
497///   deep-copying it. That list can be mutated by the UI thread across frames.
498/// - You may move vertices/indices to another thread by extracting them into your own buffers
499///   or by implementing a custom deep copy which snapshots the textures list as well.
500///
501/// The underlying copy is released when this struct is dropped.
502pub struct OwnedDrawData {
503    draw_data: *mut sys::ImDrawData,
504    // Prevent Send/Sync: this struct retains a pointer to a shared textures list.
505    _no_send_sync: PhantomData<Rc<()>>,
506}
507
508impl OwnedDrawData {
509    /// If this struct contains a `DrawData` object, then this function returns a reference to it.
510    ///
511    /// Otherwise, this struct is empty and so this function returns `None`.
512    #[inline]
513    pub fn draw_data(&self) -> Option<&DrawData> {
514        if !self.draw_data.is_null() {
515            Some(unsafe { &*(self.draw_data as *const DrawData) })
516        } else {
517            None
518        }
519    }
520}
521
522impl Default for OwnedDrawData {
523    /// The default `OwnedDrawData` struct is empty.
524    #[inline]
525    fn default() -> Self {
526        Self {
527            draw_data: std::ptr::null_mut(),
528            _no_send_sync: PhantomData,
529        }
530    }
531}
532
533impl From<&DrawData> for OwnedDrawData {
534    /// Construct `OwnedDrawData` from `DrawData` by creating a heap-allocated deep copy of the given `DrawData`
535    fn from(value: &DrawData) -> Self {
536        unsafe {
537            // Allocate a new ImDrawData using the constructor
538            let result = sys::ImDrawData_ImDrawData();
539            if result.is_null() {
540                panic!("Failed to allocate ImDrawData for OwnedDrawData");
541            }
542
543            // Copy basic fields from the source
544            let source_ptr = RawWrapper::raw(value);
545            (*result).Valid = source_ptr.Valid;
546            (*result).TotalIdxCount = source_ptr.TotalIdxCount;
547            (*result).TotalVtxCount = source_ptr.TotalVtxCount;
548            (*result).DisplayPos = source_ptr.DisplayPos;
549            (*result).DisplaySize = source_ptr.DisplaySize;
550            (*result).FramebufferScale = source_ptr.FramebufferScale;
551            (*result).OwnerViewport = source_ptr.OwnerViewport;
552
553            // Copy draw lists by cloning each list to ensure OwnedDrawData owns its memory
554            (*result).CmdListsCount = 0;
555            if source_ptr.CmdListsCount > 0 && !source_ptr.CmdLists.Data.is_null() {
556                for i in 0..(source_ptr.CmdListsCount as usize) {
557                    let src_list = *source_ptr.CmdLists.Data.add(i);
558                    if !src_list.is_null() {
559                        // Clone the output of the draw list to own it
560                        let cloned = sys::ImDrawList_CloneOutput(src_list);
561                        if !cloned.is_null() {
562                            sys::ImDrawData_AddDrawList(result, cloned);
563                        }
564                    }
565                }
566            }
567
568            // Textures list is shared, do not duplicate (renderer treats it as read-only)
569            (*result).Textures = source_ptr.Textures;
570
571            OwnedDrawData {
572                draw_data: result,
573                _no_send_sync: PhantomData,
574            }
575        }
576    }
577}
578
579impl Drop for OwnedDrawData {
580    /// Releases any heap-allocated memory consumed by this `OwnedDrawData` object
581    fn drop(&mut self) {
582        unsafe {
583            if !self.draw_data.is_null() {
584                // Destroy cloned draw lists if any
585                if !(*self.draw_data).CmdLists.Data.is_null() {
586                    for i in 0..(*self.draw_data).CmdListsCount as usize {
587                        let ptr = *(*self.draw_data).CmdLists.Data.add(i);
588                        if !ptr.is_null() {
589                            sys::ImDrawList_destroy(ptr);
590                        }
591                    }
592                }
593
594                // Destroy the ImDrawData itself
595                sys::ImDrawData_destroy(self.draw_data);
596                self.draw_data = std::ptr::null_mut();
597            }
598        }
599    }
600}
601
602/// Iterator over textures in draw data
603pub struct TextureIterator<'a> {
604    ptr: *const *mut sys::ImTextureData,
605    end: *const *mut sys::ImTextureData,
606    _phantom: std::marker::PhantomData<&'a crate::texture::TextureData>,
607}
608
609impl<'a> TextureIterator<'a> {
610    /// Create a new texture iterator from raw pointers
611    ///
612    /// # Safety
613    ///
614    /// The caller must ensure that the pointers are valid and that the range
615    /// [ptr, end) contains valid texture data pointers.
616    pub(crate) unsafe fn new(
617        ptr: *const *mut sys::ImTextureData,
618        end: *const *mut sys::ImTextureData,
619    ) -> Self {
620        Self {
621            ptr,
622            end,
623            _phantom: std::marker::PhantomData,
624        }
625    }
626}
627
628impl<'a> Iterator for TextureIterator<'a> {
629    type Item = TextureDataMut<'a>;
630
631    fn next(&mut self) -> Option<Self::Item> {
632        while self.ptr < self.end {
633            let texture_ptr = unsafe { *self.ptr };
634            self.ptr = unsafe { self.ptr.add(1) };
635            if texture_ptr.is_null() {
636                continue;
637            }
638
639            if TEXTURE_DATA_BORROWED
640                .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
641                .is_err()
642            {
643                panic!(
644                    "TextureData is already mutably borrowed; \
645                     do not hold items from DrawData::textures()/PlatformIo::textures() across iterations"
646                );
647            }
648
649            return Some(TextureDataMut {
650                raw: texture_ptr,
651                _phantom: PhantomData,
652            });
653        }
654
655        None
656    }
657}
658
659impl<'a> std::iter::FusedIterator for TextureIterator<'a> {}
660
661/// A guarded mutable view of a single `ImTextureData`.
662///
663/// This exists because the texture list is exposed from a shared `&DrawData` reference in order to
664/// keep renderer APIs ergonomic, but the list still needs to be mutated by the backend. The guard
665/// ensures at runtime that only one mutable view is alive at a time, preventing Rust aliasing UB.
666pub struct TextureDataMut<'a> {
667    raw: *mut sys::ImTextureData,
668    _phantom: PhantomData<&'a mut crate::texture::TextureData>,
669}
670
671impl Drop for TextureDataMut<'_> {
672    fn drop(&mut self) {
673        TEXTURE_DATA_BORROWED.store(false, Ordering::Release);
674    }
675}
676
677impl std::ops::Deref for TextureDataMut<'_> {
678    type Target = crate::texture::TextureData;
679
680    fn deref(&self) -> &Self::Target {
681        unsafe { &*(self.raw as *const crate::texture::TextureData) }
682    }
683}
684
685impl std::ops::DerefMut for TextureDataMut<'_> {
686    fn deref_mut(&mut self) -> &mut Self::Target {
687        unsafe { &mut *(self.raw as *mut crate::texture::TextureData) }
688    }
689}
690
691#[cfg(test)]
692mod tests {
693    use super::*;
694
695    #[test]
696    fn draw_data_textures_empty_is_safe() {
697        let mut textures_vec: crate::internal::ImVector<*mut sys::ImTextureData> =
698            crate::internal::ImVector::default();
699
700        let draw_data = DrawData {
701            valid: false,
702            cmd_lists_count: 0,
703            total_idx_count: 0,
704            total_vtx_count: 0,
705            cmd_lists: crate::internal::ImVector::default(),
706            display_pos: [0.0, 0.0],
707            display_size: [0.0, 0.0],
708            framebuffer_scale: [1.0, 1.0],
709            owner_viewport: std::ptr::null_mut(),
710            textures: &mut textures_vec,
711        };
712
713        assert_eq!(draw_data.textures().count(), 0);
714        assert_eq!(draw_data.textures_count(), 0);
715
716        let mut textures_vec: crate::internal::ImVector<*mut sys::ImTextureData> =
717            crate::internal::ImVector::default();
718        textures_vec.size = 1;
719        textures_vec.data = std::ptr::null_mut();
720        let draw_data = DrawData {
721            valid: false,
722            cmd_lists_count: 0,
723            total_idx_count: 0,
724            total_vtx_count: 0,
725            cmd_lists: crate::internal::ImVector::default(),
726            display_pos: [0.0, 0.0],
727            display_size: [0.0, 0.0],
728            framebuffer_scale: [1.0, 1.0],
729            owner_viewport: std::ptr::null_mut(),
730            textures: &mut textures_vec,
731        };
732        assert_eq!(draw_data.textures().count(), 0);
733        assert_eq!(draw_data.textures_count(), 0);
734        assert!(draw_data.texture(0).is_none());
735    }
736}