1
1
Fork 0
mirror of https://github.com/griffi-gh/hUI.git synced 2025-03-31 21:16:28 -05:00

add texture tests, fix atlas copy

This commit is contained in:
griffi-gh 2025-03-11 12:27:11 +01:00
parent 5aaa7bb282
commit c57740b8dd
3 changed files with 183 additions and 8 deletions

View file

@ -26,6 +26,7 @@
"rust-analyzer"
"clippy"
])
cargo-nextest
pkg-config
cmake
];

View file

@ -12,11 +12,15 @@ pub struct FontManager {
}
impl FontManager {
pub fn new() -> Self {
let mut this = Self {
pub(crate) fn new_internal() -> Self {
Self {
fonts: font::FontHandleManager::new(),
ftm: ftm::FontTextureManager::new(),
};
}
}
pub fn new() -> Self {
let mut this = Self::new_internal();
#[cfg(feature="default-font")] {
this.fonts.idc = 0;
this.add(include_bytes!("../assets/font/ProggyTiny.ttf"));

View file

@ -162,11 +162,12 @@ pub struct TextureAtlas {
}
impl TextureAtlas {
/// Create a new texture atlas with the specified size.
pub(crate) fn new(size: UVec2) -> Self {
/// Internal function, only directly used in tests
pub(crate) fn new_internal(size: UVec2) -> Self {
assert_size(size);
let data_bytes = (size.x * size.y) as usize * RGBA_BYTES_PER_PIXEL;
let mut this = Self {
Self {
size,
data: vec![0; data_bytes],
packer: DensePacker::new(
@ -177,7 +178,12 @@ impl TextureAtlas {
allocations: HashMap::default(),
reuse_allocations: Vec::new(),
version: 0,
};
}
}
/// Create a new texture atlas with the specified size.
pub(crate) fn new(size: UVec2) -> Self {
let mut this = Self::new_internal(size);
// HACK?: ensure 0,0 is a white pixel
let h = this.allocate_with_data(SourceTextureFormat::A8, &[255], 1);
@ -320,11 +326,13 @@ impl TextureAtlas {
.get(&handle.id)
.expect("invalid texture handle");
debug_assert_eq!(*size, handle.size, "texture size mismatch");
for y in 0..size.y {
for x in 0..size.x {
let src_idx = (y * size.x + x) as usize * bpp;
let dst_idx: usize = (
(offset.y + y) * size.x +
(offset.y + y) * self.size.x +
(offset.x + x)
) as usize * RGBA_BYTES_PER_PIXEL;
@ -333,6 +341,7 @@ impl TextureAtlas {
match format {
SourceTextureFormat::RGBA8 => {
// TODO opt: copy entire row in this case
dst.copy_from_slice(src);
},
SourceTextureFormat::ARGB8 => {
@ -429,3 +438,164 @@ impl Default for TextureAtlas {
Self::new(DEFAULT_ATLAS_SIZE)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_assert_size_valid() {
assert_size(uvec2(1, 1));
assert_size(uvec2(i32::MAX as u32, i32::MAX as u32));
}
#[test]
#[should_panic(expected = "size must be greater than 0")]
fn test_assert_size_zero() {
assert_size(uvec2(0, 0));
}
#[test]
#[should_panic(expected = "size must be less than i32::MAX")]
fn test_assert_size_too_large() {
assert_size(uvec2(i32::MAX as u32 + 1, i32::MAX as u32 + 1));
}
#[test]
fn test_texture_handle_new_broken() {
let handle = TextureHandle::new_broken();
assert_eq!(handle.id, u32::MAX);
assert_eq!(handle.size, uvec2(0, 0));
}
#[test]
fn test_texture_allocation_new() {
let handle = TextureHandle::new_broken();
let allocation = TextureAllocation::new(handle, uvec2(1, 1), uvec2(2, 2));
assert_eq!(allocation.handle, handle);
assert_eq!(allocation.offset, uvec2(1, 1));
assert_eq!(allocation.size, uvec2(2, 2));
assert_eq!(allocation.max_size, uvec2(2, 2));
}
#[test]
fn test_texture_atlas_new() {
const SIZE: u32 = 128;
let atlas = TextureAtlas::new_internal(uvec2(SIZE, SIZE));
assert_eq!(atlas.size, uvec2(SIZE, SIZE));
assert_eq!(atlas.data.len(), (SIZE as usize) * (SIZE as usize) * RGBA_BYTES_PER_PIXEL);
assert_eq!(atlas.next_id, 0);
assert_eq!(atlas.allocations.len(), 0);
assert_eq!(atlas.reuse_allocations.len(), 0);
assert_eq!(atlas.version, 0);
}
#[test]
fn test_texture_atlas_allocate() {
let mut atlas = TextureAtlas::new_internal(uvec2(128, 128));
let handle = atlas.allocate(uvec2(32, 32));
assert_eq!(handle.size, uvec2(32, 32));
assert_eq!(atlas.get_uv(handle).unwrap().bottom_right, vec2(32. / 128., 32. / 128.));
assert_eq!(atlas.allocations.len(), 1);
}
#[test]
fn test_texture_atlas_allocate_with_data() {
fn make_data(o: u8)-> Vec<u8> {
let mut data = vec![o; 32 * 32 * 4];
for y in 0..32 {
for x in 0..32 {
let idx = (y * 32 + x) * 4;
data[idx] = x as u8;
data[idx + 1] = y as u8;
}
}
data
}
let mut atlas = TextureAtlas::new_internal(uvec2(128, 128));
let data = make_data(1);
let handle = atlas.allocate_with_data(SourceTextureFormat::RGBA8, &data, 32);
assert_eq!(handle.size, uvec2(32, 32));
assert_eq!(atlas.allocations.len(), 1);
let uv = atlas.get_uv(handle).unwrap();
assert_eq!(uv.top_left, vec2(0.0, 0.0));
assert_eq!(uv.top_right, vec2(32.0 / 128.0, 0.0));
assert_eq!(uv.bottom_left, vec2(0.0, 32.0 / 128.0));
assert_eq!(uv.bottom_right, vec2(32.0 / 128.0, 32.0 / 128.0));
let data = make_data(2);
let handle = atlas.allocate_with_data(SourceTextureFormat::RGBA8, &data, 32);
assert_eq!(handle.size, uvec2(32, 32));
assert_eq!(atlas.allocations.len(), 2);
let uv = atlas.get_uv(handle).unwrap();
assert_eq!(uv.top_left, vec2(32.0 / 128.0, 0.0));
assert_eq!(uv.top_right, vec2(64.0 / 128.0, 0.0));
assert_eq!(uv.bottom_left, vec2(32.0 / 128.0, 32.0 / 128.0));
assert_eq!(uv.bottom_right, vec2(64.0 / 128.0, 32.0 / 128.0));
// now, check the texture data
assert_eq!(atlas.version(), 2);
let data = atlas.data_rgba();
// for y in 0..128 {
// for x in 0..128 {
// let idx = (y * 128 + x) * 4;
// print!("{}", data[idx + 2]);
// }
// println!();
// }
for y in 0..128 {
for x in 0..128 {
let idx = (y * 128 + x) * 4;
if y >= 32 || x >= 64 {
continue
}
assert_eq!(
if x < 32 {
[x as u8, y as u8, 1, 1]
} else if x < 64 {
[x as u8 - 32, y as u8, 2, 2]
} else {
unreachable!()
},
data[idx..idx + 4],
"pixel at ({x}, {y}) idx: {idx} is incorrect",
);
}
}
}
// #[test]
// fn test_texture_atlas_update() {
// let mut atlas = TextureAtlas::new(uvec2(128, 128));
// let data = vec![255; 32 * 32 * 4];
// let handle = atlas.allocate_with_data(SourceTextureFormat::RGBA8, &data, 32);
// let new_data = vec![0; 32 * 32 * 4];
// atlas.update(handle, SourceTextureFormat::RGBA8, &new_data);
// assert_eq!(atlas.data_rgba()[..32 * 32 * 4], new_data[..]);
// }
#[test]
fn test_texture_atlas_deallocate() {
let mut atlas = TextureAtlas::new_internal(uvec2(128, 128));
let handle = atlas.allocate(uvec2(32, 32));
atlas.deallocate(handle);
assert_eq!(atlas.allocations.len(), 0);
assert_eq!(atlas.reuse_allocations.len(), 1);
}
#[test]
fn test_texture_atlas_get_uv() {
let mut atlas = TextureAtlas::new_internal(uvec2(128, 128));
let handle = atlas.allocate(uvec2(32, 32));
let uv = atlas.get_uv(handle).unwrap();
assert_eq!(uv.top_left, vec2(0.0, 0.0));
assert_eq!(uv.top_right, vec2(32.0 / 128.0, 0.0));
assert_eq!(uv.bottom_left, vec2(0.0, 32.0 / 128.0));
assert_eq!(uv.bottom_right, vec2(32.0 / 128.0, 32.0 / 128.0));
}
}