mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-26 05:38:20 -06:00
update ftm stuff
This commit is contained in:
parent
d955445dbd
commit
f783917945
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
38
kubi-ui/src/text/font.rs
Normal 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
96
kubi-ui/src/text/ftm.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue