Skip to main content

gcrecomp_runtime/texture/
loader.rs

1// Texture loading
2use 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        // Check cache first
26        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        // Decode texture
32        let image = format.decode(data, width, height)?;
33
34        // Cache it
35        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, // Compressed, variable
73            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}