gcrecomp_runtime/texture/
loader.rs1use crate::texture::cache::TextureCache;
3use crate::texture::formats::GameCubeTextureFormat;
4use anyhow::Result;
5use image::RgbaImage;
6
7pub struct TextureLoader {
8 cache: TextureCache,
9}
10
11impl TextureLoader {
12 pub fn new() -> Self {
13 Self {
14 cache: TextureCache::new(),
15 }
16 }
17
18 pub fn load_texture(
19 &mut self,
20 data: &[u8],
21 format: GameCubeTextureFormat,
22 width: u32,
23 height: u32,
24 ) -> Result<RgbaImage> {
25 let cache_key = format!("{:?}_{}_{}", format, width, height);
27 if let Some(cached) = self.cache.get(&cache_key) {
28 return Ok(cached.clone());
29 }
30
31 let image = format.decode(data, width, height)?;
33
34 self.cache.insert(cache_key, image.clone());
36
37 Ok(image)
38 }
39
40 pub fn load_texture_with_mipmaps(
41 &mut self,
42 data: &[u8],
43 format: GameCubeTextureFormat,
44 width: u32,
45 height: u32,
46 mip_count: u32,
47 ) -> Result<Vec<RgbaImage>> {
48 let mut mipmaps = Vec::new();
49 let mut offset = 0;
50 let mut current_width = width;
51 let mut current_height = height;
52
53 for _ in 0..mip_count {
54 let size = (current_width * current_height * format.bytes_per_pixel()) as usize;
55 if offset + size <= data.len() {
56 let mip_data = &data[offset..offset + size];
57 let mip = format.decode(mip_data, current_width, current_height)?;
58 mipmaps.push(mip);
59 offset += size;
60 current_width = (current_width / 2).max(1);
61 current_height = (current_height / 2).max(1);
62 }
63 }
64
65 Ok(mipmaps)
66 }
67}
68
69impl GameCubeTextureFormat {
70 pub fn bytes_per_pixel(&self) -> u32 {
71 match self {
72 Self::CMPR => 0, Self::I4 => 1,
74 Self::I8 => 1,
75 Self::IA4 => 1,
76 Self::IA8 => 2,
77 Self::RGB565 => 2,
78 Self::RGB5A3 => 2,
79 Self::RGBA8 => 4,
80 }
81 }
82}