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/// - When you pass `&mut TextureData` to any widget/draw call, Dear ImGui will place this
397///   texture into `DrawData::textures()` for the current frame.
398/// - Your renderer backend iterates `DrawData::textures()` and performs the requested
399///   create/update/destroy operations, then updates status to `OK`/`Destroyed`.
400/// - You can also set/get a `TexID` (e.g., GPU handle) via `set_tex_id()/tex_id()` after creation.
401///
402/// Lifetime Note: If using the managed path, you must keep the underlying `ImTextureData` alive at
403/// least until the end of the frame where it is referenced by UI calls. If you create textures
404/// yourself, use [`OwnedTextureData`] to ensure the object is correctly constructed and destroyed
405/// by the C++ side.
406#[repr(transparent)]
407pub struct TextureData {
408    raw: UnsafeCell<sys::ImTextureData>,
409}
410
411// Ensure the wrapper stays layout-compatible with the sys bindings.
412const _: [(); std::mem::size_of::<sys::ImTextureData>()] = [(); std::mem::size_of::<TextureData>()];
413const _: [(); std::mem::align_of::<sys::ImTextureData>()] =
414    [(); std::mem::align_of::<TextureData>()];
415
416impl TextureData {
417    #[inline]
418    fn inner(&self) -> &sys::ImTextureData {
419        // Safety: `TextureData` is a view into an ImGui-owned `ImTextureData`. Dear ImGui and
420        // renderer backends can mutate fields (e.g. Status/TexID/BackendUserData) while Rust holds
421        // `&TextureData`, so we store it behind `UnsafeCell` to make that interior mutability
422        // explicit.
423        unsafe { &*self.raw.get() }
424    }
425
426    #[inline]
427    fn inner_mut(&mut self) -> &mut sys::ImTextureData {
428        // Safety: caller has `&mut TextureData`, so this is a unique Rust borrow for this wrapper.
429        unsafe { &mut *self.raw.get() }
430    }
431
432    /// Create a new owned texture data object.
433    ///
434    /// This is kept for convenience. Prefer [`OwnedTextureData::new()`] for clarity.
435    pub fn new() -> OwnedTextureData {
436        OwnedTextureData::new()
437    }
438
439    /// Create a new texture data from raw pointer (crate-internal)
440    ///
441    /// Safety: caller must ensure the pointer is valid for the returned lifetime.
442    pub(crate) unsafe fn from_raw<'a>(raw: *mut sys::ImTextureData) -> &'a mut Self {
443        unsafe { &mut *(raw as *mut Self) }
444    }
445
446    /// Create a shared texture data view from a raw pointer (crate-internal).
447    ///
448    /// Safety: caller must ensure the pointer is valid for the returned lifetime.
449    pub(crate) unsafe fn from_raw_ref<'a>(raw: *const sys::ImTextureData) -> &'a Self {
450        unsafe { &*(raw as *const Self) }
451    }
452
453    /// Get the raw pointer to the underlying ImTextureData
454    pub fn as_raw(&self) -> *const sys::ImTextureData {
455        self.raw.get() as *const _
456    }
457
458    /// Get the raw mutable pointer to the underlying ImTextureData
459    pub fn as_raw_mut(&mut self) -> *mut sys::ImTextureData {
460        self.raw.get()
461    }
462
463    /// Get the unique ID of this texture (for debugging)
464    pub fn unique_id(&self) -> i32 {
465        self.inner().UniqueID
466    }
467
468    /// Get the current status of this texture
469    pub fn status(&self) -> TextureStatus {
470        TextureStatus::from(self.inner().Status)
471    }
472
473    /// Set the status of this texture
474    ///
475    /// This should only be called by renderer backends after handling a request.
476    pub fn set_status(&mut self, status: TextureStatus) {
477        unsafe {
478            // When marking a texture as destroyed, Dear ImGui expects the backend to clear any
479            // backend bindings (TexID/BackendUserData). Otherwise ImGui will assert when
480            // processing the texture list.
481            if status == TextureStatus::Destroyed {
482                sys::ImTextureData_SetTexID(self.as_raw_mut(), 0 as sys::ImTextureID);
483                (*self.as_raw_mut()).BackendUserData = std::ptr::null_mut();
484            }
485            sys::ImTextureData_SetStatus(self.as_raw_mut(), status.into());
486        }
487    }
488
489    /// Get the backend user data
490    pub fn backend_user_data(&self) -> *mut c_void {
491        self.inner().BackendUserData
492    }
493
494    /// Set the backend user data
495    pub fn set_backend_user_data(&mut self, data: *mut c_void) {
496        self.inner_mut().BackendUserData = data;
497    }
498
499    /// Get the texture ID
500    pub fn tex_id(&self) -> TextureId {
501        TextureId::from(self.inner().TexID)
502    }
503
504    /// Set the texture ID
505    ///
506    /// This should only be called by renderer backends after creating or destroying the texture.
507    pub fn set_tex_id(&mut self, tex_id: TextureId) {
508        unsafe {
509            sys::ImTextureData_SetTexID(self.as_raw_mut(), tex_id.id() as sys::ImTextureID);
510        }
511    }
512
513    /// Get the texture format
514    pub fn format(&self) -> TextureFormat {
515        TextureFormat::from(self.inner().Format)
516    }
517
518    /// Get the texture width
519    pub fn width(&self) -> i32 {
520        self.inner().Width
521    }
522
523    /// Get the texture height
524    pub fn height(&self) -> i32 {
525        self.inner().Height
526    }
527
528    /// Get the bytes per pixel
529    pub fn bytes_per_pixel(&self) -> i32 {
530        self.inner().BytesPerPixel
531    }
532
533    /// Get the number of unused frames
534    pub fn unused_frames(&self) -> i32 {
535        self.inner().UnusedFrames
536    }
537
538    /// Get the reference count
539    pub fn ref_count(&self) -> u16 {
540        self.inner().RefCount
541    }
542
543    /// Check if the texture uses colors (rather than just white + alpha)
544    pub fn use_colors(&self) -> bool {
545        self.inner().UseColors
546    }
547
548    /// Check if the texture is queued for destruction next frame
549    pub fn want_destroy_next_frame(&self) -> bool {
550        self.inner().WantDestroyNextFrame
551    }
552
553    /// Get the pixel data
554    ///
555    /// Returns None if no pixel data is available.
556    pub fn pixels(&self) -> Option<&[u8]> {
557        let raw = self.inner();
558        if raw.Pixels.is_null() {
559            None
560        } else {
561            let width = raw.Width;
562            let height = raw.Height;
563            let bytes_per_pixel = raw.BytesPerPixel;
564            if width <= 0 || height <= 0 || bytes_per_pixel <= 0 {
565                return None;
566            }
567
568            let size = (width as usize)
569                .checked_mul(height as usize)?
570                .checked_mul(bytes_per_pixel as usize)?;
571            unsafe { Some(std::slice::from_raw_parts(raw.Pixels as *const u8, size)) }
572        }
573    }
574
575    /// Get the bounding box of all used pixels in the texture
576    pub fn used_rect(&self) -> TextureRect {
577        TextureRect::from(self.inner().UsedRect)
578    }
579
580    /// Get the bounding box of all queued updates
581    pub fn update_rect(&self) -> TextureRect {
582        TextureRect::from(self.inner().UpdateRect)
583    }
584
585    /// Iterate over queued update rectangles (copying to safe TextureRect)
586    pub fn updates(&self) -> impl Iterator<Item = TextureRect> + '_ {
587        let vec = &self.inner().Updates;
588        let count = if vec.Data.is_null() {
589            0
590        } else {
591            usize::try_from(vec.Size).unwrap_or(0)
592        };
593        let data = vec.Data as *const sys::ImTextureRect;
594        (0..count).map(move |i| unsafe { TextureRect::from(*data.add(i)) })
595    }
596
597    /// Get the pixel data at a specific position
598    ///
599    /// Returns None if no pixel data is available or coordinates are out of bounds.
600    pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[u8]> {
601        let raw = self.inner();
602        let width = raw.Width;
603        let height = raw.Height;
604        let bytes_per_pixel = raw.BytesPerPixel;
605        if raw.Pixels.is_null()
606            || width <= 0
607            || height <= 0
608            || bytes_per_pixel <= 0
609            || x < 0
610            || y < 0
611            || x >= width
612            || y >= height
613        {
614            None
615        } else {
616            let width_usize = width as usize;
617            let x_usize = x as usize;
618            let y_usize = y as usize;
619            let bpp_usize = bytes_per_pixel as usize;
620
621            let total_size = width_usize
622                .checked_mul(height as usize)?
623                .checked_mul(bpp_usize)?;
624
625            let offset_px = y_usize.checked_mul(width_usize)?.checked_add(x_usize)?;
626            let offset_bytes = offset_px.checked_mul(bpp_usize)?;
627            let remaining_size = total_size.checked_sub(offset_bytes)?;
628
629            unsafe {
630                let ptr = (raw.Pixels as *const u8).add(offset_bytes);
631                Some(std::slice::from_raw_parts(ptr, remaining_size))
632            }
633        }
634    }
635
636    /// Get the pitch (bytes per row)
637    pub fn pitch(&self) -> i32 {
638        self.width() * self.bytes_per_pixel()
639    }
640
641    /// Create a new texture with the specified format and dimensions
642    ///
643    /// This allocates pixel data and sets the status to WantCreate.
644    pub fn create(&mut self, format: TextureFormat, width: i32, height: i32) {
645        unsafe {
646            sys::ImTextureData_Create(self.as_raw_mut(), format.into(), width, height);
647        }
648    }
649
650    /// Destroy the pixel data
651    ///
652    /// This frees the CPU-side pixel data but doesn't affect the GPU texture.
653    pub fn destroy_pixels(&mut self) {
654        unsafe {
655            sys::ImTextureData_DestroyPixels(self.as_raw_mut());
656        }
657    }
658
659    /// Set the pixel data for the texture
660    ///
661    /// This copies the provided data into the texture's pixel buffer.
662    pub fn set_data(&mut self, data: &[u8]) {
663        unsafe {
664            let raw = self.as_raw_mut();
665            let needed = (*raw)
666                .Width
667                .saturating_mul((*raw).Height)
668                .saturating_mul((*raw).BytesPerPixel);
669            if needed <= 0 {
670                // Nothing to do without valid dimensions/format.
671                return;
672            }
673
674            // Ensure pixel buffer exists and has correct size
675            if (*raw).Pixels.is_null() {
676                sys::ImTextureData_Create(
677                    self.as_raw_mut(),
678                    (*raw).Format,
679                    (*raw).Width,
680                    (*raw).Height,
681                );
682            }
683
684            let copy_bytes = std::cmp::min(needed as usize, data.len());
685            if copy_bytes == 0 {
686                return;
687            }
688
689            std::ptr::copy_nonoverlapping(data.as_ptr(), (*raw).Pixels as *mut u8, copy_bytes);
690
691            // Mark entire texture as updated
692            (*raw).UpdateRect = sys::ImTextureRect {
693                x: 0u16,
694                y: 0u16,
695                w: (*raw).Width.clamp(0, u16::MAX as i32) as u16,
696                h: (*raw).Height.clamp(0, u16::MAX as i32) as u16,
697            };
698            sys::ImTextureData_SetStatus(raw, sys::ImTextureStatus_WantUpdates);
699        }
700    }
701
702    /// Set the width of the texture
703    pub fn set_width(&mut self, width: u32) {
704        let width = width.min(i32::MAX as u32) as i32;
705        self.inner_mut().Width = width;
706    }
707
708    /// Set the height of the texture
709    pub fn set_height(&mut self, height: u32) {
710        let height = height.min(i32::MAX as u32) as i32;
711        self.inner_mut().Height = height;
712    }
713
714    /// Set the format of the texture
715    pub fn set_format(&mut self, format: TextureFormat) {
716        self.inner_mut().Format = format.into();
717    }
718}
719
720/// Get the number of bytes per pixel for a texture format
721pub fn get_format_bytes_per_pixel(format: TextureFormat) -> i32 {
722    unsafe { sys::igImTextureDataGetFormatBytesPerPixel(format.into()) }
723}
724
725/// Create an ImTextureRef from a texture ID
726///
727/// This is the safe way to create an ImTextureRef for use with Dear ImGui.
728/// Use this instead of directly constructing the sys::ImTextureRef structure.
729pub fn create_texture_ref(texture_id: u64) -> sys::ImTextureRef {
730    sys::ImTextureRef {
731        _TexData: std::ptr::null_mut(),
732        _TexID: texture_id,
733    }
734}
735
736/// Get the name of a texture status (for debugging)
737pub fn get_status_name(status: TextureStatus) -> &'static str {
738    unsafe {
739        let ptr = sys::igImTextureDataGetStatusName(status.into());
740        if ptr.is_null() {
741            "Unknown"
742        } else {
743            std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("Invalid")
744        }
745    }
746}
747
748/// Get the name of a texture format (for debugging)
749pub fn get_format_name(format: TextureFormat) -> &'static str {
750    unsafe {
751        let ptr = sys::igImTextureDataGetFormatName(format.into());
752        if ptr.is_null() {
753            "Unknown"
754        } else {
755            std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("Invalid")
756        }
757    }
758}