1use crate::{RenderResources, RendererError, RendererResult};
7use dear_imgui_rs::{TextureData, TextureFormat as ImGuiTextureFormat, TextureId, TextureStatus};
8use std::collections::HashMap;
9use wgpu::*;
10
11#[derive(Debug, Clone)]
17pub enum TextureUpdateResult {
18 Created { texture_id: TextureId },
20 Updated,
22 Destroyed,
24 Failed,
26 NoAction,
28}
29
30impl TextureUpdateResult {
31 pub fn apply_to(self, texture_data: &mut TextureData) {
36 match self {
37 TextureUpdateResult::Created { texture_id } => {
38 texture_data.set_tex_id(texture_id);
39 texture_data.set_status(TextureStatus::OK);
40 }
41 TextureUpdateResult::Updated => {
42 texture_data.set_status(TextureStatus::OK);
43 }
44 TextureUpdateResult::Destroyed => {
45 texture_data.set_status(TextureStatus::Destroyed);
46 }
47 TextureUpdateResult::Failed => {
48 texture_data.set_status(TextureStatus::Destroyed);
49 }
50 TextureUpdateResult::NoAction => {
51 }
53 }
54 }
55}
56
57#[derive(Debug)]
61pub struct WgpuTexture {
62 pub texture: Texture,
64 pub texture_view: TextureView,
66}
67
68impl WgpuTexture {
69 pub fn new(texture: Texture, texture_view: TextureView) -> Self {
71 Self {
72 texture,
73 texture_view,
74 }
75 }
76
77 pub fn view(&self) -> &TextureView {
79 &self.texture_view
80 }
81
82 pub fn texture(&self) -> &Texture {
84 &self.texture
85 }
86}
87
88#[derive(Debug)]
93pub struct WgpuTextureManager {
94 textures: HashMap<u64, WgpuTexture>,
96 next_id: u64,
98 custom_samplers: HashMap<u64, Sampler>,
100 custom_sampler_by_texture: HashMap<u64, u64>,
102 common_bind_groups: HashMap<u64, BindGroup>,
104 next_sampler_id: u64,
106}
107
108impl Default for WgpuTextureManager {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114impl WgpuTextureManager {
115 fn convert_subrect_to_rgba(
117 texture_data: &TextureData,
118 rect: dear_imgui_rs::texture::TextureRect,
119 ) -> Option<Vec<u8>> {
120 let pixels = texture_data.pixels()?;
121 let tex_w = texture_data.width() as usize;
122 let tex_h = texture_data.height() as usize;
123 if tex_w == 0 || tex_h == 0 {
124 return None;
125 }
126
127 let bpp = texture_data.bytes_per_pixel() as usize;
128 let (rx, ry, rw, rh) = (
129 rect.x as usize,
130 rect.y as usize,
131 rect.w as usize,
132 rect.h as usize,
133 );
134 if rw == 0 || rh == 0 || rx >= tex_w || ry >= tex_h {
135 return None;
136 }
137
138 let rw = rw.min(tex_w.saturating_sub(rx));
140 let rh = rh.min(tex_h.saturating_sub(ry));
141
142 let mut out = vec![0u8; rw * rh * 4];
143 match texture_data.format() {
144 ImGuiTextureFormat::RGBA32 => {
145 for row in 0..rh {
146 let src_off = ((ry + row) * tex_w + rx) * bpp;
147 let dst_off = row * rw * 4;
148 out[dst_off..dst_off + rw * 4]
150 .copy_from_slice(&pixels[src_off..src_off + rw * 4]);
151 }
152 }
153 ImGuiTextureFormat::Alpha8 => {
154 for row in 0..rh {
155 let src_off = ((ry + row) * tex_w + rx) * bpp; let dst_off = row * rw * 4;
157 for i in 0..rw {
158 let a = pixels[src_off + i];
159 let dst = &mut out[dst_off + i * 4..dst_off + i * 4 + 4];
160 dst.copy_from_slice(&[255, 255, 255, a]);
161 }
162 }
163 }
164 }
165 Some(out)
166 }
167
168 fn apply_subrect_updates(
171 &mut self,
172 queue: &Queue,
173 texture_data: &TextureData,
174 texture_id: u64,
175 ) -> RendererResult<bool> {
176 let wgpu_tex = match self.textures.get(&texture_id) {
177 Some(t) => t,
178 None => return Ok(false),
179 };
180
181 let mut rects: Vec<dear_imgui_rs::texture::TextureRect> = texture_data.updates().collect();
184 if rects.is_empty() {
185 let r = texture_data.update_rect();
186 if r.w > 0 && r.h > 0 {
187 rects.push(r);
188 }
189 }
190 if rects.is_empty() {
191 return Ok(false);
192 }
193
194 for rect in rects {
196 if let Some(tight_rgba) = Self::convert_subrect_to_rgba(texture_data, rect) {
197 let width = rect.w as u32;
198 let height = rect.h as u32;
199 let bpp = 4u32;
200 let unpadded_bytes_per_row = width * bpp;
201 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; let padded_bytes_per_row = unpadded_bytes_per_row.div_ceil(align) * align;
203
204 if padded_bytes_per_row == unpadded_bytes_per_row {
205 queue.write_texture(
207 wgpu::TexelCopyTextureInfo {
208 texture: wgpu_tex.texture(),
209 mip_level: 0,
210 origin: wgpu::Origin3d {
211 x: rect.x as u32,
212 y: rect.y as u32,
213 z: 0,
214 },
215 aspect: wgpu::TextureAspect::All,
216 },
217 &tight_rgba,
218 wgpu::TexelCopyBufferLayout {
219 offset: 0,
220 bytes_per_row: Some(unpadded_bytes_per_row),
221 rows_per_image: Some(height),
222 },
223 wgpu::Extent3d {
224 width,
225 height,
226 depth_or_array_layers: 1,
227 },
228 );
229 } else {
230 let mut padded = vec![0u8; (padded_bytes_per_row * height) as usize];
232 for row in 0..height as usize {
233 let src_off = row * (unpadded_bytes_per_row as usize);
234 let dst_off = row * (padded_bytes_per_row as usize);
235 padded[dst_off..dst_off + (unpadded_bytes_per_row as usize)]
236 .copy_from_slice(
237 &tight_rgba[src_off..src_off + (unpadded_bytes_per_row as usize)],
238 );
239 }
240 queue.write_texture(
241 wgpu::TexelCopyTextureInfo {
242 texture: wgpu_tex.texture(),
243 mip_level: 0,
244 origin: wgpu::Origin3d {
245 x: rect.x as u32,
246 y: rect.y as u32,
247 z: 0,
248 },
249 aspect: wgpu::TextureAspect::All,
250 },
251 &padded,
252 wgpu::TexelCopyBufferLayout {
253 offset: 0,
254 bytes_per_row: Some(padded_bytes_per_row),
255 rows_per_image: Some(height),
256 },
257 wgpu::Extent3d {
258 width,
259 height,
260 depth_or_array_layers: 1,
261 },
262 );
263 }
264 if cfg!(debug_assertions) {
265 tracing::debug!(
266 target: "dear-imgui-wgpu",
267 "[dear-imgui-wgpu][debug] Updated texture id={} subrect x={} y={} w={} h={}",
268 texture_id, rect.x, rect.y, rect.w, rect.h
269 );
270 }
271 } else {
272 if cfg!(debug_assertions) {
274 tracing::debug!(
275 target: "dear-imgui-wgpu",
276 "[dear-imgui-wgpu][debug] Skipped subrect update: no pixels available"
277 );
278 }
279 return Ok(false);
280 }
281 }
282
283 Ok(true)
284 }
285 pub fn new() -> Self {
287 Self {
288 textures: HashMap::new(),
289 next_id: 1, custom_samplers: HashMap::new(),
291 custom_sampler_by_texture: HashMap::new(),
292 common_bind_groups: HashMap::new(),
293 next_sampler_id: 1, }
295 }
296
297 pub fn register_texture(&mut self, texture: WgpuTexture) -> u64 {
299 let id = self.next_id;
300 self.next_id += 1;
301 self.textures.insert(id, texture);
302 id
303 }
304
305 pub fn get_texture(&self, id: u64) -> Option<&WgpuTexture> {
307 self.textures.get(&id)
308 }
309
310 pub fn remove_texture(&mut self, id: u64) -> Option<WgpuTexture> {
312 self.textures.remove(&id)
313 }
314
315 pub fn contains_texture(&self, id: u64) -> bool {
317 self.textures.contains_key(&id)
318 }
319
320 pub fn insert_texture_with_id(&mut self, id: u64, texture: WgpuTexture) {
322 self.textures.insert(id, texture);
323 if id >= self.next_id {
325 self.next_id = id + 1;
326 }
327 }
328
329 pub(crate) fn set_custom_sampler_for_texture(
333 &mut self,
334 texture_id: u64,
335 sampler: Sampler,
336 ) -> u64 {
337 let sampler_id = self.next_sampler_id;
338 self.next_sampler_id += 1;
339 self.custom_samplers.insert(sampler_id, sampler);
340 self.custom_sampler_by_texture
341 .insert(texture_id, sampler_id);
342 self.common_bind_groups.remove(&sampler_id);
344 sampler_id
345 }
346
347 pub(crate) fn update_custom_sampler_for_texture(
355 &mut self,
356 texture_id: u64,
357 sampler: Sampler,
358 ) -> bool {
359 if !self.textures.contains_key(&texture_id) {
360 return false;
361 }
362 if let Some(sampler_id) = self.custom_sampler_by_texture.get(&texture_id).copied() {
363 self.custom_samplers.insert(sampler_id, sampler);
364 self.common_bind_groups.remove(&sampler_id);
365 } else {
366 self.set_custom_sampler_for_texture(texture_id, sampler);
367 }
368 true
369 }
370
371 pub(crate) fn custom_sampler_id_for_texture(&self, texture_id: u64) -> Option<u64> {
373 self.custom_sampler_by_texture.get(&texture_id).copied()
374 }
375
376 pub(crate) fn clear_custom_sampler_for_texture(&mut self, texture_id: u64) {
378 if let Some(sampler_id) = self.custom_sampler_by_texture.remove(&texture_id) {
379 self.common_bind_groups.remove(&sampler_id);
381 }
382 }
383
384 pub(crate) fn get_or_create_common_bind_group_for_sampler(
389 &mut self,
390 device: &Device,
391 common_layout: &BindGroupLayout,
392 uniform_buffer: &Buffer,
393 sampler_id: u64,
394 ) -> Option<BindGroup> {
395 if let Some(bg) = self.common_bind_groups.get(&sampler_id) {
396 return Some(bg.clone());
397 }
398 let sampler = self.custom_samplers.get(&sampler_id)?;
399 let bg = device.create_bind_group(&BindGroupDescriptor {
400 label: Some("Dear ImGui Common Bind Group (custom sampler)"),
401 layout: common_layout,
402 entries: &[
403 BindGroupEntry {
404 binding: 0,
405 resource: uniform_buffer.as_entire_binding(),
406 },
407 BindGroupEntry {
408 binding: 1,
409 resource: BindingResource::Sampler(sampler),
410 },
411 ],
412 });
413 self.common_bind_groups.insert(sampler_id, bg.clone());
414 Some(bg)
415 }
416
417 pub fn destroy_texture_by_id(&mut self, id: u64) {
419 self.remove_texture(id);
420 }
421
422 pub fn update_texture_from_data_with_id(
424 &mut self,
425 device: &Device,
426 queue: &Queue,
427 texture_data: &TextureData,
428 texture_id: u64,
429 ) -> RendererResult<()> {
430 if self.contains_texture(texture_id) {
433 self.remove_texture(texture_id);
435
436 let new_texture_id = self.create_texture_from_data(device, queue, texture_data)?;
438
439 if new_texture_id != texture_id
441 && let Some(texture) = self.remove_texture(new_texture_id)
442 {
443 self.insert_texture_with_id(texture_id, texture);
444 }
445
446 Ok(())
447 } else {
448 Err(RendererError::InvalidTextureId(texture_id))
449 }
450 }
451
452 pub fn texture_count(&self) -> usize {
454 self.textures.len()
455 }
456
457 pub fn clear(&mut self) {
459 self.textures.clear();
460 self.next_id = 1;
461 self.custom_sampler_by_texture.clear();
462 self.common_bind_groups.clear();
463 self.custom_samplers.clear();
465 self.next_sampler_id = 1;
466 }
467}
468
469impl WgpuTextureManager {
471 pub fn create_texture_from_data(
473 &mut self,
474 device: &Device,
475 queue: &Queue,
476 texture_data: &TextureData,
477 ) -> RendererResult<u64> {
478 let width = texture_data.width() as u32;
479 let height = texture_data.height() as u32;
480 let format = texture_data.format();
481
482 let pixels = texture_data
483 .pixels()
484 .ok_or_else(|| RendererError::BadTexture("No pixel data available".to_string()))?;
485
486 let (wgpu_format, converted_data, _bytes_per_pixel) = match format {
489 ImGuiTextureFormat::RGBA32 => {
490 if pixels.len() != (width * height * 4) as usize {
492 return Err(RendererError::BadTexture(format!(
493 "RGBA32 texture data size mismatch: expected {} bytes, got {}",
494 width * height * 4,
495 pixels.len()
496 )));
497 }
498 (TextureFormat::Rgba8Unorm, pixels.to_vec(), 4u32)
499 }
500 ImGuiTextureFormat::Alpha8 => {
501 if pixels.len() != (width * height) as usize {
504 return Err(RendererError::BadTexture(format!(
505 "Alpha8 texture data size mismatch: expected {} bytes, got {}",
506 width * height,
507 pixels.len()
508 )));
509 }
510 let mut rgba_data = Vec::with_capacity(pixels.len() * 4);
511 for &alpha in pixels {
512 rgba_data.extend_from_slice(&[255, 255, 255, alpha]); }
514 (TextureFormat::Rgba8Unorm, rgba_data, 4u32)
515 }
516 };
517
518 if cfg!(debug_assertions) {
520 tracing::debug!(
521 target: "dear-imgui-wgpu",
522 "[dear-imgui-wgpu][debug] Create texture: {}x{} format={:?}",
523 width, height, format
524 );
525 }
526 let texture = device.create_texture(&TextureDescriptor {
527 label: Some("Dear ImGui Texture"),
528 size: Extent3d {
529 width,
530 height,
531 depth_or_array_layers: 1,
532 },
533 mip_level_count: 1,
534 sample_count: 1,
535 dimension: TextureDimension::D2,
536 format: wgpu_format,
537 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
538 view_formats: &[],
539 });
540
541 let expected_size = (width * height * 4) as usize; if converted_data.len() != expected_size {
544 return Err(RendererError::BadTexture(format!(
545 "Converted texture data size mismatch: expected {} bytes, got {}",
546 expected_size,
547 converted_data.len()
548 )));
549 }
550
551 let bpp = 4u32;
554 let unpadded_bytes_per_row = width * bpp;
555 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; let padded_bytes_per_row = unpadded_bytes_per_row.div_ceil(align) * align;
557 if padded_bytes_per_row == unpadded_bytes_per_row {
558 queue.write_texture(
560 wgpu::TexelCopyTextureInfo {
561 texture: &texture,
562 mip_level: 0,
563 origin: wgpu::Origin3d::ZERO,
564 aspect: wgpu::TextureAspect::All,
565 },
566 &converted_data,
567 wgpu::TexelCopyBufferLayout {
568 offset: 0,
569 bytes_per_row: Some(unpadded_bytes_per_row),
570 rows_per_image: Some(height),
571 },
572 Extent3d {
573 width,
574 height,
575 depth_or_array_layers: 1,
576 },
577 );
578 } else {
579 let mut padded: Vec<u8> = vec![0; (padded_bytes_per_row * height) as usize];
581 for row in 0..height as usize {
582 let src_off = row * (unpadded_bytes_per_row as usize);
583 let dst_off = row * (padded_bytes_per_row as usize);
584 padded[dst_off..dst_off + (unpadded_bytes_per_row as usize)].copy_from_slice(
585 &converted_data[src_off..src_off + (unpadded_bytes_per_row as usize)],
586 );
587 }
588 queue.write_texture(
589 wgpu::TexelCopyTextureInfo {
590 texture: &texture,
591 mip_level: 0,
592 origin: wgpu::Origin3d::ZERO,
593 aspect: wgpu::TextureAspect::All,
594 },
595 &padded,
596 wgpu::TexelCopyBufferLayout {
597 offset: 0,
598 bytes_per_row: Some(padded_bytes_per_row),
599 rows_per_image: Some(height),
600 },
601 Extent3d {
602 width,
603 height,
604 depth_or_array_layers: 1,
605 },
606 );
607 if cfg!(debug_assertions) {
608 tracing::debug!(
609 target: "dear-imgui-wgpu",
610 "[dear-imgui-wgpu][debug] Upload texture with padded row pitch: unpadded={} padded={}",
611 unpadded_bytes_per_row, padded_bytes_per_row
612 );
613 }
614 }
615
616 let texture_view = texture.create_view(&TextureViewDescriptor::default());
618
619 let wgpu_texture = WgpuTexture::new(texture, texture_view);
621
622 let texture_id = self.register_texture(wgpu_texture);
624 if cfg!(debug_assertions) {
625 tracing::debug!(
626 target: "dear-imgui-wgpu",
627 "[dear-imgui-wgpu][debug] Texture registered: id={}",
628 texture_id
629 );
630 }
631 Ok(texture_id)
632 }
633
634 pub fn update_texture_from_data(
636 &mut self,
637 device: &Device,
638 queue: &Queue,
639 texture_data: &TextureData,
640 ) -> RendererResult<()> {
641 let texture_id = texture_data.tex_id().id();
642
643 if self.contains_texture(texture_id) {
647 if self.apply_subrect_updates(queue, texture_data, texture_id)? {
649 return Ok(());
650 }
651
652 self.remove_texture(texture_id);
654 let new_texture_id = self.create_texture_from_data(device, queue, texture_data)?;
655 if new_texture_id != texture_id
656 && let Some(texture) = self.remove_texture(new_texture_id)
657 {
658 self.insert_texture_with_id(texture_id, texture);
659 }
660 } else {
661 let new_texture_id = self.create_texture_from_data(device, queue, texture_data)?;
663 if new_texture_id != texture_id
664 && let Some(texture) = self.remove_texture(new_texture_id)
665 {
666 self.insert_texture_with_id(texture_id, texture);
667 }
668 }
669
670 Ok(())
671 }
672
673 pub fn destroy_texture(&mut self, texture_id: TextureId) {
675 let texture_id_u64 = texture_id.id();
676 self.remove_texture(texture_id_u64);
677 }
679
680 pub fn handle_texture_updates(
690 &mut self,
691 draw_data: &dear_imgui_rs::render::DrawData,
692 device: &Device,
693 queue: &Queue,
694 render_resources: &mut RenderResources,
695 ) {
696 for mut texture_data in draw_data.textures() {
697 let status = texture_data.status();
698 let current_tex_id = texture_data.tex_id().id();
699
700 match status {
701 TextureStatus::WantCreate => {
702 if current_tex_id != 0 {
708 render_resources.remove_image_bind_group(current_tex_id);
709 }
710
711 match self.create_texture_from_data(device, queue, &*texture_data) {
712 Ok(wgpu_texture_id) => {
713 let new_texture_id = dear_imgui_rs::TextureId::from(wgpu_texture_id);
718
719 texture_data.set_tex_id(new_texture_id);
720
721 texture_data.set_status(TextureStatus::OK);
723 }
724 Err(e) => {
725 println!(
726 "Failed to create texture for ID: {}, error: {}",
727 current_tex_id, e
728 );
729 }
730 }
731 }
732 TextureStatus::WantUpdates => {
733 let imgui_tex_id = texture_data.tex_id();
734 let internal_id = imgui_tex_id.id();
735
736 if internal_id == 0 || !self.contains_texture(internal_id) {
740 match self.create_texture_from_data(device, queue, &*texture_data) {
741 Ok(new_id) => {
742 texture_data.set_tex_id(dear_imgui_rs::TextureId::from(new_id));
743 texture_data.set_status(TextureStatus::OK);
744 }
745 Err(_e) => {
746 texture_data.set_status(TextureStatus::Destroyed);
748 }
749 }
750 } else {
751 render_resources.remove_image_bind_group(internal_id);
754
755 if self
757 .apply_subrect_updates(queue, &*texture_data, internal_id)
758 .unwrap_or(false)
759 {
760 texture_data.set_status(TextureStatus::OK);
761 } else if self
762 .update_texture_from_data_with_id(
763 device,
764 queue,
765 &*texture_data,
766 internal_id,
767 )
768 .is_err()
769 {
770 texture_data.set_status(TextureStatus::OK);
773 } else {
774 texture_data.set_status(TextureStatus::OK);
775 }
776 }
777 }
778 TextureStatus::WantDestroy => {
779 let mut can_destroy = true;
781 unsafe {
782 let raw = texture_data.as_raw();
783 if !raw.is_null() {
784 #[allow(unused_unsafe)]
786 {
787 can_destroy = (*raw).UnusedFrames > 0;
790 }
791 }
792 }
793 if can_destroy {
794 let imgui_tex_id = texture_data.tex_id();
795 let internal_id = imgui_tex_id.id();
796 self.remove_texture(internal_id);
798 self.clear_custom_sampler_for_texture(internal_id);
799 render_resources.remove_image_bind_group(internal_id);
800 texture_data.set_status(TextureStatus::Destroyed);
801 }
802 }
803 TextureStatus::OK | TextureStatus::Destroyed => {
804 }
806 }
807 }
808 }
809
810 pub fn update_single_texture(
835 &mut self,
836 texture_data: &dear_imgui_rs::TextureData,
837 device: &Device,
838 queue: &Queue,
839 ) -> RendererResult<TextureUpdateResult> {
840 match texture_data.status() {
841 TextureStatus::WantCreate => {
842 let texture_id = self.create_texture_from_data(device, queue, texture_data)?;
843 Ok(TextureUpdateResult::Created {
844 texture_id: TextureId::from(texture_id),
845 })
846 }
847 TextureStatus::WantUpdates => {
848 let internal_id = texture_data.tex_id().id();
849 if internal_id == 0 || !self.contains_texture(internal_id) {
850 let texture_id = self.create_texture_from_data(device, queue, texture_data)?;
852 Ok(TextureUpdateResult::Created {
853 texture_id: TextureId::from(texture_id),
854 })
855 } else {
856 match self.update_texture_from_data_with_id(
857 device,
858 queue,
859 texture_data,
860 internal_id,
861 ) {
862 Ok(_) => Ok(TextureUpdateResult::Updated),
863 Err(e) => Err(e),
864 }
865 }
866 }
867 TextureStatus::WantDestroy => {
868 self.destroy_texture(texture_data.tex_id());
869 Ok(TextureUpdateResult::Destroyed)
870 }
871 TextureStatus::OK | TextureStatus::Destroyed => Ok(TextureUpdateResult::NoAction),
872 }
873 }
874}
875
876#[cfg(test)]
877mod tests {
878 use super::*;
879 use dear_imgui_rs::texture::{TextureData, TextureFormat as ImFormat, TextureRect};
880
881 #[test]
882 fn texture_update_result_apply_to_sets_status_and_id() {
883 let mut tex = TextureData::new();
884
885 TextureUpdateResult::Created {
887 texture_id: TextureId::from(42u64),
888 }
889 .apply_to(&mut tex);
890 assert_eq!(tex.status(), TextureStatus::OK);
891 assert_eq!(tex.tex_id().id(), 42);
892
893 TextureUpdateResult::Updated.apply_to(&mut tex);
895 assert_eq!(tex.status(), TextureStatus::OK);
896 assert_eq!(tex.tex_id().id(), 42);
897
898 unsafe {
903 (*tex.as_raw_mut()).WantDestroyNextFrame = true;
904 }
905 TextureUpdateResult::Destroyed.apply_to(&mut tex);
906 assert_eq!(tex.status(), TextureStatus::Destroyed);
907
908 unsafe {
911 (*tex.as_raw_mut()).WantDestroyNextFrame = false;
912 }
913 tex.create(ImFormat::RGBA32, 1, 1);
914 TextureUpdateResult::Failed.apply_to(&mut tex);
915 assert_eq!(tex.status(), TextureStatus::WantCreate);
916
917 TextureUpdateResult::NoAction.apply_to(&mut tex);
919 assert_eq!(tex.status(), TextureStatus::WantCreate);
920 }
921
922 #[test]
923 fn convert_subrect_to_rgba_rgba32_full_rect() {
924 let mut tex = TextureData::new();
925 let width = 2;
926 let height = 2;
927 tex.create(ImFormat::RGBA32, width, height);
928
929 let pixels: [u8; 16] = [
931 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, ];
936 tex.set_data(&pixels);
937
938 let rect = TextureRect {
939 x: 0,
940 y: 0,
941 w: width as u16,
942 h: height as u16,
943 };
944
945 let out = WgpuTextureManager::convert_subrect_to_rgba(&tex, rect).expect("expected data");
946 assert_eq!(out, pixels);
947 }
948
949 #[test]
950 fn convert_subrect_to_rgba_alpha8_full_rect() {
951 let mut tex = TextureData::new();
952 let width = 2;
953 let height = 2;
954 tex.create(ImFormat::Alpha8, width, height);
955
956 let alphas: [u8; 4] = [0, 64, 128, 255];
958 tex.set_data(&alphas);
959
960 let rect = TextureRect {
961 x: 0,
962 y: 0,
963 w: width as u16,
964 h: height as u16,
965 };
966
967 let out = WgpuTextureManager::convert_subrect_to_rgba(&tex, rect).expect("expected data");
968 assert_eq!(
970 out,
971 vec![
972 255, 255, 255, 0, 255, 255, 255, 64, 255, 255, 255, 128, 255, 255, 255, 255, ]
977 );
978 }
979
980 #[test]
981 fn convert_subrect_to_rgba_out_of_bounds_returns_none() {
982 let mut tex = TextureData::new();
983 tex.create(ImFormat::RGBA32, 2, 2);
984 let rect = TextureRect {
985 x: 10,
986 y: 10,
987 w: 1,
988 h: 1,
989 };
990 assert!(WgpuTextureManager::convert_subrect_to_rgba(&tex, rect).is_none());
991 }
992}