use std::collections::HashMap;
use crate::atlas::{MsdfAtlas, SdfAtlas, UvRect};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpuAtlasFormat {
R8Unorm,
Rgb8Unorm,
}
#[derive(Debug, Clone, Copy)]
pub struct NormalizedUvRect {
pub u_min: f32,
pub v_min: f32,
pub u_max: f32,
pub v_max: f32,
}
impl From<&UvRect> for NormalizedUvRect {
fn from(r: &UvRect) -> Self {
Self {
u_min: r.u_min,
v_min: r.v_min,
u_max: r.u_max,
v_max: r.v_max,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct AtlasGlyphMetrics {
pub bearing_x: f32,
pub bearing_y: f32,
pub advance_x: f32,
pub width_px: u32,
pub height_px: u32,
}
#[derive(Debug, Clone)]
pub struct GpuAtlasDescriptor {
pub width: u32,
pub height: u32,
pub format: GpuAtlasFormat,
pub data: Vec<u8>,
pub uv_map: HashMap<u16, NormalizedUvRect>,
pub glyph_metrics: HashMap<u16, AtlasGlyphMetrics>,
}
impl SdfAtlas {
pub fn to_gpu_descriptor(&self) -> GpuAtlasDescriptor {
let uv_map: HashMap<u16, NormalizedUvRect> = self
.uv_map
.iter()
.map(|(&glyph_id, uv)| (glyph_id, NormalizedUvRect::from(uv)))
.collect();
GpuAtlasDescriptor {
width: self.width,
height: self.height,
format: GpuAtlasFormat::R8Unorm,
data: self.texture.clone(),
uv_map,
glyph_metrics: HashMap::new(),
}
}
}
impl MsdfAtlas {
pub fn to_gpu_descriptor(&self) -> GpuAtlasDescriptor {
let uv_map: HashMap<u16, NormalizedUvRect> = self
.uv_map
.iter()
.map(|(&glyph_id, uv)| (glyph_id, NormalizedUvRect::from(uv)))
.collect();
GpuAtlasDescriptor {
width: self.width,
height: self.height,
format: GpuAtlasFormat::Rgb8Unorm,
data: self.texture.clone(),
uv_map,
glyph_metrics: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::atlas::{SdfAtlas, SdfTile};
fn make_tile(glyph_id: u16, w: u32, h: u32) -> SdfTile {
SdfTile {
glyph_id,
width: w,
height: h,
data: vec![128u8; (w * h) as usize],
bearing_x: 0,
bearing_y: 0,
advance_x: w as f32,
}
}
#[test]
fn test_gpu_descriptor_uv_normalized() {
let atlas = SdfAtlas::new(256, 256);
let desc = atlas.to_gpu_descriptor();
assert_eq!(desc.width, 256);
assert_eq!(desc.height, 256);
assert_eq!(desc.format, GpuAtlasFormat::R8Unorm);
assert_eq!(desc.data.len(), 256 * 256);
assert!(desc.uv_map.is_empty());
}
#[test]
fn test_gpu_descriptor_uv_values_in_range() {
let tiles = [make_tile(0, 16, 16), make_tile(1, 32, 32)];
let atlas = SdfAtlas::pack(&tiles);
let desc = atlas.to_gpu_descriptor();
for uv in desc.uv_map.values() {
assert!(
uv.u_min >= 0.0 && uv.u_min <= 1.0,
"u_min out of range: {}",
uv.u_min
);
assert!(
uv.u_max >= 0.0 && uv.u_max <= 1.0,
"u_max out of range: {}",
uv.u_max
);
assert!(
uv.v_min >= 0.0 && uv.v_min <= 1.0,
"v_min out of range: {}",
uv.v_min
);
assert!(
uv.v_max >= 0.0 && uv.v_max <= 1.0,
"v_max out of range: {}",
uv.v_max
);
assert!(uv.u_min <= uv.u_max, "u_min > u_max");
assert!(uv.v_min <= uv.v_max, "v_min > v_max");
}
}
#[test]
fn test_msdf_gpu_descriptor_format() {
let atlas = MsdfAtlas {
width: 128,
height: 128,
texture: vec![0u8; 128 * 128 * 3],
uv_map: HashMap::new(),
};
let desc = atlas.to_gpu_descriptor();
assert_eq!(desc.format, GpuAtlasFormat::Rgb8Unorm);
assert_eq!(desc.width, 128);
assert_eq!(desc.height, 128);
assert_eq!(desc.data.len(), 128 * 128 * 3);
assert!(desc.uv_map.is_empty());
}
#[test]
fn test_normalized_uv_rect_from_uv_rect() {
let uv = UvRect {
u_min: 0.1,
v_min: 0.2,
u_max: 0.5,
v_max: 0.8,
};
let norm = NormalizedUvRect::from(&uv);
assert!((norm.u_min - 0.1).abs() < f32::EPSILON);
assert!((norm.v_min - 0.2).abs() < f32::EPSILON);
assert!((norm.u_max - 0.5).abs() < f32::EPSILON);
assert!((norm.v_max - 0.8).abs() < f32::EPSILON);
}
}