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}