mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-25 08:28:42 -06:00
wip refactor
This commit is contained in:
parent
f61aa6734f
commit
ae26a4d933
|
@ -6,10 +6,13 @@ use crate::{
|
||||||
IfModified
|
IfModified
|
||||||
};
|
};
|
||||||
|
|
||||||
mod atlas;
|
pub(crate) mod atlas;
|
||||||
mod corner_radius;
|
//pub(crate) use atlas::{TextureAllocation, TextureAtlasManager};
|
||||||
|
//pub use atlas::TextureHandle;
|
||||||
|
|
||||||
|
mod corner_radius;
|
||||||
pub use corner_radius::RoundedCorners;
|
pub use corner_radius::RoundedCorners;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
||||||
use glam::{Vec2, Vec4, vec2};
|
use glam::{Vec2, Vec4, vec2};
|
||||||
|
@ -296,6 +299,7 @@ impl UiDrawCall {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl IfModified<UiDrawCall> for (bool, &UiDrawCall) {
|
impl IfModified<UiDrawCall> for (bool, &UiDrawCall) {
|
||||||
fn if_modified(&self) -> Option<&UiDrawCall> {
|
fn if_modified(&self) -> Option<&UiDrawCall> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
|
|
@ -3,7 +3,11 @@ use hashbrown::HashMap;
|
||||||
use nohash_hasher::BuildNoHashHasher;
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
use rect_packer::DensePacker;
|
use rect_packer::DensePacker;
|
||||||
|
|
||||||
|
use crate::IfModified;
|
||||||
|
|
||||||
const CHANNEL_COUNT: u32 = 4;
|
const CHANNEL_COUNT: u32 = 4;
|
||||||
|
//TODO: make this work
|
||||||
|
const ALLOW_ROTATION: bool = false;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct TextureHandle {
|
pub struct TextureHandle {
|
||||||
|
@ -27,15 +31,20 @@ pub(crate) struct TextureAllocation {
|
||||||
pub rotated: bool,
|
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 {
|
pub(crate) struct TextureAtlasManager {
|
||||||
packer: DensePacker,
|
packer: DensePacker,
|
||||||
count: u32,
|
count: u32,
|
||||||
size: UVec2,
|
size: UVec2,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
allocations: HashMap<u32, TextureAllocation, BuildNoHashHasher<u32>>,
|
allocations: HashMap<u32, TextureAllocation, BuildNoHashHasher<u32>>,
|
||||||
|
modified: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureAtlasManager {
|
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 {
|
pub fn new(size: UVec2) -> Self {
|
||||||
Self {
|
Self {
|
||||||
packer: DensePacker::new(size.x as i32, size.y as i32),
|
packer: DensePacker::new(size.x as i32, size.y as i32),
|
||||||
|
@ -43,9 +52,11 @@ impl TextureAtlasManager {
|
||||||
size: UVec2::new(0, 0),
|
size: UVec2::new(0, 0),
|
||||||
data: vec![0; (size.x * size.y * CHANNEL_COUNT) as usize],
|
data: vec![0; (size.x * size.y * CHANNEL_COUNT) as usize],
|
||||||
allocations: HashMap::default(),
|
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) {
|
pub fn resize(&mut self, new_size: UVec2) {
|
||||||
if new_size.x > self.size.x && new_size.y > self.size.y{
|
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);
|
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");
|
todo!("Atlas downscaling is not implemented yet");
|
||||||
}
|
}
|
||||||
self.size = new_size;
|
self.size = new_size;
|
||||||
|
self.modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a new texture region in the atlas and return a handle to it\
|
/// 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\
|
/// 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
|
/// Use `allocate` to allocate a texture and resize the atlas if necessary\
|
||||||
fn allocate(&mut self, size: UVec2) -> Option<TextureHandle> {
|
/// Does not modify the texture data
|
||||||
let result = self.packer.pack(size.x as i32, size.y as i32, true)?;
|
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;
|
let index = self.count;
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
let allocation = TextureAllocation {
|
let allocation = TextureAllocation {
|
||||||
|
@ -79,28 +92,30 @@ impl TextureAtlasManager {
|
||||||
position: UVec2::new(result.x as u32, result.y as u32),
|
position: UVec2::new(result.x as u32, result.y as u32),
|
||||||
size,
|
size,
|
||||||
//If the size does not match the requested size, the texture was rotated
|
//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);
|
self.allocations.insert_unique_unchecked(index, allocation);
|
||||||
Some(TextureHandle { index })
|
Some(TextureHandle { index })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a new texture region in the atlas and resize the atlas if necessary\
|
/// Allocate a new texture region in the atlas and resize the atlas if necessary\
|
||||||
/// This function should never fail under normal circumstances.
|
/// This function should never fail under normal circumstances.\
|
||||||
fn allocate_resize(&mut self, size: UVec2) -> TextureHandle {
|
/// May modify the texture data if the atlas is resized
|
||||||
|
pub fn allocate(&mut self, size: UVec2) -> TextureHandle {
|
||||||
let mut new_size = self.size;
|
let mut new_size = self.size;
|
||||||
while !self.packer.can_pack(size.x as i32, size.y as i32, true) {
|
while !self.packer.can_pack(size.x as i32, size.y as i32, true) {
|
||||||
new_size *= 2;
|
new_size *= 2;
|
||||||
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
||||||
}
|
}
|
||||||
self.resize(new_size);
|
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 {
|
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 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();
|
let allocation = self.allocations.get_mut(&handle.index).unwrap();
|
||||||
assert!(!allocation.rotated, "Rotated textures are not implemented yet");
|
assert!(!allocation.rotated, "Rotated textures are not implemented yet");
|
||||||
for y in 0..size.y {
|
for y in 0..size.y {
|
||||||
|
@ -130,10 +145,33 @@ impl TextureAtlasManager {
|
||||||
pub fn get(&self, handle: TextureHandle) -> Option<&TextureAllocation> {
|
pub fn get(&self, handle: TextureHandle) -> Option<&TextureAllocation> {
|
||||||
self.allocations.get(&handle.index)
|
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 {
|
impl Default for TextureAtlasManager {
|
||||||
|
/// Create a new texture atlas with a default size of 512x512
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(UVec2::new(512, 512))
|
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;
|
pub use instance::UiInstance;
|
||||||
|
|
||||||
|
#[deprecated(since = "0.1.0-alpha.4", note = "will be removed in the next release")]
|
||||||
pub trait IfModified<T> {
|
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>;
|
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;
|
use ftm::FontTextureManager;
|
||||||
pub use ftm::{FontTextureInfo, GlyphCacheEntry};
|
pub use ftm::{FontTextureInfo, GlyphCacheEntry};
|
||||||
|
|
||||||
|
use crate::draw::atlas::TextureAtlasManager;
|
||||||
|
|
||||||
pub struct TextRenderer {
|
pub struct TextRenderer {
|
||||||
fm: FontManager,
|
fm: FontManager,
|
||||||
ftm: FontTextureManager,
|
ftm: FontTextureManager,
|
||||||
|
@ -29,16 +31,8 @@ impl TextRenderer {
|
||||||
self.fm.add_font(Font::from_bytes(font, FontSettings::default()).unwrap())
|
self.fm.add_font(Font::from_bytes(font, FontSettings::default()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_frame(&mut self) {
|
pub fn glyph(&mut self, atlas: &mut TextureAtlasManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||||
self.ftm.reset_modified();
|
self.ftm.glyph(&self.fm, atlas, font_handle, character, size)
|
||||||
}
|
|
||||||
|
|
||||||
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(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
pub(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use fontdue::Metrics;
|
use fontdue::Metrics;
|
||||||
use glam::{IVec2, UVec2, uvec2, ivec2};
|
use glam::UVec2;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rect_packer::DensePacker;
|
use crate::{
|
||||||
|
draw::atlas::{TextureAtlasManager, TextureHandle},
|
||||||
use crate::IfModified;
|
IfModified
|
||||||
|
};
|
||||||
|
|
||||||
use super::font::{FontHandle, FontManager};
|
use super::font::{FontHandle, FontManager};
|
||||||
|
|
||||||
|
@ -16,70 +17,30 @@ struct GlyphCacheKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GlyphCacheEntry {
|
pub struct GlyphCacheEntry {
|
||||||
pub data: Vec<u8>,
|
|
||||||
pub metrics: Metrics,
|
pub metrics: Metrics,
|
||||||
pub position: IVec2,
|
pub texture: TextureHandle,
|
||||||
pub size: UVec2,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 {
|
pub struct FontTextureManager {
|
||||||
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>,
|
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>
|
||||||
packer: DensePacker,
|
|
||||||
font_texture: Vec<u8>,
|
|
||||||
font_texture_size: UVec2,
|
|
||||||
modified: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontTextureManager {
|
impl FontTextureManager {
|
||||||
pub fn new(size: UVec2) -> Self {
|
pub fn new() -> Self {
|
||||||
FontTextureManager {
|
FontTextureManager {
|
||||||
glyph_cache: HashMap::new(),
|
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.
|
/// 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 {
|
let key = GlyphCacheKey {
|
||||||
font_index: font_handle.0,
|
font_index: font_handle.0,
|
||||||
character,
|
character,
|
||||||
|
@ -91,37 +52,15 @@ impl FontTextureManager {
|
||||||
let font = font_manager.get(font_handle).unwrap();
|
let font = font_manager.get(font_handle).unwrap();
|
||||||
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
||||||
log::debug!("rasterized glyph: {}, {:?}, {:?}", character, metrics, bitmap);
|
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 = atlas.add(metrics.width, &bitmap);
|
||||||
let texture_size = uvec2(metrics.width as u32, metrics.height as u32);
|
|
||||||
let entry = Arc::new(GlyphCacheEntry {
|
let entry = Arc::new(GlyphCacheEntry {
|
||||||
data: bitmap,
|
|
||||||
metrics,
|
metrics,
|
||||||
position: ivec2(texture_position.x, texture_position.y),
|
texture
|
||||||
size: texture_size,
|
|
||||||
});
|
});
|
||||||
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
|
self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry));
|
||||||
self.glyph_place(&entry);
|
|
||||||
self.modified = true;
|
|
||||||
entry
|
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> {
|
// 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);
|
// let (is_new, glyph) = self.glyph_allocate(font_manager, font_handle, character, size);
|
||||||
// if is_new {
|
// if is_new {
|
||||||
|
@ -133,7 +72,5 @@ impl FontTextureManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FontTextureManager {
|
impl Default for FontTextureManager {
|
||||||
fn default() -> Self {
|
fn default() -> Self { Self::new() }
|
||||||
Self::new(uvec2(1024, 1024))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue