Skip to main content

dear_imgui_rs/
texture.rs

1//! Texture management for Dear ImGui
2//!
3//! This module provides access to Dear ImGui's modern texture management system
4//! introduced in version 1.92+. It includes support for ImTextureData, texture
5//! status management, and automatic texture updates.
6
7use crate::sys;
8use std::cell::UnsafeCell;
9use std::ffi::c_void;
10use std::ptr::NonNull;
11
12/// Simple texture ID for backward compatibility
13///
14/// This is a simple wrapper around u64 that can be used to identify textures.
15/// For modern texture management, use TextureData instead.
16///
17/// Note: Changed from usize to u64 in Dear ImGui 1.91.4+ to support 64-bit handles
18/// like Vulkan and DX12 on 32-bit targets.
19#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
20#[repr(transparent)]
21pub struct TextureId(u64);
22
23impl TextureId {
24    /// Creates a new texture id with the given identifier
25    #[inline]
26    pub const fn new(id: u64) -> Self {
27        Self(id)
28    }
29
30    /// Returns the id of the TextureId
31    #[inline]
32    pub const fn id(self) -> u64 {
33        self.0
34    }
35
36    /// Creates a null texture ID
37    #[inline]
38    pub const fn null() -> Self {
39        Self(0)
40    }
41
42    /// Checks if this texture ID is null
43    #[inline]
44    pub const fn is_null(self) -> bool {
45        self.0 == 0
46    }
47}
48
49impl From<u64> for TextureId {
50    #[inline]
51    fn from(id: u64) -> Self {
52        TextureId(id)
53    }
54}
55
56impl<T> From<*const T> for TextureId {
57    #[inline]
58    fn from(ptr: *const T) -> Self {
59        TextureId(ptr as usize as u64)
60    }
61}
62
63impl<T> From<*mut T> for TextureId {
64    #[inline]
65    fn from(ptr: *mut T) -> Self {
66        TextureId(ptr as usize as u64)
67    }
68}
69
70impl From<TextureId> for *const c_void {
71    #[inline]
72    fn from(id: TextureId) -> Self {
73        debug_assert!(
74            id.0 <= (usize::MAX as u64),
75            "TextureId value {} exceeds pointer width on this target",
76            id.0
77        );
78        id.0 as usize as *const c_void
79    }
80}
81
82impl From<TextureId> for *mut c_void {
83    #[inline]
84    fn from(id: TextureId) -> Self {
85        debug_assert!(
86            id.0 <= (usize::MAX as u64),
87            "TextureId value {} exceeds pointer width on this target",
88            id.0
89        );
90        id.0 as usize as *mut c_void
91    }
92}
93
94// Backward compatibility: allow conversion from usize for legacy code
95impl From<usize> for TextureId {
96    #[inline]
97    fn from(id: usize) -> Self {
98        TextureId(id as u64)
99    }
100}
101
102// Allow conversion to usize for legacy code
103impl From<TextureId> for usize {
104    #[inline]
105    fn from(id: TextureId) -> Self {
106        debug_assert!(
107            id.0 <= (usize::MAX as u64),
108            "TextureId value {} exceeds usize width on this target",
109            id.0
110        );
111        id.0 as usize
112    }
113}
114
115impl Default for TextureId {
116    #[inline]
117    fn default() -> Self {
118        Self::null()
119    }
120}
121
122/// Raw texture ID type for compatibility with Dear ImGui
123pub type RawTextureId = sys::ImTextureID;
124
125impl From<TextureId> for RawTextureId {
126    #[inline]
127    fn from(id: TextureId) -> Self {
128        id.id() as sys::ImTextureID
129    }
130}
131
132/// A convenient, typed wrapper around ImGui's ImTextureRef (v1.92+)
133///
134/// Can reference either a plain `TextureId` (legacy path) or a managed `TextureData`.
135///
136/// Examples
137/// - With a plain GPU handle (legacy path):
138/// ```no_run
139/// # use dear_imgui_rs::{Ui, TextureId};
140/// # fn demo(ui: &Ui) {
141/// let tex_id = TextureId::new(12345);
142/// ui.image(tex_id, [64.0, 64.0]);
143/// # }
144/// ```
145/// - With a managed texture (ImGui 1.92 texture system):
146/// ```no_run
147/// # use dear_imgui_rs::{Ui, texture::{TextureData, TextureFormat}};
148/// # fn demo(ui: &Ui) {
149/// let mut tex = TextureData::new();
150/// tex.create(TextureFormat::RGBA32, 256, 256);
151/// // Fill pixels or schedule updates...
152/// ui.image(&mut *tex, [256.0, 256.0]);
153/// // The renderer backend will honor WantCreate/WantUpdates/WantDestroy
154/// // via DrawData::textures() when rendering this frame.
155/// # }
156/// ```
157#[derive(Copy, Clone, Debug)]
158#[repr(transparent)]
159pub struct TextureRef(sys::ImTextureRef);
160
161// Ensure the wrapper stays layout-compatible with the sys bindings.
162const _: [(); std::mem::size_of::<sys::ImTextureRef>()] = [(); std::mem::size_of::<TextureRef>()];
163const _: [(); std::mem::align_of::<sys::ImTextureRef>()] = [(); std::mem::align_of::<TextureRef>()];
164
165impl TextureRef {
166    /// Create a texture reference from a raw ImGui texture ref
167    #[inline]
168    pub fn from_raw(raw: sys::ImTextureRef) -> Self {
169        Self(raw)
170    }
171
172    /// Get the underlying ImGui texture ref (by value)
173    #[inline]
174    pub fn raw(self) -> sys::ImTextureRef {
175        self.0
176    }
177}
178
179impl From<TextureId> for TextureRef {
180    #[inline]
181    fn from(id: TextureId) -> Self {
182        TextureRef(sys::ImTextureRef {
183            _TexData: std::ptr::null_mut(),
184            _TexID: id.id() as sys::ImTextureID,
185        })
186    }
187}
188
189impl From<u64> for TextureRef {
190    #[inline]
191    fn from(id: u64) -> Self {
192        TextureRef::from(TextureId::from(id))
193    }
194}
195
196impl From<&TextureData> for TextureRef {
197    #[inline]
198    fn from(td: &TextureData) -> Self {
199        // Safety: A shared `&TextureData` must not be used to give Dear ImGui a mutable
200        // `ImTextureData*` because ImGui/backends may mutate fields such as `Status`/`TexID`
201        // during the frame, which would violate Rust aliasing rules.
202        //
203        // We therefore treat `&TextureData` as a legacy reference: only forward the current
204        // `TexID` value (if any). For managed textures, pass `&mut TextureData` instead.
205        TextureRef(sys::ImTextureRef {
206            _TexData: std::ptr::null_mut(),
207            _TexID: td.tex_id().id() as sys::ImTextureID,
208        })
209    }
210}
211
212impl From<&mut TextureData> for TextureRef {
213    #[inline]
214    fn from(td: &mut TextureData) -> Self {
215        TextureRef(sys::ImTextureRef {
216            _TexData: td.as_raw_mut(),
217            _TexID: 0,
218        })
219    }
220}
221
222/// Texture format supported by Dear ImGui
223#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
224#[repr(i32)]
225pub enum TextureFormat {
226    /// 4 components per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight * 4
227    RGBA32 = sys::ImTextureFormat_RGBA32 as i32,
228    /// 1 component per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight
229    Alpha8 = sys::ImTextureFormat_Alpha8 as i32,
230}
231
232impl From<sys::ImTextureFormat> for TextureFormat {
233    fn from(format: sys::ImTextureFormat) -> Self {
234        match format {
235            sys::ImTextureFormat_RGBA32 => TextureFormat::RGBA32,
236            sys::ImTextureFormat_Alpha8 => TextureFormat::Alpha8,
237            _ => TextureFormat::RGBA32, // Default fallback
238        }
239    }
240}
241
242impl From<TextureFormat> for sys::ImTextureFormat {
243    fn from(format: TextureFormat) -> Self {
244        format as sys::ImTextureFormat
245    }
246}
247
248/// Status of a texture to communicate with Renderer Backend
249#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
250#[repr(i32)]
251pub enum TextureStatus {
252    /// Texture is ready and can be used
253    OK = sys::ImTextureStatus_OK as i32,
254    /// Backend destroyed the texture
255    Destroyed = sys::ImTextureStatus_Destroyed as i32,
256    /// Requesting backend to create the texture. Set status OK when done.
257    WantCreate = sys::ImTextureStatus_WantCreate as i32,
258    /// Requesting backend to update specific blocks of pixels. Set status OK when done.
259    WantUpdates = sys::ImTextureStatus_WantUpdates as i32,
260    /// Requesting backend to destroy the texture. Set status to Destroyed when done.
261    WantDestroy = sys::ImTextureStatus_WantDestroy as i32,
262}
263
264impl From<sys::ImTextureStatus> for TextureStatus {
265    fn from(status: sys::ImTextureStatus) -> Self {
266        match status {
267            sys::ImTextureStatus_OK => TextureStatus::OK,
268            sys::ImTextureStatus_Destroyed => TextureStatus::Destroyed,
269            sys::ImTextureStatus_WantCreate => TextureStatus::WantCreate,
270            sys::ImTextureStatus_WantUpdates => TextureStatus::WantUpdates,
271            sys::ImTextureStatus_WantDestroy => TextureStatus::WantDestroy,
272            _ => TextureStatus::Destroyed, // Default fallback
273        }
274    }
275}
276
277impl From<TextureStatus> for sys::ImTextureStatus {
278    fn from(status: TextureStatus) -> Self {
279        status as sys::ImTextureStatus
280    }
281}
282
283/// Coordinates of a rectangle within a texture
284#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
285pub struct TextureRect {
286    /// Upper-left X coordinate of rectangle to update
287    pub x: u16,
288    /// Upper-left Y coordinate of rectangle to update
289    pub y: u16,
290    /// Width of rectangle to update
291    pub w: u16,
292    /// Height of rectangle to update
293    pub h: u16,
294}
295
296impl From<sys::ImTextureRect> for TextureRect {
297    fn from(rect: sys::ImTextureRect) -> Self {
298        Self {
299            x: rect.x,
300            y: rect.y,
301            w: rect.w,
302            h: rect.h,
303        }
304    }
305}
306
307impl From<TextureRect> for sys::ImTextureRect {
308    fn from(rect: TextureRect) -> Self {
309        Self {
310            x: rect.x,
311            y: rect.y,
312            w: rect.w,
313            h: rect.h,
314        }
315    }
316}
317
318/// Owned texture data managed by Dear ImGui.
319///
320/// This owns an `ImTextureData` instance allocated by Dear ImGui (C++) and will
321/// destroy it on drop. It dereferences to [`TextureData`] so you can call the
322/// same APIs as on borrowed texture data (e.g. items returned by
323/// `DrawData::textures()`).
324pub struct OwnedTextureData {
325    raw: NonNull<sys::ImTextureData>,
326}
327
328impl OwnedTextureData {
329    /// Create a new empty texture data object (C++ constructed).
330    pub fn new() -> Self {
331        let raw = unsafe { sys::ImTextureData_ImTextureData() };
332        let raw = NonNull::new(raw).expect("ImTextureData_ImTextureData() returned null");
333        Self { raw }
334    }
335
336    /// Leak the underlying `ImTextureData*` without destroying it.
337    pub fn into_raw(self) -> *mut sys::ImTextureData {
338        let raw = self.raw.as_ptr();
339        std::mem::forget(self);
340        raw
341    }
342
343    /// Take ownership of a raw `ImTextureData*`.
344    ///
345    /// # Safety
346    /// - `raw` must be a valid pointer returned by `ImTextureData_ImTextureData()`.
347    /// - The caller must ensure no other owner will call `ImTextureData_destroy(raw)`.
348    pub unsafe fn from_raw_owned(raw: *mut sys::ImTextureData) -> Self {
349        let raw = NonNull::new(raw).expect("raw ImTextureData pointer was null");
350        Self { raw }
351    }
352}
353
354impl Drop for OwnedTextureData {
355    fn drop(&mut self) {
356        unsafe { sys::ImTextureData_destroy(self.raw.as_ptr()) }
357    }
358}
359
360impl std::ops::Deref for OwnedTextureData {
361    type Target = TextureData;
362
363    fn deref(&self) -> &Self::Target {
364        unsafe { &*(self.raw.as_ptr() as *const TextureData) }
365    }
366}
367
368impl std::ops::DerefMut for OwnedTextureData {
369    fn deref_mut(&mut self) -> &mut Self::Target {
370        unsafe { &mut *(self.raw.as_ptr() as *mut TextureData) }
371    }
372}
373
374impl AsRef<TextureData> for OwnedTextureData {
375    fn as_ref(&self) -> &TextureData {
376        self
377    }
378}
379
380impl AsMut<TextureData> for OwnedTextureData {
381    fn as_mut(&mut self) -> &mut TextureData {
382        self
383    }
384}
385
386/// Texture data managed by Dear ImGui
387///
388/// This is a wrapper around ImTextureData that provides safe access to
389/// texture information and pixel data. It's used by renderer backends
390/// to create, update, and destroy textures.
391///
392/// Lifecycle & Backend Flow (ImGui 1.92+)
393/// - Create an instance (e.g. via `OwnedTextureData::new()` + `create()`)
394/// - Mutate pixels, set flags/rects (e.g. call `set_data()` or directly write `Pixels` then
395///   set `UpdateRect`), and set status to `WantCreate`/`WantUpdates`.
396/// - Register user-created textures once via `Context::register_user_texture(&mut tex)`. Dear
397///   ImGui builds `DrawData::textures()` from its internal `PlatformIO.Textures[]` list (font atlas
398///   textures are registered by ImGui itself).
399/// - Your renderer backend iterates `DrawData::textures()` and performs the requested
400///   create/update/destroy operations, then updates status to `OK`/`Destroyed`.
401/// - You can also set/get a `TexID` (e.g., GPU handle) via `set_tex_id()/tex_id()` after creation.
402///
403/// Lifetime Note: If using the managed path, you must keep the underlying `ImTextureData` alive at
404/// least until the end of the frame where it is referenced by UI calls. If you create textures
405/// yourself, use [`OwnedTextureData`] to ensure the object is correctly constructed and destroyed
406/// by the C++ side.
407#[repr(transparent)]
408pub struct TextureData {
409    raw: UnsafeCell<sys::ImTextureData>,
410}
411
412// Ensure the wrapper stays layout-compatible with the sys bindings.
413const _: [(); std::mem::size_of::<sys::ImTextureData>()] = [(); std::mem::size_of::<TextureData>()];
414const _: [(); std::mem::align_of::<sys::ImTextureData>()] =
415    [(); std::mem::align_of::<TextureData>()];
416
417impl TextureData {
418    #[inline]
419    fn inner(&self) -> &sys::ImTextureData {
420        // Safety: `TextureData` is a view into an ImGui-owned `ImTextureData`. Dear ImGui and
421        // renderer backends can mutate fields (e.g. Status/TexID/BackendUserData) while Rust holds
422        // `&TextureData`, so we store it behind `UnsafeCell` to make that interior mutability
423        // explicit.
424        unsafe { &*self.raw.get() }
425    }
426
427    #[inline]
428    fn inner_mut(&mut self) -> &mut sys::ImTextureData {
429        // Safety: caller has `&mut TextureData`, so this is a unique Rust borrow for this wrapper.
430        unsafe { &mut *self.raw.get() }
431    }
432
433    /// Create a new owned texture data object.
434    ///
435    /// This is kept for convenience. Prefer [`OwnedTextureData::new()`] for clarity.
436    pub fn new() -> OwnedTextureData {
437        OwnedTextureData::new()
438    }
439
440    /// Create a new texture data from raw pointer (crate-internal)
441    ///
442    /// Safety: caller must ensure the pointer is valid for the returned lifetime.
443    pub(crate) unsafe fn from_raw<'a>(raw: *mut sys::ImTextureData) -> &'a mut Self {
444        unsafe { &mut *(raw as *mut Self) }
445    }
446
447    /// Create a shared texture data view from a raw pointer (crate-internal).
448    ///
449    /// Safety: caller must ensure the pointer is valid for the returned lifetime.
450    pub(crate) unsafe fn from_raw_ref<'a>(raw: *const sys::ImTextureData) -> &'a Self {
451        unsafe { &*(raw as *const Self) }
452    }
453
454    /// Get the raw pointer to the underlying ImTextureData
455    pub fn as_raw(&self) -> *const sys::ImTextureData {
456        self.raw.get() as *const _
457    }
458
459    /// Get the raw mutable pointer to the underlying ImTextureData
460    pub fn as_raw_mut(&mut self) -> *mut sys::ImTextureData {
461        self.raw.get()
462    }
463
464    /// Get the unique ID of this texture (for debugging)
465    pub fn unique_id(&self) -> i32 {
466        self.inner().UniqueID
467    }
468
469    /// Get the current status of this texture
470    pub fn status(&self) -> TextureStatus {
471        TextureStatus::from(self.inner().Status)
472    }
473
474    /// Set the status of this texture
475    ///
476    /// This should only be called by renderer backends after handling a request.
477    pub fn set_status(&mut self, status: TextureStatus) {
478        unsafe {
479            // When marking a texture as destroyed, Dear ImGui expects the backend to clear any
480            // backend bindings (TexID/BackendUserData). Otherwise ImGui will assert when
481            // processing the texture list.
482            if status == TextureStatus::Destroyed {
483                sys::ImTextureData_SetTexID(self.as_raw_mut(), 0 as sys::ImTextureID);
484                (*self.as_raw_mut()).BackendUserData = std::ptr::null_mut();
485            }
486            sys::ImTextureData_SetStatus(self.as_raw_mut(), status.into());
487        }
488    }
489
490    /// Get the backend user data
491    pub fn backend_user_data(&self) -> *mut c_void {
492        self.inner().BackendUserData
493    }
494
495    /// Set the backend user data
496    pub fn set_backend_user_data(&mut self, data: *mut c_void) {
497        self.inner_mut().BackendUserData = data;
498    }
499
500    /// Get the texture ID
501    pub fn tex_id(&self) -> TextureId {
502        TextureId::from(self.inner().TexID)
503    }
504
505    /// Set the texture ID
506    ///
507    /// This should only be called by renderer backends after creating or destroying the texture.
508    pub fn set_tex_id(&mut self, tex_id: TextureId) {
509        unsafe {
510            sys::ImTextureData_SetTexID(self.as_raw_mut(), tex_id.id() as sys::ImTextureID);
511        }
512    }
513
514    /// Get the texture format
515    pub fn format(&self) -> TextureFormat {
516        TextureFormat::from(self.inner().Format)
517    }
518
519    /// Get the texture width
520    pub fn width(&self) -> i32 {
521        self.inner().Width
522    }
523
524    /// Get the texture height
525    pub fn height(&self) -> i32 {
526        self.inner().Height
527    }
528
529    /// Get the bytes per pixel
530    pub fn bytes_per_pixel(&self) -> i32 {
531        self.inner().BytesPerPixel
532    }
533
534    /// Get the number of unused frames
535    pub fn unused_frames(&self) -> i32 {
536        self.inner().UnusedFrames
537    }
538
539    /// Get the reference count
540    pub fn ref_count(&self) -> u16 {
541        self.inner().RefCount
542    }
543
544    /// Check if the texture uses colors (rather than just white + alpha)
545    pub fn use_colors(&self) -> bool {
546        self.inner().UseColors
547    }
548
549    /// Check if the texture is queued for destruction next frame
550    pub fn want_destroy_next_frame(&self) -> bool {
551        self.inner().WantDestroyNextFrame
552    }
553
554    /// Get the pixel data
555    ///
556    /// Returns None if no pixel data is available.
557    pub fn pixels(&self) -> Option<&[u8]> {
558        let raw = self.inner();
559        if raw.Pixels.is_null() {
560            None
561        } else {
562            let width = raw.Width;
563            let height = raw.Height;
564            let bytes_per_pixel = raw.BytesPerPixel;
565            if width <= 0 || height <= 0 || bytes_per_pixel <= 0 {
566                return None;
567            }
568
569            let size = (width as usize)
570                .checked_mul(height as usize)?
571                .checked_mul(bytes_per_pixel as usize)?;
572            unsafe { Some(std::slice::from_raw_parts(raw.Pixels as *const u8, size)) }
573        }
574    }
575
576    /// Get the bounding box of all used pixels in the texture
577    pub fn used_rect(&self) -> TextureRect {
578        TextureRect::from(self.inner().UsedRect)
579    }
580
581    /// Get the bounding box of all queued updates
582    pub fn update_rect(&self) -> TextureRect {
583        TextureRect::from(self.inner().UpdateRect)
584    }
585
586    /// Iterate over queued update rectangles (copying to safe TextureRect)
587    pub fn updates(&self) -> impl Iterator<Item = TextureRect> + '_ {
588        let vec = &self.inner().Updates;
589        let count = if vec.Data.is_null() {
590            0
591        } else {
592            usize::try_from(vec.Size).unwrap_or(0)
593        };
594        let data = vec.Data as *const sys::ImTextureRect;
595        (0..count).map(move |i| unsafe { TextureRect::from(*data.add(i)) })
596    }
597
598    /// Get the pixel data at a specific position
599    ///
600    /// Returns None if no pixel data is available or coordinates are out of bounds.
601    pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[u8]> {
602        let raw = self.inner();
603        let width = raw.Width;
604        let height = raw.Height;
605        let bytes_per_pixel = raw.BytesPerPixel;
606        if raw.Pixels.is_null()
607            || width <= 0
608            || height <= 0
609            || bytes_per_pixel <= 0
610            || x < 0
611            || y < 0
612            || x >= width
613            || y >= height
614        {
615            None
616        } else {
617            let width_usize = width as usize;
618            let x_usize = x as usize;
619            let y_usize = y as usize;
620            let bpp_usize = bytes_per_pixel as usize;
621
622            let total_size = width_usize
623                .checked_mul(height as usize)?
624                .checked_mul(bpp_usize)?;
625
626            let offset_px = y_usize.checked_mul(width_usize)?.checked_add(x_usize)?;
627            let offset_bytes = offset_px.checked_mul(bpp_usize)?;
628            let remaining_size = total_size.checked_sub(offset_bytes)?;
629
630            unsafe {
631                let ptr = (raw.Pixels as *const u8).add(offset_bytes);
632                Some(std::slice::from_raw_parts(ptr, remaining_size))
633            }
634        }
635    }
636
637    /// Get the pitch (bytes per row)
638    pub fn pitch(&self) -> i32 {
639        self.width() * self.bytes_per_pixel()
640    }
641
642    /// Create a new texture with the specified format and dimensions
643    ///
644    /// This allocates pixel data and sets the status to WantCreate.
645    pub fn create(&mut self, format: TextureFormat, width: i32, height: i32) {
646        unsafe {
647            sys::ImTextureData_Create(self.as_raw_mut(), format.into(), width, height);
648        }
649    }
650
651    /// Destroy the pixel data
652    ///
653    /// This frees the CPU-side pixel data but doesn't affect the GPU texture.
654    pub fn destroy_pixels(&mut self) {
655        unsafe {
656            sys::ImTextureData_DestroyPixels(self.as_raw_mut());
657        }
658    }
659
660    /// Set the pixel data for the texture
661    ///
662    /// This copies the provided data into the texture's pixel buffer.
663    pub fn set_data(&mut self, data: &[u8]) {
664        unsafe {
665            let raw = self.as_raw_mut();
666            let needed = (*raw)
667                .Width
668                .saturating_mul((*raw).Height)
669                .saturating_mul((*raw).BytesPerPixel);
670            if needed <= 0 {
671                // Nothing to do without valid dimensions/format.
672                return;
673            }
674
675            // Ensure pixel buffer exists and has correct size
676            if (*raw).Pixels.is_null() {
677                sys::ImTextureData_Create(
678                    self.as_raw_mut(),
679                    (*raw).Format,
680                    (*raw).Width,
681                    (*raw).Height,
682                );
683            }
684
685            let copy_bytes = std::cmp::min(needed as usize, data.len());
686            if copy_bytes == 0 {
687                return;
688            }
689
690            std::ptr::copy_nonoverlapping(data.as_ptr(), (*raw).Pixels as *mut u8, copy_bytes);
691
692            // Mark entire texture as updated
693            (*raw).UpdateRect = sys::ImTextureRect {
694                x: 0u16,
695                y: 0u16,
696                w: (*raw).Width.clamp(0, u16::MAX as i32) as u16,
697                h: (*raw).Height.clamp(0, u16::MAX as i32) as u16,
698            };
699            sys::ImTextureData_SetStatus(raw, sys::ImTextureStatus_WantUpdates);
700        }
701    }
702
703    /// Set the width of the texture
704    pub fn set_width(&mut self, width: u32) {
705        let width = width.min(i32::MAX as u32) as i32;
706        self.inner_mut().Width = width;
707    }
708
709    /// Set the height of the texture
710    pub fn set_height(&mut self, height: u32) {
711        let height = height.min(i32::MAX as u32) as i32;
712        self.inner_mut().Height = height;
713    }
714
715    /// Set the format of the texture
716    pub fn set_format(&mut self, format: TextureFormat) {
717        self.inner_mut().Format = format.into();
718    }
719}
720
721/// Get the number of bytes per pixel for a texture format
722pub fn get_format_bytes_per_pixel(format: TextureFormat) -> i32 {
723    unsafe { sys::igImTextureDataGetFormatBytesPerPixel(format.into()) }
724}
725
726/// Create an ImTextureRef from a texture ID
727///
728/// This is the safe way to create an ImTextureRef for use with Dear ImGui.
729/// Use this instead of directly constructing the sys::ImTextureRef structure.
730pub fn create_texture_ref(texture_id: u64) -> sys::ImTextureRef {
731    sys::ImTextureRef {
732        _TexData: std::ptr::null_mut(),
733        _TexID: texture_id,
734    }
735}
736
737/// Get the name of a texture status (for debugging)
738pub fn get_status_name(status: TextureStatus) -> &'static str {
739    unsafe {
740        let ptr = sys::igImTextureDataGetStatusName(status.into());
741        if ptr.is_null() {
742            "Unknown"
743        } else {
744            std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("Invalid")
745        }
746    }
747}
748
749/// Get the name of a texture format (for debugging)
750pub fn get_format_name(format: TextureFormat) -> &'static str {
751    unsafe {
752        let ptr = sys::igImTextureDataGetFormatName(format.into());
753        if ptr.is_null() {
754            "Unknown"
755        } else {
756            std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("Invalid")
757        }
758    }
759}