mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-21 06:38:42 -06:00
nya
This commit is contained in:
parent
70f7641214
commit
881861baef
|
@ -2,12 +2,13 @@ pub mod paint;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
|
use text::FontManager;
|
||||||
use texture::TextureAtlas;
|
use texture::TextureAtlas;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Painter {
|
pub struct Painter {
|
||||||
pub(crate) atlas: TextureAtlas,
|
pub atlas: TextureAtlas,
|
||||||
// ftm: FontTextureManager,
|
pub fonts: FontManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Painter {
|
impl Painter {
|
||||||
|
@ -15,7 +16,19 @@ impl Painter {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn atlas(&self) -> &TextureAtlas {
|
// pub fn atlas(&self) -> &TextureAtlas {
|
||||||
&self.atlas
|
// &self.atlas
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// pub fn atlas_mut(&mut self) -> &mut TextureAtlas {
|
||||||
|
// &mut self.atlas
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn fonts(&self) -> &FontManager {
|
||||||
|
// &self.fonts
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn fonts_mut(&mut self) -> &mut FontManager {
|
||||||
|
// &mut self.fonts
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
use glam::Vec2;
|
||||||
use crate::{paint::buffer::PaintBuffer, Painter};
|
use crate::{paint::buffer::PaintBuffer, Painter};
|
||||||
|
|
||||||
|
// mod root;
|
||||||
|
// pub use root::RootCommand;
|
||||||
|
|
||||||
mod transform;
|
mod transform;
|
||||||
pub use transform::PaintTransform;
|
pub use transform::PaintTransform;
|
||||||
|
|
||||||
|
@ -10,5 +14,20 @@ mod text;
|
||||||
pub use text::PaintText;
|
pub use text::PaintText;
|
||||||
|
|
||||||
pub trait PaintCommand {
|
pub trait PaintCommand {
|
||||||
|
/// Called before actual paint command is executed\
|
||||||
|
/// Opportunity to pre-cache bitmaps, etc.
|
||||||
|
///
|
||||||
|
/// Make sure to propagate this call to children!
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn pre_paint(&self, ctx: &mut Painter) {}
|
||||||
|
|
||||||
|
/// Paint the command into the buffer
|
||||||
|
///
|
||||||
|
/// Do not allocate new textures or cache glyphs here, use `pre_paint` instead!\
|
||||||
|
/// (Doing this WILL lead to atlas corruption flicker for a single frame if it's forced to resize!)
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer);
|
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Measurable: PaintCommand {
|
||||||
|
fn size(&self, ctx: &Painter) -> Vec2;
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
use fontdue::layout::{CoordinateSystem, Layout};
|
use fontdue::layout::{self, CoordinateSystem, GlyphRasterConfig, Layout};
|
||||||
|
use glam::{vec2, Vec2};
|
||||||
use crate::{
|
use crate::{
|
||||||
Painter,
|
|
||||||
paint::{
|
paint::{
|
||||||
buffer::PaintBuffer,
|
buffer::PaintBuffer,
|
||||||
command::PaintCommand,
|
command::PaintCommand,
|
||||||
},
|
}, text::FontHandle, Painter
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::Measurable;
|
||||||
|
|
||||||
|
// TODO align, multichunk etc
|
||||||
|
|
||||||
pub struct TextChunk {
|
pub struct TextChunk {
|
||||||
pub text: Cow<'static, str>,
|
pub text: Cow<'static, str>,
|
||||||
pub font: (),
|
pub font: FontHandle,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,25 +24,74 @@ pub struct PaintText {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintText {
|
impl PaintText {
|
||||||
pub fn new(text: impl Into<Cow<'static, str>>, size: f32) -> Self {
|
pub fn new(text: impl Into<Cow<'static, str>>, font: FontHandle, size: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
text: TextChunk {
|
text: TextChunk {
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
font: todo!(),
|
font,
|
||||||
size,
|
size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_font_array<'a>(&self, ctx: &'a Painter) -> Vec<&'a fontdue::Font> {
|
||||||
|
let font = ctx.fonts.get_fontdue_font(self.text.font)
|
||||||
|
.expect("FontHandle is invalid");
|
||||||
|
vec![&font]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_layout(&self, font_array: &[&fontdue::Font]) -> Layout {
|
||||||
|
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||||
|
layout.append(
|
||||||
|
&font_array,
|
||||||
|
&fontdue::layout::TextStyle::new(
|
||||||
|
&self.text.text,
|
||||||
|
self.text.size,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
layout
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintCommand for PaintText {
|
impl PaintCommand for PaintText {
|
||||||
|
fn pre_paint(&self, ctx: &mut Painter) {
|
||||||
|
let font_array = self.build_font_array(ctx);
|
||||||
|
let layout = self.build_layout(&font_array);
|
||||||
|
|
||||||
|
for glyph in layout.glyphs() {
|
||||||
|
ctx.fonts.render_glyph(&mut ctx.atlas, self.text.font, glyph.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
||||||
// let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
// let font_array = self.build_font_array(ctx);
|
||||||
// layout.append(
|
// let layout = self.build_layout(&font_array);
|
||||||
// &[text_renderer.internal_font(*font_handle)],
|
|
||||||
// &TextStyle::new(text, *size as f32, 0)
|
// for glyph in layout.glyphs() {
|
||||||
// );
|
// let config = GlyphRasterConfig {
|
||||||
|
// glyph_index: glyph.font_index
|
||||||
|
// };
|
||||||
|
// let glyph_raster = ctx.fonts().render_glyph(atlas, font, config);
|
||||||
|
// }
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Measurable for PaintText {
|
||||||
|
fn size(&self, ctx: &Painter) -> Vec2 {
|
||||||
|
let font_array = self.build_font_array(ctx);
|
||||||
|
let layout = self.build_layout(&font_array);
|
||||||
|
|
||||||
|
let width = layout.lines().map(|lines| {
|
||||||
|
lines.iter().fold(0.0_f32, |acc, x| {
|
||||||
|
let glyph = layout.glyphs().get(x.glyph_end).unwrap();
|
||||||
|
acc.max(glyph.x + glyph.width as f32)
|
||||||
|
})
|
||||||
|
}).unwrap_or(0.);
|
||||||
|
let height = layout.height();
|
||||||
|
|
||||||
|
vec2(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,12 @@ pub struct PaintTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintCommand for PaintTransform {
|
impl PaintCommand for PaintTransform {
|
||||||
|
fn pre_paint(&self, ctx: &mut Painter) {
|
||||||
|
for child in &self.children {
|
||||||
|
child.pre_paint(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
||||||
// remember the starting index
|
// remember the starting index
|
||||||
let starting_index = into.vertices.len();
|
let starting_index = into.vertices.len();
|
||||||
|
|
|
@ -1,2 +1,69 @@
|
||||||
pub mod ftm;
|
use fontdue::layout::GlyphRasterConfig;
|
||||||
pub mod font;
|
use crate::texture::{TextureAtlas, TextureHandle};
|
||||||
|
|
||||||
|
pub(crate) mod ftm;
|
||||||
|
pub(crate) mod font;
|
||||||
|
|
||||||
|
pub use font::FontHandle;
|
||||||
|
|
||||||
|
pub struct FontManager {
|
||||||
|
fonts: font::FontHandleManager,
|
||||||
|
ftm: ftm::FontTextureManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
fonts: font::FontHandleManager::new(),
|
||||||
|
ftm: ftm::FontTextureManager::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a font to the manager.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the font data is invalid.
|
||||||
|
pub fn add_font(&mut self, data: &[u8]) -> FontHandle {
|
||||||
|
let font = self.fonts.add_font(data);
|
||||||
|
self.ftm.init_font(font);
|
||||||
|
font
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove and deallocate a font from the manager.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the font handle is invalid.
|
||||||
|
pub fn remove_font(&mut self, font: FontHandle, atlas: &mut TextureAtlas) {
|
||||||
|
self.ftm.drop_font(font, atlas);
|
||||||
|
self.fonts.remove_font(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a glyph and cache it in the texture atlas.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the font handle is invalid or not initialized.
|
||||||
|
/// - Fuck around and find out, this api is unstable
|
||||||
|
pub(crate) fn render_glyph(
|
||||||
|
&mut self,
|
||||||
|
atlas: &mut TextureAtlas,
|
||||||
|
font: FontHandle,
|
||||||
|
config: GlyphRasterConfig
|
||||||
|
) -> TextureHandle {
|
||||||
|
self.ftm.render_glyph(font, &self.fonts, config, atlas)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal API
|
||||||
|
pub(crate) fn get_fontdue_font(
|
||||||
|
&self,
|
||||||
|
handle: FontHandle
|
||||||
|
) -> Option<&fontdue::Font> {
|
||||||
|
self.fonts.get_font_repr(handle)
|
||||||
|
.map(|x| &x.font)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FontManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
|
pub(crate) type FontId = u16;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct FontHandle(pub(crate) FontId);
|
||||||
|
|
||||||
|
pub(crate) struct FontRepr {
|
||||||
|
pub(crate) font: fontdue::Font,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FontHandleManager {
|
||||||
|
idc: FontId,
|
||||||
|
fonts: HashMap<FontId, FontRepr,BuildNoHashHasher<FontId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontHandleManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
idc: 0,
|
||||||
|
fonts: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a font to the manager.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the font data is invalid.
|
||||||
|
pub fn add_font(&mut self, data: &[u8]) -> FontHandle {
|
||||||
|
let font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default()).unwrap();
|
||||||
|
self.fonts.insert_unique_unchecked(self.idc, FontRepr { font });
|
||||||
|
self.idc += 1;
|
||||||
|
FontHandle(self.idc - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal function
|
||||||
|
///
|
||||||
|
/// Remove and deallocate a font from the manager if the font handle is valid.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the font handle is invalid.
|
||||||
|
pub(crate) fn remove_font(&mut self, handle: FontHandle) {
|
||||||
|
self.fonts.remove(&handle.0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the font handle for the specified font.
|
||||||
|
pub(crate) fn get_font_repr(&self, handle: FontHandle) -> Option<&FontRepr> {
|
||||||
|
self.fonts.get(&handle.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
use fontdue::layout::GlyphRasterConfig;
|
use fontdue::layout::GlyphRasterConfig;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use nohash_hasher::BuildNoHashHasher;
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
use crate::texture::{TextureAtlas, TextureHandle};
|
use crate::texture::{SourceTextureFormat, TextureAtlas, TextureHandle};
|
||||||
|
use super::font::{FontHandle, FontHandleManager, FontId};
|
||||||
type FontId = u16;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct FontHandle(FontId);
|
|
||||||
|
|
||||||
/// Maps to the actual texture handle.
|
/// Maps to the actual texture handle.
|
||||||
struct GlyphCacheItem {
|
struct RasterizedGlyphInternal {
|
||||||
handle: TextureHandle,
|
handle: TextureHandle,
|
||||||
|
metrics: fontdue::Metrics,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map from raster config to glyph cache item.
|
/// Map from raster config to glyph cache item.
|
||||||
///
|
///
|
||||||
/// Partitioned by font id in FtM :3
|
/// Partitioned by font id in FtM :3
|
||||||
type PartitionKey = HashMap<GlyphRasterConfig, GlyphCacheItem>;
|
type PartitionKey = HashMap<GlyphRasterConfig, RasterizedGlyphInternal>;
|
||||||
|
|
||||||
/// Manages glyph cache items in a texture atlas.
|
/// Manages glyph cache items in a texture atlas.
|
||||||
pub struct FontTextureManager {
|
pub struct FontTextureManager {
|
||||||
|
@ -24,17 +21,74 @@ pub struct FontTextureManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontTextureManager {
|
impl FontTextureManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
partition: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Drop cached data for the specified font.
|
/// Drop cached data for the specified font.
|
||||||
///
|
///
|
||||||
/// Panics:
|
/// Panics:
|
||||||
/// - If the font handle is invalid.
|
/// - If the font handle is invalid or not initialized.
|
||||||
/// - If any of the cached items are not found in the texture atlas or became invalid.\
|
/// - If any of the cached items are not found in the texture atlas or became invalid.\
|
||||||
/// This may happen if, for example, a different atlas is passed than the one used to allocate the items.
|
/// This may happen if, for example, a different atlas is passed than the one used to allocate the items.
|
||||||
fn drop_font(&mut self, font: FontHandle, atlas: &mut TextureAtlas) {
|
pub(crate) fn drop_font(&mut self, font: FontHandle, atlas: &mut TextureAtlas) {
|
||||||
let dump = self.partition.remove(&font.0).expect("Font handle is invalid");
|
let dump = self.partition.remove(&font.0).expect("Font handle is invalid");
|
||||||
for (_, item) in dump {
|
for (_, item) in dump {
|
||||||
atlas.deallocate(item.handle);
|
atlas.deallocate(item.handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize the partition for the specified font.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the partition for the font already exists.
|
||||||
|
pub(crate) fn init_font(&mut self, font: FontHandle) {
|
||||||
|
assert!(!self.partition.contains_key(&font.0), "Font handle already initialized");
|
||||||
|
self.partition.insert_unique_unchecked(font.0, HashMap::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a glyph and cache it in the texture atlas.
|
||||||
|
///
|
||||||
|
/// Panics:
|
||||||
|
/// - If the font handle is invalid or not initialized.
|
||||||
|
/// - Fuck around and find out, this api is unstable
|
||||||
|
pub(crate) fn render_glyph(
|
||||||
|
&mut self,
|
||||||
|
font_handle: FontHandle,
|
||||||
|
fhm_internal: &FontHandleManager,
|
||||||
|
config: GlyphRasterConfig,
|
||||||
|
atlas: &mut TextureAtlas
|
||||||
|
) -> TextureHandle {
|
||||||
|
// Get partiton
|
||||||
|
let partition = self.partition.get_mut(&font_handle.0)
|
||||||
|
.expect("Font handle is not registered in FtM");
|
||||||
|
|
||||||
|
// Check if glyph is alr cached
|
||||||
|
if let Some(item) = partition.get(&config) {
|
||||||
|
return item.handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get fontdue font from the manager
|
||||||
|
let font = &fhm_internal.get_font_repr(font_handle)
|
||||||
|
.expect("Font handle is invalid")
|
||||||
|
.font;
|
||||||
|
|
||||||
|
// Rasterize the font and copy the texture data
|
||||||
|
let (metrics, data) = font.rasterize_config(config);
|
||||||
|
let handle = atlas.allocate_with_data(SourceTextureFormat::A8, &data, metrics.width);
|
||||||
|
|
||||||
|
// Create a texture item struct and insert it into the partition
|
||||||
|
let itm = RasterizedGlyphInternal { handle, metrics };
|
||||||
|
partition.insert_unique_unchecked(config, itm);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for FontTextureManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue