dear_imgui_rs/texture/
data.rs1use super::format::{texture_format_bytes_per_pixel, texture_format_bytes_per_pixel_i32};
2use super::validation::{
3 checked_texture_byte_len, checked_texture_byte_len_if_valid, checked_texture_dimension_to_i32,
4 non_negative_texture_count_from_i32,
5};
6use super::{
7 ManagedTextureId, OwnedTextureData, TextureFormat, TextureId, TextureRect, TextureRef,
8 TextureStatus,
9};
10use crate::sys;
11use std::cell::UnsafeCell;
12use std::ffi::c_void;
13
14#[repr(transparent)]
36pub struct TextureData {
37 raw: UnsafeCell<sys::ImTextureData>,
38}
39
40const _: [(); std::mem::size_of::<sys::ImTextureData>()] = [(); std::mem::size_of::<TextureData>()];
42const _: [(); std::mem::align_of::<sys::ImTextureData>()] =
43 [(); std::mem::align_of::<TextureData>()];
44
45impl TextureData {
46 #[inline]
47 pub(super) fn inner(&self) -> &sys::ImTextureData {
48 unsafe { &*self.raw.get() }
53 }
54
55 #[inline]
56 pub(super) fn inner_mut(&mut self) -> &mut sys::ImTextureData {
57 unsafe { &mut *self.raw.get() }
59 }
60
61 pub(super) fn assert_metadata_mutation_allowed(&self, caller: &str) {
62 let raw = self.inner();
63 assert!(
64 raw.Pixels.is_null(),
65 "{caller} cannot change texture metadata while pixel storage is allocated"
66 );
67 assert!(
68 raw.Status == sys::ImTextureStatus_Destroyed,
69 "{caller} requires Destroyed texture status"
70 );
71 }
72
73 pub fn new() -> OwnedTextureData {
77 OwnedTextureData::new()
78 }
79
80 pub(crate) unsafe fn from_raw<'a>(raw: *mut sys::ImTextureData) -> &'a mut Self {
84 unsafe { &mut *(raw as *mut Self) }
85 }
86
87 pub(crate) unsafe fn from_raw_ref<'a>(raw: *const sys::ImTextureData) -> &'a Self {
91 unsafe { &*(raw as *const Self) }
92 }
93
94 pub fn as_raw(&self) -> *const sys::ImTextureData {
96 self.raw.get() as *const _
97 }
98
99 pub fn as_raw_mut(&mut self) -> *mut sys::ImTextureData {
101 self.raw.get()
102 }
103
104 pub fn unique_id(&self) -> ManagedTextureId {
106 ManagedTextureId::from_raw(self.inner().UniqueID)
107 }
108
109 pub fn status(&self) -> TextureStatus {
111 TextureStatus::from(self.inner().Status)
112 }
113
114 pub fn set_status(&mut self, status: TextureStatus) {
118 unsafe {
119 if status == TextureStatus::Destroyed {
123 sys::ImTextureData_SetTexID(self.as_raw_mut(), 0 as sys::ImTextureID);
124 (*self.as_raw_mut()).BackendUserData = std::ptr::null_mut();
125 }
126 sys::ImTextureData_SetStatus(self.as_raw_mut(), status.into());
127 }
128 }
129
130 pub fn backend_user_data(&self) -> *mut c_void {
132 self.inner().BackendUserData
133 }
134
135 pub fn set_backend_user_data(&mut self, data: *mut c_void) {
137 self.inner_mut().BackendUserData = data;
138 }
139
140 pub fn tex_id(&self) -> TextureId {
142 TextureId::from(self.inner().TexID)
143 }
144
145 pub fn set_tex_id(&mut self, tex_id: TextureId) {
149 unsafe {
150 sys::ImTextureData_SetTexID(self.as_raw_mut(), tex_id.id() as sys::ImTextureID);
151 }
152 }
153
154 #[inline]
156 pub fn texture_ref(&mut self) -> TextureRef<'_> {
157 unsafe { TextureRef::from_raw(sys::ImTextureData_GetTexRef(self.as_raw_mut())) }
158 }
159
160 pub fn format(&self) -> TextureFormat {
162 TextureFormat::from(self.inner().Format)
163 }
164
165 pub fn width(&self) -> u32 {
167 u32::try_from(self.raw_width_i32()).unwrap_or(0)
168 }
169
170 pub fn height(&self) -> u32 {
172 u32::try_from(self.raw_height_i32()).unwrap_or(0)
173 }
174
175 pub fn bytes_per_pixel(&self) -> usize {
177 usize::try_from(self.raw_bytes_per_pixel_i32()).unwrap_or(0)
178 }
179
180 pub fn unused_frames(&self) -> usize {
182 non_negative_texture_count_from_i32(
183 "TextureData::unused_frames()",
184 self.inner().UnusedFrames,
185 )
186 }
187
188 pub fn ref_count(&self) -> u16 {
190 self.inner().RefCount
191 }
192
193 pub fn use_colors(&self) -> bool {
195 self.inner().UseColors
196 }
197
198 pub fn want_destroy_next_frame(&self) -> bool {
200 self.inner().WantDestroyNextFrame
201 }
202
203 pub fn pixels(&self) -> Option<&[u8]> {
207 let raw = self.inner();
208 if raw.Pixels.is_null() {
209 None
210 } else {
211 let width = raw.Width;
212 let height = raw.Height;
213 let bytes_per_pixel = raw.BytesPerPixel;
214 if width <= 0 || height <= 0 || bytes_per_pixel <= 0 {
215 return None;
216 }
217
218 let size = (width as usize)
219 .checked_mul(height as usize)?
220 .checked_mul(bytes_per_pixel as usize)?;
221 unsafe { Some(std::slice::from_raw_parts(raw.Pixels as *const u8, size)) }
222 }
223 }
224
225 pub fn used_rect(&self) -> TextureRect {
227 TextureRect::from(self.inner().UsedRect)
228 }
229
230 pub fn update_rect(&self) -> TextureRect {
232 TextureRect::from(self.inner().UpdateRect)
233 }
234
235 pub fn updates(&self) -> impl Iterator<Item = TextureRect> + '_ {
237 let vec = &self.inner().Updates;
238 let count = if vec.Data.is_null() {
239 0
240 } else {
241 usize::try_from(vec.Size).unwrap_or(0)
242 };
243 let data = vec.Data as *const sys::ImTextureRect;
244 (0..count).map(move |i| unsafe { TextureRect::from(*data.add(i)) })
245 }
246
247 pub fn pixels_at(&self, x: u32, y: u32) -> Option<&[u8]> {
251 let raw = self.inner();
252 let width = u32::try_from(raw.Width).ok()?;
253 let height = u32::try_from(raw.Height).ok()?;
254 let bytes_per_pixel = usize::try_from(raw.BytesPerPixel).ok()?;
255 if raw.Pixels.is_null() || width == 0 || height == 0 || bytes_per_pixel == 0 {
256 return None;
257 }
258 if x >= width || y >= height {
259 None
260 } else {
261 let width_usize = usize::try_from(width).ok()?;
262 let height_usize = usize::try_from(height).ok()?;
263 let x_usize = usize::try_from(x).ok()?;
264 let y_usize = usize::try_from(y).ok()?;
265
266 let total_size = width_usize
267 .checked_mul(height_usize)?
268 .checked_mul(bytes_per_pixel)?;
269
270 let offset_px = y_usize.checked_mul(width_usize)?.checked_add(x_usize)?;
271 let offset_bytes = offset_px.checked_mul(bytes_per_pixel)?;
272 let remaining_size = total_size.checked_sub(offset_bytes)?;
273
274 unsafe {
275 let ptr = (raw.Pixels as *const u8).add(offset_bytes);
276 Some(std::slice::from_raw_parts(ptr, remaining_size))
277 }
278 }
279 }
280
281 pub fn pitch(&self) -> usize {
283 let width = self.width();
284 let bytes_per_pixel = self.bytes_per_pixel();
285 if width == 0 || bytes_per_pixel == 0 {
286 return 0;
287 }
288 usize::try_from(width)
289 .expect("TextureData::pitch() width must fit usize")
290 .checked_mul(bytes_per_pixel)
291 .expect("TextureData::pitch() byte pitch overflowed usize")
292 }
293
294 pub fn create(&mut self, format: TextureFormat, width: u32, height: u32) {
298 assert!(
299 self.status() == TextureStatus::Destroyed,
300 "TextureData::create() requires Destroyed texture status"
301 );
302 let bytes_per_pixel = texture_format_bytes_per_pixel(format);
303 let _ = checked_texture_byte_len("TextureData::create()", width, height, bytes_per_pixel);
304 let width = checked_texture_dimension_to_i32("TextureData::create()", "width", width);
305 let height = checked_texture_dimension_to_i32("TextureData::create()", "height", height);
306
307 unsafe {
308 sys::ImTextureData_Create(self.as_raw_mut(), format.into(), width, height);
309 }
310 }
311
312 pub fn destroy_pixels(&mut self) {
316 unsafe {
317 sys::ImTextureData_DestroyPixels(self.as_raw_mut());
318 }
319 }
320
321 pub fn set_data(&mut self, data: &[u8]) {
325 unsafe {
326 let raw = self.as_raw_mut();
327 let Some(needed) = checked_texture_byte_len_if_valid(
328 "TextureData::set_data()",
329 (*raw).Width,
330 (*raw).Height,
331 (*raw).BytesPerPixel,
332 ) else {
333 return;
335 };
336
337 if (*raw).Pixels.is_null() {
339 assert!(
340 (*raw).Status == sys::ImTextureStatus_Destroyed,
341 "TextureData::set_data() requires Destroyed texture status when allocating missing pixel storage"
342 );
343 sys::ImTextureData_Create(
344 self.as_raw_mut(),
345 (*raw).Format,
346 (*raw).Width,
347 (*raw).Height,
348 );
349 }
350
351 let copy_bytes = std::cmp::min(needed, data.len());
352 if copy_bytes == 0 {
353 return;
354 }
355
356 std::ptr::copy_nonoverlapping(data.as_ptr(), (*raw).Pixels as *mut u8, copy_bytes);
357
358 (*raw).UpdateRect = sys::ImTextureRect {
360 x: 0u16,
361 y: 0u16,
362 w: (*raw).Width.clamp(0, u16::MAX as i32) as u16,
363 h: (*raw).Height.clamp(0, u16::MAX as i32) as u16,
364 };
365 sys::ImTextureData_SetStatus(raw, sys::ImTextureStatus_WantUpdates);
366 }
367 }
368
369 pub fn set_width(&mut self, width: u32) {
371 self.assert_metadata_mutation_allowed("TextureData::set_width()");
372 assert!(width > 0, "TextureData::set_width() width must be positive");
373 let width =
374 i32::try_from(width).expect("TextureData::set_width() width exceeded i32 range");
375 self.inner_mut().Width = width;
376 }
377
378 pub fn set_height(&mut self, height: u32) {
380 self.assert_metadata_mutation_allowed("TextureData::set_height()");
381 assert!(
382 height > 0,
383 "TextureData::set_height() height must be positive"
384 );
385 let height =
386 i32::try_from(height).expect("TextureData::set_height() height exceeded i32 range");
387 self.inner_mut().Height = height;
388 }
389
390 pub fn set_format(&mut self, format: TextureFormat) {
392 self.assert_metadata_mutation_allowed("TextureData::set_format()");
393 let raw = self.inner_mut();
394 raw.Format = format.into();
395 raw.BytesPerPixel = texture_format_bytes_per_pixel_i32(format);
396 }
397
398 pub(crate) fn raw_width_i32(&self) -> i32 {
399 self.inner().Width
400 }
401
402 pub(crate) fn raw_height_i32(&self) -> i32 {
403 self.inner().Height
404 }
405
406 pub(crate) fn raw_bytes_per_pixel_i32(&self) -> i32 {
407 self.inner().BytesPerPixel
408 }
409}