gcrecomp_runtime/texture/
formats.rs1use anyhow::Result;
3use image::{DynamicImage, RgbaImage};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum GameCubeTextureFormat {
7 CMPR, I4, I8, IA4, IA8, RGB565, RGB5A3, RGBA8, }
16
17impl GameCubeTextureFormat {
18 pub fn from_gx_format(format: u8) -> Option<Self> {
19 match format {
20 0x00 => Some(Self::I4),
21 0x01 => Some(Self::I8),
22 0x02 => Some(Self::IA4),
23 0x03 => Some(Self::IA8),
24 0x04 => Some(Self::RGB565),
25 0x05 => Some(Self::RGB5A3),
26 0x06 => Some(Self::RGBA8),
27 0x08 => Some(Self::CMPR),
28 _ => None,
29 }
30 }
31
32 pub fn decode(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
33 match self {
34 Self::CMPR => Self::decode_cmpr(data, width, height),
35 Self::I4 => Self::decode_i4(data, width, height),
36 Self::I8 => Self::decode_i8(data, width, height),
37 Self::IA4 => Self::decode_ia4(data, width, height),
38 Self::IA8 => Self::decode_ia8(data, width, height),
39 Self::RGB565 => Self::decode_rgb565(data, width, height),
40 Self::RGB5A3 => Self::decode_rgb5a3(data, width, height),
41 Self::RGBA8 => Self::decode_rgba8(data, width, height),
42 }
43 }
44
45 fn decode_cmpr(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
46 let mut image = RgbaImage::new(width, height);
49 Ok(image)
51 }
52
53 fn decode_i4(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
54 let mut image = RgbaImage::new(width, height);
55 let pixels_per_byte = 2;
56 let mut data_idx = 0;
57
58 for y in 0..height {
59 for x in 0..width {
60 let byte_idx = (y * width + x) / pixels_per_byte;
61 if (byte_idx as usize) < data.len() {
62 let byte = data[byte_idx as usize];
63 let pixel_idx = (x % pixels_per_byte) as usize;
64 let intensity = if pixel_idx == 0 {
65 ((byte >> 4) & 0xF) * 17
66 } else {
67 (byte & 0xF) * 17
68 };
69
70 image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, 255]));
71 }
72 data_idx += 1;
73 }
74 }
75
76 Ok(image)
77 }
78
79 fn decode_i8(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
80 let mut image = RgbaImage::new(width, height);
81 let mut data_idx = 0;
82
83 for y in 0..height {
84 for x in 0..width {
85 if data_idx < data.len() {
86 let intensity = data[data_idx];
87 image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, 255]));
88 data_idx += 1;
89 }
90 }
91 }
92
93 Ok(image)
94 }
95
96 fn decode_ia4(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
97 let mut image = RgbaImage::new(width, height);
98 let pixels_per_byte = 2;
99
100 for y in 0..height {
101 for x in 0..width {
102 let byte_idx = ((y * width + x) / pixels_per_byte) as usize;
103 if byte_idx < data.len() {
104 let byte = data[byte_idx];
105 let pixel_idx = (x % pixels_per_byte) as usize;
106 let (intensity, alpha) = if pixel_idx == 0 {
107 (((byte >> 4) & 0xF) * 17, ((byte >> 7) & 0x1) * 255)
108 } else {
109 ((byte & 0xF) * 17, ((byte >> 3) & 0x1) * 255)
110 };
111
112 image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, alpha]));
113 }
114 }
115 }
116
117 Ok(image)
118 }
119
120 fn decode_ia8(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
121 let mut image = RgbaImage::new(width, height);
122 let mut data_idx = 0;
123
124 for y in 0..height {
125 for x in 0..width {
126 if data_idx + 1 < data.len() {
127 let intensity = data[data_idx];
128 let alpha = data[data_idx + 1];
129 image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, alpha]));
130 data_idx += 2;
131 }
132 }
133 }
134
135 Ok(image)
136 }
137
138 fn decode_rgb565(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
139 let mut image = RgbaImage::new(width, height);
140 let mut data_idx = 0;
141
142 for y in 0..height {
143 for x in 0..width {
144 if data_idx + 1 < data.len() {
145 let word = u16::from_be_bytes([data[data_idx], data[data_idx + 1]]);
146 let r = ((word >> 11) & 0x1F) as u8 * 8;
147 let g = ((word >> 5) & 0x3F) as u8 * 4;
148 let b = (word & 0x1F) as u8 * 8;
149 image.put_pixel(x, y, image::Rgba([r, g, b, 255]));
150 data_idx += 2;
151 }
152 }
153 }
154
155 Ok(image)
156 }
157
158 fn decode_rgb5a3(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
159 let mut image = RgbaImage::new(width, height);
160 let mut data_idx = 0;
161
162 for y in 0..height {
163 for x in 0..width {
164 if data_idx + 1 < data.len() {
165 let word = u16::from_be_bytes([data[data_idx], data[data_idx + 1]]);
166 if (word & 0x8000) != 0 {
167 let r = ((word >> 10) & 0x1F) as u8 * 8;
169 let g = ((word >> 5) & 0x1F) as u8 * 8;
170 let b = (word & 0x1F) as u8 * 8;
171 image.put_pixel(x, y, image::Rgba([r, g, b, 255]));
172 } else {
173 let a = ((word >> 12) & 0x7) as u8 * 32;
175 let r = ((word >> 8) & 0xF) as u8 * 16;
176 let g = ((word >> 4) & 0xF) as u8 * 16;
177 let b = (word & 0xF) as u8 * 16;
178 image.put_pixel(x, y, image::Rgba([r, g, b, a]));
179 }
180 data_idx += 2;
181 }
182 }
183 }
184
185 Ok(image)
186 }
187
188 fn decode_rgba8(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
189 let mut image = RgbaImage::new(width, height);
190 let mut data_idx = 0;
191
192 for y in 0..height {
193 for x in 0..width {
194 if data_idx + 3 < data.len() {
195 let r = data[data_idx];
196 let g = data[data_idx + 1];
197 let b = data[data_idx + 2];
198 let a = data[data_idx + 3];
199 image.put_pixel(x, y, image::Rgba([r, g, b, a]));
200 data_idx += 4;
201 }
202 }
203 }
204
205 Ok(image)
206 }
207}