update ftm stuff

This commit is contained in:
griffi-gh 2023-11-30 21:15:00 +01:00
parent 769d7e84e9
commit 1c52273ce2
5 changed files with 140 additions and 96 deletions

View file

@ -1,6 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use glam::{Vec2, Vec4, vec2}; use glam::{Vec2, Vec4, vec2};
use crate::text::TextRenderer;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum UiDrawCommand { pub enum UiDrawCommand {
@ -62,7 +61,7 @@ pub struct UiDrawPlan {
} }
impl UiDrawPlan { impl UiDrawPlan {
pub fn build(calls: &UiDrawCommands, tr: &mut TextRenderer) -> Self { pub fn build(calls: &UiDrawCommands) -> Self {
let mut call = UiDrawCall::default(); let mut call = UiDrawCall::default();
for command in &calls.commands { for command in &calls.commands {
match command { match command {

View file

@ -13,7 +13,6 @@ use element::UiElement;
use state::StateRepo; use state::StateRepo;
use event::UiEvent; use event::UiEvent;
use draw::{UiDrawCommands, UiDrawPlan}; use draw::{UiDrawCommands, UiDrawPlan};
use text::TextRenderer;
// pub struct ElementContext<'a> { // pub struct ElementContext<'a> {
// pub state: &'a mut StateRepo, // pub state: &'a mut StateRepo,
@ -29,7 +28,7 @@ pub struct KubiUi {
draw_commands: UiDrawCommands, draw_commands: UiDrawCommands,
draw_plan: UiDrawPlan, draw_plan: UiDrawPlan,
draw_plan_modified: bool, draw_plan_modified: bool,
font_renderer: TextRenderer, // ftm: FontTextureManager,
} }
impl KubiUi { impl KubiUi {
@ -43,7 +42,7 @@ impl KubiUi {
draw_commands: UiDrawCommands::default(), draw_commands: UiDrawCommands::default(),
draw_plan: UiDrawPlan::default(), draw_plan: UiDrawPlan::default(),
draw_plan_modified: false, draw_plan_modified: false,
font_renderer: TextRenderer::default(), // ftm: FontTextureManager::default(),
} }
} }
@ -67,7 +66,7 @@ impl KubiUi {
if self.draw_commands.commands == self.prev_draw_commands.commands { if self.draw_commands.commands == self.prev_draw_commands.commands {
return return
} }
self.draw_plan = UiDrawPlan::build(&self.draw_commands, &mut self.font_renderer); self.draw_plan = UiDrawPlan::build(&self.draw_commands);
self.draw_plan_modified = true; self.draw_plan_modified = true;
} }

View file

@ -1,90 +1,2 @@
use std::sync::Arc; pub mod font;
use fontdue::{Font, Metrics}; pub mod ftm;
use glam::{IVec2, UVec2, uvec2, ivec2};
use hashbrown::HashMap;
use rect_packer::DensePacker;
#[cfg(feature = "builtin_font")]
const BIN_FONT: &[u8] = include_bytes!("../assets/font/ProggyTiny.ttf");
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct FontHandle(pub(crate) usize);
#[cfg(feature = "builtin_font")]
pub const BUILTIN_FONT: FontHandle = FontHandle(0);
#[derive(PartialEq, Eq, Hash)]
struct GlyphCacheKey {
font_index: usize,
character: char,
size: u8,
}
struct GlyphCacheEntry {
pub data: Vec<u8>,
pub metrics: Metrics,
pub texture_position: IVec2,
pub texture_size: UVec2,
}
pub struct TextRenderer {
fonts: Vec<Font>,
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>,
packer: DensePacker,
font_texture: Vec<u8>,
font_texture_size: UVec2,
}
impl TextRenderer {
pub fn new(size: UVec2) -> Self {
let mut renderer = TextRenderer {
fonts: Vec::new(),
glyph_cache: HashMap::new(),
packer: DensePacker::new(size.x as i32, size.y as i32),
font_texture: vec![0; (size.x * size.y) as usize],
font_texture_size: size,
};
#[cfg(feature = "builtin_font")]
{
let font = Font::from_bytes(BIN_FONT, fontdue::FontSettings::default()).unwrap();
renderer.add_font(font);
}
renderer
}
/// Add a (fontdue) font to the renderer.
pub fn add_font(&mut self, font: Font) -> FontHandle {
self.fonts.push(font);
FontHandle(self.fonts.len() - 1)
}
/// Either looks up the glyph in the cache or renders it and adds it to the cache.
pub fn glyph(&mut self, font: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
let key = GlyphCacheKey {
font_index: font.0,
character,
size,
};
if let Some(entry) = self.glyph_cache.get(&key) {
return Arc::clone(entry);
}
let font = &self.fonts[key.font_index];
let (metrics, bitmap) = font.rasterize(character, size as f32);
let texture_position = self.packer.pack(metrics.width as i32, metrics.height as i32, false).unwrap();
let texture_size = uvec2(metrics.width as u32, metrics.height as u32);
let entry = Arc::new(GlyphCacheEntry {
data: bitmap,
metrics,
texture_position: ivec2(texture_position.x, texture_position.y),
texture_size,
});
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
entry
}
}
impl Default for TextRenderer {
fn default() -> Self {
Self::new(uvec2(2048, 2048))
}
}

38
kubi-ui/src/text/font.rs Normal file
View file

@ -0,0 +1,38 @@
use fontdue::Font;
#[cfg(feature = "builtin_font")]
const BIN_FONT: &[u8] = include_bytes!("../../assets/font/ProggyTiny.ttf");
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct FontHandle(pub(crate) usize);
#[cfg(feature = "builtin_font")]
pub const BUILTIN_FONT: FontHandle = FontHandle(0);
pub struct FontManager {
fonts: Vec<Font>,
}
impl FontManager {
pub fn new() -> Self {
let mut this = Self {
fonts: Vec::new(),
};
#[cfg(feature = "builtin_font")]
{
let font = Font::from_bytes(BIN_FONT, fontdue::FontSettings::default()).unwrap();
this.add_font(font);
};
this
}
/// Add a (fontdue) font to the renderer.
pub fn add_font(&mut self, font: Font) -> FontHandle {
self.fonts.push(font);
FontHandle(self.fonts.len() - 1)
}
pub fn get(&self, handle: FontHandle) -> Option<&Font> {
self.fonts.get(handle.0)
}
}

96
kubi-ui/src/text/ftm.rs Normal file
View file

@ -0,0 +1,96 @@
use std::sync::Arc;
use fontdue::{Font, Metrics};
use glam::{IVec2, UVec2, uvec2, ivec2};
use hashbrown::HashMap;
use rect_packer::DensePacker;
use super::font::{FontHandle, FontManager};
#[derive(PartialEq, Eq, Hash)]
struct GlyphCacheKey {
font_index: usize,
character: char,
size: u8,
}
struct GlyphCacheEntry {
pub data: Vec<u8>,
pub metrics: Metrics,
pub position: IVec2,
pub size: UVec2,
}
pub struct FontTextureManager {
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>,
packer: DensePacker,
font_texture: Vec<u8>,
font_texture_size: UVec2,
modified: bool,
}
impl FontTextureManager {
pub fn new(size: UVec2) -> Self {
let mut renderer = FontTextureManager {
glyph_cache: HashMap::new(),
packer: DensePacker::new(size.x as i32, size.y as i32),
font_texture: vec![0; (size.x * size.y) as usize],
font_texture_size: size,
modified: false,
};
renderer
}
/// Either looks up the glyph in the cache or renders it and adds it to the cache.
fn glyph_allocate(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> (bool, Arc<GlyphCacheEntry>) {
let key = GlyphCacheKey {
font_index: font_handle.0,
character,
size,
};
if let Some(entry) = self.glyph_cache.get(&key) {
return (false, Arc::clone(entry));
}
let font = font_manager.get(font_handle).unwrap();
let (metrics, bitmap) = font.rasterize(character, size as f32);
let texture_position = self.packer.pack(metrics.width as i32, metrics.height as i32, false).unwrap();
let texture_size = uvec2(metrics.width as u32, metrics.height as u32);
let entry = Arc::new(GlyphCacheEntry {
data: bitmap,
metrics,
position: ivec2(texture_position.x, texture_position.y),
size: texture_size,
});
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
(true, entry)
}
/// Place glyph onto the font texture.
fn glyph_place(&mut self, entry: &GlyphCacheEntry) {
let tex_size = self.font_texture_size;
let GlyphCacheEntry { size, position, .. } = entry;
for y in 0..size.y {
for x in 0..size.x {
let src = (size.x * y + x) as usize;
let dst = (tex_size.x * (y + position.y as u32) + (x + position.x as u32)) as usize;
self.font_texture[dst] = entry.data[src];
}
}
}
pub fn glyph(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
let (is_new, glyph) = self.glyph_allocate(font_manager, font_handle, character, size);
if is_new {
self.glyph_place(&glyph);
self.modified = true;
}
glyph
}
}
impl Default for FontTextureManager {
fn default() -> Self {
Self::new(uvec2(2048, 2048))
}
}