1use crate::{GlTexture, InitError, InitResult};
4use dear_imgui_rs::{OwnedTextureData, TextureData, TextureFormat, TextureId, TextureStatus};
5use glow::{Context, HasContext};
6use std::collections::HashMap;
7
8pub trait TextureMap {
10 fn get(&self, texture_id: TextureId) -> Option<GlTexture>;
12
13 fn set(&mut self, texture_id: TextureId, gl_texture: GlTexture);
15
16 fn remove(&mut self, texture_id: TextureId) -> Option<GlTexture>;
18
19 fn clear(&mut self);
21
22 fn register_texture(
24 &mut self,
25 gl_texture: GlTexture,
26 width: i32,
27 height: i32,
28 format: TextureFormat,
29 ) -> TextureId;
30
31 fn update_texture(
33 &mut self,
34 texture_id: TextureId,
35 gl_texture: GlTexture,
36 width: i32,
37 height: i32,
38 );
39
40 fn get_texture_data(&self, texture_id: TextureId) -> Option<&TextureData>;
42
43 fn get_texture_data_mut(&mut self, texture_id: TextureId) -> Option<&mut TextureData>;
45}
46
47#[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 pub fn new() -> Self {
123 Self {
124 textures: HashMap::new(),
125 texture_data: HashMap::new(),
126 next_id: 0,
127 }
128 }
129
130 pub fn len(&self) -> usize {
132 self.textures.len()
133 }
134
135 pub fn is_empty(&self) -> bool {
137 self.textures.is_empty()
138 }
139
140 pub fn iter(&self) -> impl Iterator<Item = (&TextureId, &GlTexture)> {
142 self.textures.iter()
143 }
144
145 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
153pub 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 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
204pub 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 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 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
262pub 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
290pub 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 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 gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
307
308 let gl_texture = if texture_id.id() == 0 {
309 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 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 gl.bind_texture(glow::TEXTURE_2D, last_texture);
373
374 Ok(gl_texture)
375 }
376}