Skip to main content

dear_imgui_rs/render/draw_data/
textures.rs

1use super::DrawData;
2use crate::sys;
3use crate::texture::TextureData;
4use std::marker::PhantomData;
5
6impl DrawData {
7    /// Returns a shared iterator over textures attached to this draw data.
8    ///
9    /// Use this for inspection, snapshotting, or read-only request collection. Renderer backends
10    /// that need to write `TexID`/`Status` after handling texture requests must use
11    /// [`Self::textures_mut`] instead.
12    pub fn textures(&self) -> TextureIterator<'_> {
13        unsafe {
14            if self.textures.is_null() {
15                TextureIterator::new(std::ptr::null(), std::ptr::null())
16            } else {
17                let vector = &*self.textures;
18                if vector.size <= 0 || vector.data.is_null() {
19                    TextureIterator::new(std::ptr::null(), std::ptr::null())
20                } else {
21                    TextureIterator::new(vector.data, vector.data.add(vector.size as usize))
22                }
23            }
24        }
25    }
26
27    /// Returns a mutable cursor over textures that need to be updated.
28    ///
29    /// This is used by renderer backends to process texture creation, updates, and destruction.
30    /// Each item is an `ImTextureData*` carrying a `Status` which can be one of:
31    /// - `OK`: nothing to do.
32    /// - `WantCreate`: create a GPU texture and upload all pixels.
33    /// - `WantUpdates`: upload specified `UpdateRect` regions.
34    /// - `WantDestroy`: destroy the GPU texture (may be delayed until unused).
35    /// Most of the time this list has only 1 texture and it doesn't need any update.
36    ///
37    /// The cursor intentionally does not implement [`Iterator`]. Each mutable item is borrowed
38    /// from the cursor itself, so safe Rust cannot hold one texture update guard while asking for
39    /// the next one.
40    ///
41    /// ```compile_fail
42    /// use dear_imgui_rs::render::DrawData;
43    ///
44    /// fn shared_texture_blocks_mutable_cursor(draw_data: &mut DrawData) {
45    ///     let shared = draw_data.texture(0);
46    ///     let mut textures = draw_data.textures_mut();
47    ///     let _first = textures.next();
48    ///     drop(shared);
49    /// }
50    /// ```
51    ///
52    /// ```compile_fail
53    /// use dear_imgui_rs::render::DrawData;
54    ///
55    /// fn texture_guards_cannot_overlap(draw_data: &mut DrawData) {
56    ///     let mut textures = draw_data.textures_mut();
57    ///     let first = textures.next();
58    ///     let second = textures.next();
59    ///     drop(first);
60    ///     drop(second);
61    /// }
62    /// ```
63    pub fn textures_mut(&mut self) -> TextureMutCursor<'_> {
64        unsafe {
65            if self.textures.is_null() {
66                TextureMutCursor::new(std::ptr::null_mut(), std::ptr::null_mut())
67            } else {
68                let vector = &mut *self.textures;
69                if vector.size <= 0 || vector.data.is_null() {
70                    TextureMutCursor::new(std::ptr::null_mut(), std::ptr::null_mut())
71                } else {
72                    TextureMutCursor::new(vector.data, vector.data.add(vector.size as usize))
73                }
74            }
75        }
76    }
77
78    /// Returns the number of textures in the texture list
79    pub fn textures_count(&self) -> usize {
80        unsafe {
81            if self.textures.is_null() {
82                0
83            } else {
84                let vector = &*self.textures;
85                if vector.size <= 0 || vector.data.is_null() {
86                    0
87                } else {
88                    vector.size as usize
89                }
90            }
91        }
92    }
93
94    /// Get a specific texture by index
95    ///
96    /// Returns None if the index is out of bounds or no textures are available.
97    pub fn texture(&self, index: usize) -> Option<&TextureData> {
98        unsafe {
99            if self.textures.is_null() {
100                return None;
101            }
102            let vector = &*self.textures;
103            let size = usize::try_from(vector.size).ok()?;
104            if size == 0 || vector.data.is_null() {
105                return None;
106            }
107            if index >= size {
108                return None;
109            }
110            let texture_ptr = *vector.data.add(index);
111            if texture_ptr.is_null() {
112                return None;
113            }
114            Some(TextureData::from_raw_ref(texture_ptr as *const _))
115        }
116    }
117
118    /// Get a mutable reference to a specific texture by index
119    ///
120    /// Returns None if the index is out of bounds or no textures are available.
121    pub fn texture_mut(&mut self, index: usize) -> Option<&mut TextureData> {
122        unsafe {
123            if self.textures.is_null() {
124                return None;
125            }
126            let vector = &*self.textures;
127            let size = usize::try_from(vector.size).ok()?;
128            if size == 0 || vector.data.is_null() {
129                return None;
130            }
131            if index >= size {
132                return None;
133            }
134            let texture_ptr = *vector.data.add(index);
135            if texture_ptr.is_null() {
136                return None;
137            }
138            Some(TextureData::from_raw(texture_ptr))
139        }
140    }
141}
142
143/// Iterator over textures in draw data
144pub struct TextureIterator<'a> {
145    ptr: *const *mut sys::ImTextureData,
146    end: *const *mut sys::ImTextureData,
147    _phantom: PhantomData<&'a TextureData>,
148}
149
150impl<'a> TextureIterator<'a> {
151    /// Create a new texture iterator from raw pointers
152    ///
153    /// # Safety
154    ///
155    /// The caller must ensure that the pointers are valid and that the range
156    /// [ptr, end) contains valid texture data pointers.
157    pub(crate) unsafe fn new(
158        ptr: *const *mut sys::ImTextureData,
159        end: *const *mut sys::ImTextureData,
160    ) -> Self {
161        Self {
162            ptr,
163            end,
164            _phantom: PhantomData,
165        }
166    }
167}
168
169impl<'a> Iterator for TextureIterator<'a> {
170    type Item = &'a TextureData;
171
172    fn next(&mut self) -> Option<Self::Item> {
173        while self.ptr < self.end {
174            let texture_ptr = unsafe { *self.ptr };
175            self.ptr = unsafe { self.ptr.add(1) };
176            if texture_ptr.is_null() {
177                continue;
178            }
179
180            return Some(unsafe { TextureData::from_raw_ref(texture_ptr as *const _) });
181        }
182
183        None
184    }
185}
186
187impl<'a> std::iter::FusedIterator for TextureIterator<'a> {}
188
189/// Mutable cursor over a texture list.
190///
191/// This cursor is the mutable counterpart to [`TextureIterator`]. It is a streaming cursor rather
192/// than a standard iterator so each returned [`TextureDataMut`] is tied to the borrow of the cursor
193/// used for that single `next()` call.
194pub struct TextureMutCursor<'a> {
195    ptr: *mut *mut sys::ImTextureData,
196    end: *mut *mut sys::ImTextureData,
197    _phantom: PhantomData<&'a mut TextureData>,
198}
199
200impl<'a> TextureMutCursor<'a> {
201    /// Create a new texture cursor from raw pointers.
202    ///
203    /// # Safety
204    ///
205    /// The caller must ensure that the pointers are valid and that the range
206    /// [ptr, end) contains valid texture data pointers. The caller must also hold the unique
207    /// mutable borrow of the owner texture list for `'a`.
208    pub(crate) unsafe fn new(
209        ptr: *mut *mut sys::ImTextureData,
210        end: *mut *mut sys::ImTextureData,
211    ) -> Self {
212        Self {
213            ptr,
214            end,
215            _phantom: PhantomData,
216        }
217    }
218
219    /// Advance to the next non-null texture.
220    pub fn next(&mut self) -> Option<TextureDataMut<'_>> {
221        while self.ptr < self.end {
222            let texture_ptr = unsafe { *self.ptr };
223            self.ptr = unsafe { self.ptr.add(1) };
224            if texture_ptr.is_null() {
225                continue;
226            }
227
228            return Some(TextureDataMut {
229                raw: texture_ptr,
230                _phantom: PhantomData,
231            });
232        }
233
234        None
235    }
236}
237
238/// A guarded mutable view of a single `ImTextureData`.
239///
240/// The guard is created by [`TextureMutCursor::next`] and borrows the cursor for its lifetime, so
241/// safe Rust cannot hold multiple mutable texture views from the same list at the same time.
242pub struct TextureDataMut<'a> {
243    raw: *mut sys::ImTextureData,
244    _phantom: PhantomData<&'a mut TextureData>,
245}
246
247impl std::ops::Deref for TextureDataMut<'_> {
248    type Target = TextureData;
249
250    fn deref(&self) -> &Self::Target {
251        unsafe { TextureData::from_raw_ref(self.raw as *const _) }
252    }
253}
254
255impl std::ops::DerefMut for TextureDataMut<'_> {
256    fn deref_mut(&mut self) -> &mut Self::Target {
257        unsafe { TextureData::from_raw(self.raw) }
258    }
259}