mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-21 22:58:42 -06:00
wip refactor
This commit is contained in:
parent
f61aa6734f
commit
ae26a4d933
|
@ -6,10 +6,13 @@ use crate::{
|
|||
IfModified
|
||||
};
|
||||
|
||||
mod atlas;
|
||||
mod corner_radius;
|
||||
pub(crate) mod atlas;
|
||||
//pub(crate) use atlas::{TextureAllocation, TextureAtlasManager};
|
||||
//pub use atlas::TextureHandle;
|
||||
|
||||
mod corner_radius;
|
||||
pub use corner_radius::RoundedCorners;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
||||
use glam::{Vec2, Vec4, vec2};
|
||||
|
@ -296,6 +299,7 @@ impl UiDrawCall {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl IfModified<UiDrawCall> for (bool, &UiDrawCall) {
|
||||
fn if_modified(&self) -> Option<&UiDrawCall> {
|
||||
match self.0 {
|
||||
|
|
|
@ -3,7 +3,11 @@ use hashbrown::HashMap;
|
|||
use nohash_hasher::BuildNoHashHasher;
|
||||
use rect_packer::DensePacker;
|
||||
|
||||
use crate::IfModified;
|
||||
|
||||
const CHANNEL_COUNT: u32 = 4;
|
||||
//TODO: make this work
|
||||
const ALLOW_ROTATION: bool = false;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TextureHandle {
|
||||
|
@ -27,15 +31,20 @@ pub(crate) struct TextureAllocation {
|
|||
pub rotated: bool,
|
||||
}
|
||||
|
||||
/// Manages a texture atlas and the allocation of space within it\
|
||||
/// The atlas is alllowed to grow and resize dynamically, as needed
|
||||
pub(crate) struct TextureAtlasManager {
|
||||
packer: DensePacker,
|
||||
count: u32,
|
||||
size: UVec2,
|
||||
data: Vec<u8>,
|
||||
allocations: HashMap<u32, TextureAllocation, BuildNoHashHasher<u32>>,
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
impl TextureAtlasManager {
|
||||
/// Create a new texture atlas with the specified size\
|
||||
/// 512x512 is a good default size for most applications, and the texture atlas can grow dynamically as needed
|
||||
pub fn new(size: UVec2) -> Self {
|
||||
Self {
|
||||
packer: DensePacker::new(size.x as i32, size.y as i32),
|
||||
|
@ -43,9 +52,11 @@ impl TextureAtlasManager {
|
|||
size: UVec2::new(0, 0),
|
||||
data: vec![0; (size.x * size.y * CHANNEL_COUNT) as usize],
|
||||
allocations: HashMap::default(),
|
||||
modified: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resize the texture atlas to the new size in-place, preserving the existing data
|
||||
pub fn resize(&mut self, new_size: UVec2) {
|
||||
if new_size.x > self.size.x && new_size.y > self.size.y{
|
||||
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
||||
|
@ -65,13 +76,15 @@ impl TextureAtlasManager {
|
|||
todo!("Atlas downscaling is not implemented yet");
|
||||
}
|
||||
self.size = new_size;
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Allocate a new texture region in the atlas and return a handle to it\
|
||||
/// Returns None if the texture could not be allocated due to lack of space\
|
||||
/// Use `allocate_resize` to allocate a texture and resize the atlas if necessary
|
||||
fn allocate(&mut self, size: UVec2) -> Option<TextureHandle> {
|
||||
let result = self.packer.pack(size.x as i32, size.y as i32, true)?;
|
||||
/// Use `allocate` to allocate a texture and resize the atlas if necessary\
|
||||
/// Does not modify the texture data
|
||||
fn try_allocate(&mut self, size: UVec2) -> Option<TextureHandle> {
|
||||
let result = self.packer.pack(size.x as i32, size.y as i32, ALLOW_ROTATION)?;
|
||||
let index = self.count;
|
||||
self.count += 1;
|
||||
let allocation = TextureAllocation {
|
||||
|
@ -79,28 +92,30 @@ impl TextureAtlasManager {
|
|||
position: UVec2::new(result.x as u32, result.y as u32),
|
||||
size,
|
||||
//If the size does not match the requested size, the texture was rotated
|
||||
rotated: result.width != size.x as i32,
|
||||
rotated: ALLOW_ROTATION && (result.width != size.x as i32),
|
||||
};
|
||||
self.allocations.insert_unique_unchecked(index, allocation);
|
||||
Some(TextureHandle { index })
|
||||
}
|
||||
|
||||
/// Allocate a new texture region in the atlas and resize the atlas if necessary\
|
||||
/// This function should never fail under normal circumstances.
|
||||
fn allocate_resize(&mut self, size: UVec2) -> TextureHandle {
|
||||
/// This function should never fail under normal circumstances.\
|
||||
/// May modify the texture data if the atlas is resized
|
||||
pub fn allocate(&mut self, size: UVec2) -> TextureHandle {
|
||||
let mut new_size = self.size;
|
||||
while !self.packer.can_pack(size.x as i32, size.y as i32, true) {
|
||||
new_size *= 2;
|
||||
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
||||
}
|
||||
self.resize(new_size);
|
||||
self.allocate(size).unwrap()
|
||||
self.try_allocate(size).unwrap()
|
||||
}
|
||||
|
||||
/// Allocate a new texture region in the atlas and copy the data into it
|
||||
/// Allocate a new texture region in the atlas and copy the data into it\
|
||||
/// This function may resize the atlas as needed, and should never fail under normal circumstances.
|
||||
pub fn add(&mut self, width: usize, data: &[u8]) -> TextureHandle {
|
||||
let size = uvec2(width as u32, (data.len() / (width * CHANNEL_COUNT as usize)) as u32);
|
||||
let handle = self.allocate_resize(size);
|
||||
let handle = self.allocate(size);
|
||||
let allocation = self.allocations.get_mut(&handle.index).unwrap();
|
||||
assert!(!allocation.rotated, "Rotated textures are not implemented yet");
|
||||
for y in 0..size.y {
|
||||
|
@ -130,10 +145,33 @@ impl TextureAtlasManager {
|
|||
pub fn get(&self, handle: TextureHandle) -> Option<&TextureAllocation> {
|
||||
self.allocations.get(&handle.index)
|
||||
}
|
||||
|
||||
/// Reset the `is_modified` flag
|
||||
pub fn reset_modified(&mut self) {
|
||||
self.modified = false;
|
||||
}
|
||||
|
||||
/// Returns true if the atlas has been modified since the last call to `reset_modified`\
|
||||
/// If this function returns true, the texture atlas should be re-uploaded to the GPU\
|
||||
/// This function is mostly useful for developers of graphics backends
|
||||
pub fn is_modified(&self) -> bool {
|
||||
self.modified
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextureAtlasManager {
|
||||
/// Create a new texture atlas with a default size of 512x512
|
||||
fn default() -> Self {
|
||||
Self::new(UVec2::new(512, 512))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a> IfModified<TextureAtlasManager> for TextureAtlasManager {
|
||||
fn if_modified(&self) -> Option<&Self> {
|
||||
match self.modified {
|
||||
true => Some(self),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,26 +23,8 @@ pub mod text;
|
|||
|
||||
pub use instance::UiInstance;
|
||||
|
||||
#[deprecated(since = "0.1.0-alpha.4", note = "will be removed in the next release")]
|
||||
pub trait IfModified<T> {
|
||||
#[deprecated(since = "0.1.0-alpha.4", note = "will be removed in the next release")]
|
||||
fn if_modified(&self) -> Option<&T>;
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(since = "0.1.0-alpha.3", note = "will be removed in the next release")]
|
||||
pub struct ElementList(Vec<Box<dyn element::UiElement>>);
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(since = "0.1.0-alpha.3", note = "will be removed in the next release")]
|
||||
impl ElementList {
|
||||
pub fn add(&mut self, element: impl element::UiElement + 'static) {
|
||||
self.0.push(Box::new(element));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(since = "0.1.0-alpha.3", note = "will be removed in the next release")]
|
||||
pub fn elements(f: impl FnOnce(&mut ElementList)) -> Vec<Box<dyn element::UiElement>> {
|
||||
let mut elements = ElementList(Vec::new());
|
||||
f(&mut elements);
|
||||
elements.0
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ use fontdue::{Font, FontSettings};
|
|||
use ftm::FontTextureManager;
|
||||
pub use ftm::{FontTextureInfo, GlyphCacheEntry};
|
||||
|
||||
use crate::draw::atlas::TextureAtlasManager;
|
||||
|
||||
pub struct TextRenderer {
|
||||
fm: FontManager,
|
||||
ftm: FontTextureManager,
|
||||
|
@ -29,16 +31,8 @@ impl TextRenderer {
|
|||
self.fm.add_font(Font::from_bytes(font, FontSettings::default()).unwrap())
|
||||
}
|
||||
|
||||
pub fn reset_frame(&mut self) {
|
||||
self.ftm.reset_modified();
|
||||
}
|
||||
|
||||
pub fn font_texture(&self) -> FontTextureInfo {
|
||||
self.ftm.info()
|
||||
}
|
||||
|
||||
pub fn glyph(&mut self, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||
self.ftm.glyph(&self.fm, font_handle, character, size)
|
||||
pub fn glyph(&mut self, atlas: &mut TextureAtlasManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||
self.ftm.glyph(&self.fm, atlas, font_handle, character, size)
|
||||
}
|
||||
|
||||
pub(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::sync::Arc;
|
||||
use fontdue::Metrics;
|
||||
use glam::{IVec2, UVec2, uvec2, ivec2};
|
||||
use glam::UVec2;
|
||||
use hashbrown::HashMap;
|
||||
use rect_packer::DensePacker;
|
||||
|
||||
use crate::IfModified;
|
||||
use crate::{
|
||||
draw::atlas::{TextureAtlasManager, TextureHandle},
|
||||
IfModified
|
||||
};
|
||||
|
||||
use super::font::{FontHandle, FontManager};
|
||||
|
||||
|
@ -16,70 +17,30 @@ struct GlyphCacheKey {
|
|||
}
|
||||
|
||||
pub struct GlyphCacheEntry {
|
||||
pub data: Vec<u8>,
|
||||
pub metrics: Metrics,
|
||||
pub position: IVec2,
|
||||
pub size: UVec2,
|
||||
pub texture: TextureHandle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FontTextureInfo<'a> {
|
||||
pub modified: bool,
|
||||
pub data: &'a [u8],
|
||||
pub size: UVec2,
|
||||
}
|
||||
|
||||
impl<'a> IfModified<FontTextureInfo<'a>> for FontTextureInfo<'a> {
|
||||
fn if_modified(&self) -> Option<&Self> {
|
||||
match self.modified {
|
||||
true => Some(self),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'a> FontTextureInfo<'a> {
|
||||
// fn if_modified(&self) -> Option<Self> {
|
||||
// match self.modified {
|
||||
// true => Some(*self),
|
||||
// false => None,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct FontTextureManager {
|
||||
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>,
|
||||
packer: DensePacker,
|
||||
font_texture: Vec<u8>,
|
||||
font_texture_size: UVec2,
|
||||
modified: bool,
|
||||
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>
|
||||
}
|
||||
|
||||
impl FontTextureManager {
|
||||
pub fn new(size: UVec2) -> Self {
|
||||
pub fn new() -> Self {
|
||||
FontTextureManager {
|
||||
glyph_cache: HashMap::new(),
|
||||
packer: DensePacker::new(size.x as i32, size.y as i32),
|
||||
font_texture: vec![0; (size.x * size.y * 4) as usize],
|
||||
font_texture_size: size,
|
||||
modified: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_modified(&mut self) {
|
||||
self.modified = false;
|
||||
}
|
||||
|
||||
pub fn info(&self) -> FontTextureInfo {
|
||||
FontTextureInfo {
|
||||
modified: self.modified,
|
||||
data: &self.font_texture,
|
||||
size: self.font_texture_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Either looks up the glyph in the cache or renders it and adds it to the cache.
|
||||
pub fn glyph(&mut self, font_manager: &FontManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||
pub fn glyph(
|
||||
&mut self,
|
||||
atlas: &mut TextureAtlasManager,
|
||||
font_manager: &FontManager,
|
||||
font_handle: FontHandle,
|
||||
character: char,
|
||||
size: u8
|
||||
) -> Arc<GlyphCacheEntry> {
|
||||
let key = GlyphCacheKey {
|
||||
font_index: font_handle.0,
|
||||
character,
|
||||
|
@ -91,37 +52,15 @@ impl FontTextureManager {
|
|||
let font = font_manager.get(font_handle).unwrap();
|
||||
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
||||
log::debug!("rasterized glyph: {}, {:?}, {:?}", character, metrics, bitmap);
|
||||
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 texture = atlas.add(metrics.width, &bitmap);
|
||||
let entry = Arc::new(GlyphCacheEntry {
|
||||
data: bitmap,
|
||||
metrics,
|
||||
position: ivec2(texture_position.x, texture_position.y),
|
||||
size: texture_size,
|
||||
texture
|
||||
});
|
||||
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
|
||||
self.glyph_place(&entry);
|
||||
self.modified = 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, data, .. } = entry;
|
||||
//println!("{size:?} {position:?}");
|
||||
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 * 4;
|
||||
self.font_texture[dst..=(dst + 3)].copy_from_slice(&[255, 255, 255, data[src]]);
|
||||
//print!("{} ", if data[src] > 0 {'#'} else {'.'});
|
||||
//print!("{src} {dst} / ");
|
||||
}
|
||||
//println!();
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -133,7 +72,5 @@ impl FontTextureManager {
|
|||
}
|
||||
|
||||
impl Default for FontTextureManager {
|
||||
fn default() -> Self {
|
||||
Self::new(uvec2(1024, 1024))
|
||||
}
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue