Skip to main content

dear_imgui_glow/
texture.rs

1//! Texture management for Dear ImGui
2
3use crate::{GlTexture, InitError, InitResult};
4use dear_imgui_rs::{OwnedTextureData, TextureData, TextureFormat, TextureId, TextureStatus};
5use glow::{Context, HasContext};
6use std::collections::HashMap;
7
8/// Trait for managing texture mappings with modern Dear ImGui texture system
9pub trait TextureMap {
10    /// Get the OpenGL texture for a Dear ImGui texture ID
11    fn get(&self, texture_id: TextureId) -> Option<GlTexture>;
12
13    /// Set the OpenGL texture for a Dear ImGui texture ID
14    fn set(&mut self, texture_id: TextureId, gl_texture: GlTexture);
15
16    /// Remove a texture mapping
17    fn remove(&mut self, texture_id: TextureId) -> Option<GlTexture>;
18
19    /// Clear all texture mappings
20    fn clear(&mut self);
21
22    /// Register a texture with Dear ImGui's texture management system
23    fn register_texture(
24        &mut self,
25        gl_texture: GlTexture,
26        width: i32,
27        height: i32,
28        format: TextureFormat,
29    ) -> TextureId;
30
31    /// Update a texture in Dear ImGui's texture management system
32    fn update_texture(
33        &mut self,
34        texture_id: TextureId,
35        gl_texture: GlTexture,
36        width: i32,
37        height: i32,
38    );
39
40    /// Get texture data from Dear ImGui's texture management system
41    fn get_texture_data(&self, texture_id: TextureId) -> Option<&TextureData>;
42
43    /// Get mutable texture data from Dear ImGui's texture management system
44    fn get_texture_data_mut(&mut self, texture_id: TextureId) -> Option<&mut TextureData>;
45}
46
47/// Simple texture map implementation using a HashMap with modern texture management
48#[derive(Default)]
49pub struct SimpleTextureMap {
50    textures: HashMap<TextureId, GlTexture>,
51    texture_data: HashMap<TextureId, OwnedTextureData>,
52    next_id: usize,
53}
54
55impl TextureMap for SimpleTextureMap {
56    fn get(&self, texture_id: TextureId) -> Option<GlTexture> {
57        self.textures.get(&texture_id).copied()
58    }
59
60    fn set(&mut self, texture_id: TextureId, gl_texture: GlTexture) {
61        self.textures.insert(texture_id, gl_texture);
62    }
63
64    fn remove(&mut self, texture_id: TextureId) -> Option<GlTexture> {
65        let gl_texture = self.textures.remove(&texture_id);
66        self.texture_data.remove(&texture_id);
67        gl_texture
68    }
69
70    fn clear(&mut self) {
71        self.textures.clear();
72        self.texture_data.clear();
73    }
74
75    fn register_texture(
76        &mut self,
77        gl_texture: GlTexture,
78        width: i32,
79        height: i32,
80        format: TextureFormat,
81    ) -> TextureId {
82        self.next_id += 1;
83        let texture_id = TextureId::new(self.next_id as u64);
84
85        let mut texture_data = TextureData::new();
86        texture_data.create(format, width, height);
87        texture_data.set_tex_id(texture_id);
88        texture_data.set_status(TextureStatus::OK);
89
90        self.textures.insert(texture_id, gl_texture);
91        self.texture_data.insert(texture_id, texture_data);
92
93        texture_id
94    }
95
96    fn update_texture(
97        &mut self,
98        texture_id: TextureId,
99        gl_texture: GlTexture,
100        _width: i32,
101        _height: i32,
102    ) {
103        self.textures.insert(texture_id, gl_texture);
104
105        if let Some(texture_data) = self.texture_data.get_mut(&texture_id) {
106            texture_data.set_tex_id(texture_id);
107            texture_data.set_status(TextureStatus::OK);
108        }
109    }
110
111    fn get_texture_data(&self, texture_id: TextureId) -> Option<&TextureData> {
112        self.texture_data.get(&texture_id).map(AsRef::as_ref)
113    }
114
115    fn get_texture_data_mut(&mut self, texture_id: TextureId) -> Option<&mut TextureData> {
116        self.texture_data.get_mut(&texture_id).map(AsMut::as_mut)
117    }
118}
119
120impl SimpleTextureMap {
121    /// Create a new empty texture map
122    pub fn new() -> Self {
123        Self {
124            textures: HashMap::new(),
125            texture_data: HashMap::new(),
126            next_id: 0,
127        }
128    }
129
130    /// Get the number of textures in the map
131    pub fn len(&self) -> usize {
132        self.textures.len()
133    }
134
135    /// Check if the texture map is empty
136    pub fn is_empty(&self) -> bool {
137        self.textures.is_empty()
138    }
139
140    /// Iterate over all texture mappings
141    pub fn iter(&self) -> impl Iterator<Item = (&TextureId, &GlTexture)> {
142        self.textures.iter()
143    }
144
145    /// Iterate over all texture data
146    pub fn texture_data_iter(&self) -> impl Iterator<Item = (&TextureId, &TextureData)> {
147        self.texture_data
148            .iter()
149            .map(|(id, texture_data)| (id, texture_data.as_ref()))
150    }
151}
152
153/// Create a texture from raw RGBA data
154pub fn create_texture_from_rgba(
155    gl: &Context,
156    width: u32,
157    height: u32,
158    data: &[u8],
159) -> InitResult<GlTexture> {
160    unsafe {
161        let texture = gl.create_texture().map_err(InitError::CreateTexture)?;
162
163        gl.bind_texture(glow::TEXTURE_2D, Some(texture));
164        gl.tex_image_2d(
165            glow::TEXTURE_2D,
166            0,
167            glow::RGBA as i32,
168            width as i32,
169            height as i32,
170            0,
171            glow::RGBA,
172            glow::UNSIGNED_BYTE,
173            glow::PixelUnpackData::Slice(Some(data)),
174        );
175
176        // Set texture parameters
177        gl.tex_parameter_i32(
178            glow::TEXTURE_2D,
179            glow::TEXTURE_MIN_FILTER,
180            glow::LINEAR as i32,
181        );
182        gl.tex_parameter_i32(
183            glow::TEXTURE_2D,
184            glow::TEXTURE_MAG_FILTER,
185            glow::LINEAR as i32,
186        );
187        gl.tex_parameter_i32(
188            glow::TEXTURE_2D,
189            glow::TEXTURE_WRAP_S,
190            glow::CLAMP_TO_EDGE as i32,
191        );
192        gl.tex_parameter_i32(
193            glow::TEXTURE_2D,
194            glow::TEXTURE_WRAP_T,
195            glow::CLAMP_TO_EDGE as i32,
196        );
197
198        gl.bind_texture(glow::TEXTURE_2D, None);
199
200        Ok(texture)
201    }
202}
203
204/// Create a texture from raw alpha data (single channel)
205pub fn create_texture_from_alpha(
206    gl: &Context,
207    width: u32,
208    height: u32,
209    data: &[u8],
210) -> InitResult<GlTexture> {
211    unsafe {
212        let texture = gl.create_texture().map_err(InitError::CreateTexture)?;
213
214        gl.bind_texture(glow::TEXTURE_2D, Some(texture));
215
216        // Set pixel store parameters
217        gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
218        gl.pixel_store_i32(glow::UNPACK_SKIP_PIXELS, 0);
219        gl.pixel_store_i32(glow::UNPACK_SKIP_ROWS, 0);
220        gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
221
222        gl.tex_image_2d(
223            glow::TEXTURE_2D,
224            0,
225            glow::RED as i32,
226            width as i32,
227            height as i32,
228            0,
229            glow::RED,
230            glow::UNSIGNED_BYTE,
231            glow::PixelUnpackData::Slice(Some(data)),
232        );
233
234        // Set texture parameters
235        gl.tex_parameter_i32(
236            glow::TEXTURE_2D,
237            glow::TEXTURE_MIN_FILTER,
238            glow::LINEAR as i32,
239        );
240        gl.tex_parameter_i32(
241            glow::TEXTURE_2D,
242            glow::TEXTURE_MAG_FILTER,
243            glow::LINEAR as i32,
244        );
245        gl.tex_parameter_i32(
246            glow::TEXTURE_2D,
247            glow::TEXTURE_WRAP_S,
248            glow::CLAMP_TO_EDGE as i32,
249        );
250        gl.tex_parameter_i32(
251            glow::TEXTURE_2D,
252            glow::TEXTURE_WRAP_T,
253            glow::CLAMP_TO_EDGE as i32,
254        );
255
256        gl.bind_texture(glow::TEXTURE_2D, None);
257
258        Ok(texture)
259    }
260}
261
262/// Update a texture with new data
263pub fn update_texture(
264    gl: &Context,
265    texture: GlTexture,
266    x: u32,
267    y: u32,
268    width: u32,
269    height: u32,
270    data: &[u8],
271    format: u32,
272) {
273    unsafe {
274        gl.bind_texture(glow::TEXTURE_2D, Some(texture));
275        gl.tex_sub_image_2d(
276            glow::TEXTURE_2D,
277            0,
278            x as i32,
279            y as i32,
280            width as i32,
281            height as i32,
282            format,
283            glow::UNSIGNED_BYTE,
284            glow::PixelUnpackData::Slice(Some(data)),
285        );
286        gl.bind_texture(glow::TEXTURE_2D, None);
287    }
288}
289
290/// Update texture from ImGui texture data (similar to ImGui_ImplOpenGL3_UpdateTexture)
291pub fn update_imgui_texture(
292    gl: &Context,
293    texture_id: TextureId,
294    width: u32,
295    height: u32,
296    data: &[u8],
297) -> InitResult<GlTexture> {
298    unsafe {
299        // Backup current texture binding
300        let last_texture = u32::try_from(gl.get_parameter_i32(glow::TEXTURE_BINDING_2D))
301            .ok()
302            .and_then(std::num::NonZeroU32::new)
303            .map(glow::NativeTexture);
304
305        // Set pixel store parameters
306        gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
307
308        let gl_texture = if texture_id.id() == 0 {
309            // Create new texture
310            let texture = gl.create_texture().map_err(InitError::CreateTexture)?;
311
312            gl.bind_texture(glow::TEXTURE_2D, Some(texture));
313            gl.tex_parameter_i32(
314                glow::TEXTURE_2D,
315                glow::TEXTURE_MIN_FILTER,
316                glow::LINEAR as i32,
317            );
318            gl.tex_parameter_i32(
319                glow::TEXTURE_2D,
320                glow::TEXTURE_MAG_FILTER,
321                glow::LINEAR as i32,
322            );
323            gl.tex_parameter_i32(
324                glow::TEXTURE_2D,
325                glow::TEXTURE_WRAP_S,
326                glow::CLAMP_TO_EDGE as i32,
327            );
328            gl.tex_parameter_i32(
329                glow::TEXTURE_2D,
330                glow::TEXTURE_WRAP_T,
331                glow::CLAMP_TO_EDGE as i32,
332            );
333            gl.tex_image_2d(
334                glow::TEXTURE_2D,
335                0,
336                glow::RGBA as i32,
337                width as i32,
338                height as i32,
339                0,
340                glow::RGBA,
341                glow::UNSIGNED_BYTE,
342                glow::PixelUnpackData::Slice(Some(data)),
343            );
344
345            texture
346        } else {
347            // Update existing texture
348            let texture_u32 = u32::try_from(texture_id.id()).map_err(|_| {
349                InitError::Generic("TextureId is out of range for OpenGL".to_string())
350            })?;
351            let texture_nz = std::num::NonZeroU32::new(texture_u32).ok_or_else(|| {
352                InitError::Generic("TextureId must be non-zero for OpenGL".to_string())
353            })?;
354            let texture = glow::NativeTexture(texture_nz);
355            gl.bind_texture(glow::TEXTURE_2D, Some(texture));
356            gl.tex_image_2d(
357                glow::TEXTURE_2D,
358                0,
359                glow::RGBA as i32,
360                width as i32,
361                height as i32,
362                0,
363                glow::RGBA,
364                glow::UNSIGNED_BYTE,
365                glow::PixelUnpackData::Slice(Some(data)),
366            );
367
368            texture
369        };
370
371        // Restore previous texture binding
372        gl.bind_texture(glow::TEXTURE_2D, last_texture);
373
374        Ok(gl_texture)
375    }
376}