use crate::render::render_graph::full_mip_levels;
use alloc::vec;
use alloc::vec::Vec;
pub struct MipLevel {
pub width: u32,
pub height: u32,
pub pixels: Vec<u8>,
}
pub fn generate_mip_chain(width: u32, height: u32, rgba8: &[u8]) -> Vec<MipLevel> {
let count = full_mip_levels(width, height);
let base_len = width as usize * height as usize * 4;
let mut levels: Vec<MipLevel> = Vec::with_capacity(count as usize);
levels.push(MipLevel {
width,
height,
pixels: rgba8[..base_len].to_vec(),
});
for _ in 1..count {
let prev = levels.last().expect("level 0 was pushed before the loop");
let dw = (prev.width / 2).max(1);
let dh = (prev.height / 2).max(1);
let mut pixels = vec![0u8; dw as usize * dh as usize * 4];
downsample_box(prev, dw, dh, &mut pixels);
levels.push(MipLevel {
width: dw,
height: dh,
pixels,
});
}
levels
}
fn downsample_box(src: &MipLevel, dw: u32, dh: u32, dst: &mut [u8]) {
let sw = src.width as usize;
let sh = src.height as usize;
for y in 0..dh as usize {
let sy0 = (2 * y).min(sh - 1);
let sy1 = (2 * y + 1).min(sh - 1);
for x in 0..dw as usize {
let sx0 = (2 * x).min(sw - 1);
let sx1 = (2 * x + 1).min(sw - 1);
let i00 = (sy0 * sw + sx0) * 4;
let i01 = (sy0 * sw + sx1) * 4;
let i10 = (sy1 * sw + sx0) * 4;
let i11 = (sy1 * sw + sx1) * 4;
let d = (y * dw as usize + x) * 4;
for c in 0..4 {
let sum = src.pixels[i00 + c] as u32
+ src.pixels[i01 + c] as u32
+ src.pixels[i10 + c] as u32
+ src.pixels[i11 + c] as u32;
dst[d + c] = ((sum + 2) / 4) as u8;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn level_count_matches_floor_log2_plus_one() {
assert_eq!(full_mip_levels(1, 1), 1);
assert_eq!(full_mip_levels(2, 2), 2);
assert_eq!(full_mip_levels(256, 256), 9);
assert_eq!(full_mip_levels(512, 512), 10);
assert_eq!(full_mip_levels(640, 384), 10); assert_eq!(full_mip_levels(1, 8), 4); }
#[test]
fn chain_dimensions_halve_to_one() {
let px = vec![0u8; 4 * 4 * 4];
let chain = generate_mip_chain(4, 4, &px);
let dims: Vec<(u32, u32)> = chain.iter().map(|m| (m.width, m.height)).collect();
assert_eq!(dims, vec![(4, 4), (2, 2), (1, 1)]);
assert_eq!(chain.len() as u32, full_mip_levels(4, 4));
}
#[test]
fn non_square_chain_floors_each_axis_independently() {
let px = vec![0u8; 4 * 2 * 4];
let chain = generate_mip_chain(4, 2, &px);
let dims: Vec<(u32, u32)> = chain.iter().map(|m| (m.width, m.height)).collect();
assert_eq!(dims, vec![(4, 2), (2, 1), (1, 1)]);
}
#[test]
fn two_by_two_averages_to_single_texel() {
let px = vec![
0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, ];
let chain = generate_mip_chain(2, 2, &px);
assert_eq!(chain.len(), 2);
let mip1 = &chain[1];
assert_eq!((mip1.width, mip1.height), (1, 1));
assert_eq!(mip1.pixels, vec![6, 6, 6, 6]);
}
#[test]
fn rounds_to_nearest() {
let dark = vec![0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255];
let c = generate_mip_chain(2, 2, &dark);
assert_eq!(&c[1].pixels[0..3], &[0, 0, 0]);
let bright = vec![0, 0, 0, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255];
let c = generate_mip_chain(2, 2, &bright);
assert_eq!(&c[1].pixels[0..3], &[1, 1, 1]);
}
}