use astrelis_core::math::Vec2;
use astrelis_core::profiling::profile_function;
use astrelis_render::Color;
use astrelis_text::{AtlasEntry, FontRenderer, ShapedGlyph};
use crate::gpu_types::TextInstance;
pub fn glyphs_to_instances(
font_renderer: &mut FontRenderer,
glyphs: &[ShapedGlyph],
base_position: Vec2,
color: Color,
z_depth: f32,
) -> Vec<TextInstance> {
let mut instances = Vec::with_capacity(glyphs.len());
glyphs_to_instances_into(
font_renderer,
glyphs,
base_position,
color,
z_depth,
&mut instances,
);
instances
}
pub fn glyphs_to_instances_into(
font_renderer: &mut FontRenderer,
glyphs: &[ShapedGlyph],
base_position: Vec2,
color: Color,
z_depth: f32,
out: &mut Vec<TextInstance>,
) {
profile_function!();
let atlas_size = font_renderer.atlas_size() as f32;
for glyph in glyphs {
if let Some((atlas_entry, placement)) =
font_renderer.ensure_glyph_with_placement(glyph.cache_key)
{
let screen_pos =
base_position + glyph.position + Vec2::new(placement.left, -placement.top);
let atlas_uv_min = [
atlas_entry.x as f32 / atlas_size,
atlas_entry.y as f32 / atlas_size,
];
let atlas_uv_max = [
(atlas_entry.x + atlas_entry.width) as f32 / atlas_size,
(atlas_entry.y + atlas_entry.height) as f32 / atlas_size,
];
let size = Vec2::new(placement.width, placement.height);
out.push(TextInstance::new(
screen_pos,
size,
atlas_uv_min,
atlas_uv_max,
color,
z_depth,
));
}
}
}
pub fn glyph_to_instance(
font_renderer: &mut FontRenderer,
glyph: &ShapedGlyph,
base_position: Vec2,
color: Color,
z_depth: f32,
) -> Option<TextInstance> {
let atlas_size = font_renderer.atlas_size() as f32;
let (atlas_entry, placement) = font_renderer.ensure_glyph_with_placement(glyph.cache_key)?;
let screen_pos = base_position + glyph.position + Vec2::new(placement.left, -placement.top);
let atlas_uv_min = [
atlas_entry.x as f32 / atlas_size,
atlas_entry.y as f32 / atlas_size,
];
let atlas_uv_max = [
(atlas_entry.x + atlas_entry.width) as f32 / atlas_size,
(atlas_entry.y + atlas_entry.height) as f32 / atlas_size,
];
let size = Vec2::new(placement.width, placement.height);
Some(TextInstance::new(
screen_pos,
size,
atlas_uv_min,
atlas_uv_max,
color,
z_depth,
))
}
pub struct GlyphBatch {
pub atlas_page: u32,
pub start_index: u32,
pub count: u32,
}
impl GlyphBatch {
pub fn new(atlas_page: u32, start_index: u32, count: u32) -> Self {
Self {
atlas_page,
start_index,
count,
}
}
}
pub fn create_glyph_batches(instance_count: usize) -> Vec<GlyphBatch> {
if instance_count == 0 {
return Vec::new();
}
vec![GlyphBatch::new(0, 0, instance_count as u32)]
}
pub fn atlas_entry_uv_coords(entry: &AtlasEntry, atlas_size: u32) -> ([f32; 2], [f32; 2]) {
let atlas_size_f = atlas_size as f32;
let uv_min = [entry.x as f32 / atlas_size_f, entry.y as f32 / atlas_size_f];
let uv_max = [
(entry.x + entry.width) as f32 / atlas_size_f,
(entry.y + entry.height) as f32 / atlas_size_f,
];
(uv_min, uv_max)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atlas_entry_uv_coords() {
let entry = AtlasEntry {
x: 100,
y: 200,
width: 50,
height: 60,
};
let atlas_size = 1024;
let (uv_min, uv_max) = atlas_entry_uv_coords(&entry, atlas_size);
assert_eq!(uv_min[0], 100.0 / 1024.0);
assert_eq!(uv_min[1], 200.0 / 1024.0);
assert_eq!(uv_max[0], 150.0 / 1024.0);
assert_eq!(uv_max[1], 260.0 / 1024.0);
}
#[test]
fn test_create_glyph_batches_empty() {
let batches = create_glyph_batches(0);
assert!(batches.is_empty());
}
#[test]
fn test_create_glyph_batches_single() {
let batches = create_glyph_batches(10);
assert_eq!(batches.len(), 1);
assert_eq!(batches[0].atlas_page, 0);
assert_eq!(batches[0].start_index, 0);
assert_eq!(batches[0].count, 10);
}
#[test]
fn test_glyph_batch_creation() {
let batch = GlyphBatch::new(2, 100, 50);
assert_eq!(batch.atlas_page, 2);
assert_eq!(batch.start_index, 100);
assert_eq!(batch.count, 50);
}
}