1#![allow(dead_code)]
2use std::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct MipLevel {
14 pub level: u32,
16 pub width: u32,
18 pub height: u32,
20 pub offset: usize,
22 pub size: usize,
24}
25
26impl MipLevel {
27 pub fn pixel_count(&self) -> u64 {
29 u64::from(self.width) * u64::from(self.height)
30 }
31}
32
33impl fmt::Display for MipLevel {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(
36 f,
37 "Mip[{}]: {}x{} (offset={}, size={})",
38 self.level, self.width, self.height, self.offset, self.size
39 )
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct MipChain {
46 pub base_width: u32,
48 pub base_height: u32,
50 pub channels: u32,
52 pub levels: Vec<MipLevel>,
54}
55
56impl MipChain {
57 pub fn compute(
61 base_width: u32,
62 base_height: u32,
63 channels: u32,
64 max_levels: Option<u32>,
65 ) -> Self {
66 let full_count = compute_mip_count(base_width, base_height);
67 let level_count = max_levels.map_or(full_count, |m| m.min(full_count));
68
69 let mut levels = Vec::with_capacity(level_count as usize);
70 let mut w = base_width;
71 let mut h = base_height;
72 let mut offset = 0usize;
73
74 for i in 0..level_count {
75 let size = (w as usize) * (h as usize) * (channels as usize);
76 levels.push(MipLevel {
77 level: i,
78 width: w,
79 height: h,
80 offset,
81 size,
82 });
83 offset += size;
84 w = (w / 2).max(1);
85 h = (h / 2).max(1);
86 }
87
88 Self {
89 base_width,
90 base_height,
91 channels,
92 levels,
93 }
94 }
95
96 pub fn level_count(&self) -> u32 {
98 self.levels.len() as u32
99 }
100
101 pub fn total_size(&self) -> usize {
103 self.levels.iter().map(|l| l.size).sum()
104 }
105
106 pub fn level(&self, index: u32) -> Option<&MipLevel> {
108 self.levels.get(index as usize)
109 }
110
111 pub fn is_single_level(&self) -> bool {
113 self.levels.len() <= 1
114 }
115}
116
117#[allow(clippy::cast_precision_loss)]
121pub fn compute_mip_count(width: u32, height: u32) -> u32 {
122 if width == 0 || height == 0 {
123 return 0;
124 }
125 let max_dim = width.max(height);
126 (max_dim as f64).log2().floor() as u32 + 1
127}
128
129pub fn mip_dimension(base: u32, level: u32) -> u32 {
131 (base >> level).max(1)
132}
133
134pub fn downsample_box_u8(src: &[u8], src_width: u32, src_height: u32, channels: u32) -> Vec<u8> {
138 let dst_width = (src_width / 2).max(1);
139 let dst_height = (src_height / 2).max(1);
140 let ch = channels as usize;
141 let mut dst = vec![0u8; dst_width as usize * dst_height as usize * ch];
142
143 let sw = src_width as usize;
144
145 for dy in 0..dst_height as usize {
146 for dx in 0..dst_width as usize {
147 let sx = dx * 2;
148 let sy = dy * 2;
149
150 let sx1 = (sx + 1).min(src_width as usize - 1);
152 let sy1 = (sy + 1).min(src_height as usize - 1);
153
154 for c in 0..ch {
155 let s00 = u16::from(src[(sy * sw + sx) * ch + c]);
156 let s10 = u16::from(src[(sy * sw + sx1) * ch + c]);
157 let s01 = u16::from(src[(sy1 * sw + sx) * ch + c]);
158 let s11 = u16::from(src[(sy1 * sw + sx1) * ch + c]);
159 let avg = ((s00 + s10 + s01 + s11 + 2) / 4) as u8;
160 dst[(dy * dst_width as usize + dx) * ch + c] = avg;
161 }
162 }
163 }
164
165 dst
166}
167
168pub fn generate_mip_chain_u8(
173 base_data: &[u8],
174 width: u32,
175 height: u32,
176 channels: u32,
177) -> (Vec<u8>, MipChain) {
178 let chain = MipChain::compute(width, height, channels, None);
179 let mut output = Vec::with_capacity(chain.total_size());
180
181 output.extend_from_slice(base_data);
183
184 let mut prev_data = base_data.to_vec();
185 let mut prev_w = width;
186 let mut prev_h = height;
187
188 for level in chain.levels.iter().skip(1) {
189 let down = downsample_box_u8(&prev_data, prev_w, prev_h, channels);
190 output.extend_from_slice(&down);
191 prev_w = level.width;
192 prev_h = level.height;
193 prev_data = down;
194 }
195
196 (output, chain)
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_compute_mip_count_square() {
205 assert_eq!(compute_mip_count(256, 256), 9); }
207
208 #[test]
209 fn test_compute_mip_count_nonsquare() {
210 assert_eq!(compute_mip_count(1920, 1080), 11); }
212
213 #[test]
214 fn test_compute_mip_count_one() {
215 assert_eq!(compute_mip_count(1, 1), 1);
216 }
217
218 #[test]
219 fn test_compute_mip_count_zero() {
220 assert_eq!(compute_mip_count(0, 100), 0);
221 assert_eq!(compute_mip_count(100, 0), 0);
222 }
223
224 #[test]
225 fn test_mip_dimension() {
226 assert_eq!(mip_dimension(256, 0), 256);
227 assert_eq!(mip_dimension(256, 1), 128);
228 assert_eq!(mip_dimension(256, 8), 1);
229 assert_eq!(mip_dimension(256, 20), 1); }
231
232 #[test]
233 fn test_mip_chain_compute() {
234 let chain = MipChain::compute(64, 64, 4, None);
235 assert_eq!(chain.level_count(), 7); assert_eq!(chain.levels[0].width, 64);
237 assert_eq!(chain.levels[0].height, 64);
238 assert_eq!(chain.levels[1].width, 32);
239 assert_eq!(chain.levels[6].width, 1);
240 }
241
242 #[test]
243 fn test_mip_chain_max_levels() {
244 let chain = MipChain::compute(256, 256, 4, Some(3));
245 assert_eq!(chain.level_count(), 3);
246 assert_eq!(chain.levels[2].width, 64);
247 }
248
249 #[test]
250 fn test_mip_chain_total_size() {
251 let chain = MipChain::compute(4, 4, 1, None);
252 assert_eq!(chain.total_size(), 21);
254 }
255
256 #[test]
257 fn test_mip_chain_offsets() {
258 let chain = MipChain::compute(8, 8, 1, None);
259 assert_eq!(chain.levels[0].offset, 0);
260 assert_eq!(chain.levels[1].offset, 64); assert_eq!(chain.levels[2].offset, 80); }
263
264 #[test]
265 fn test_mip_level_display() {
266 let level = MipLevel {
267 level: 2,
268 width: 64,
269 height: 32,
270 offset: 1000,
271 size: 2048,
272 };
273 assert_eq!(format!("{level}"), "Mip[2]: 64x32 (offset=1000, size=2048)");
274 }
275
276 #[test]
277 fn test_downsample_box_u8_simple() {
278 let src = vec![200u8; 16];
280 let dst = downsample_box_u8(&src, 4, 4, 1);
281 assert_eq!(dst.len(), 4); for &v in &dst {
283 assert_eq!(v, 200);
284 }
285 }
286
287 #[test]
288 fn test_downsample_box_u8_gradient() {
289 let src = vec![0u8, 100, 200, 56];
291 let dst = downsample_box_u8(&src, 2, 2, 1);
292 assert_eq!(dst.len(), 1);
293 assert_eq!(dst[0], 89);
295 }
296
297 #[test]
298 fn test_generate_mip_chain_u8() {
299 let base = vec![128u8; 4 * 4]; let (data, chain) = generate_mip_chain_u8(&base, 4, 4, 1);
301 assert_eq!(chain.level_count(), 3);
302 assert_eq!(data.len(), chain.total_size());
303 assert_eq!(&data[0..16], &base[..]);
305 }
306
307 #[test]
308 fn test_mip_chain_is_single_level() {
309 let chain = MipChain::compute(1, 1, 4, None);
310 assert!(chain.is_single_level());
311 let chain2 = MipChain::compute(4, 4, 4, None);
312 assert!(!chain2.is_single_level());
313 }
314
315 #[test]
316 fn test_mip_level_pixel_count() {
317 let level = MipLevel {
318 level: 0,
319 width: 100,
320 height: 50,
321 offset: 0,
322 size: 0,
323 };
324 assert_eq!(level.pixel_count(), 5000);
325 }
326}