Break out texture atlas into own struct
This commit is contained in:
parent
e607097da0
commit
2fc191eed4
3 changed files with 98 additions and 65 deletions
|
@ -1,6 +1,9 @@
|
|||
use rusttype::{point, Scale};
|
||||
|
||||
use crate::math::{vec2, Vec2};
|
||||
use crate::{
|
||||
math::{vec2, Vec2},
|
||||
texture_atlas::TextureAtlas,
|
||||
};
|
||||
|
||||
pub struct TextFragment {
|
||||
/// The start of each character, starting at zero.
|
||||
|
@ -48,7 +51,6 @@ pub struct GlyphInfo {
|
|||
const FIRST_ASCII: usize = 32; // 32 == space
|
||||
const LAST_ASCII: usize = 126;
|
||||
|
||||
// TODO: break out texture atlas into separate struct, and fill it dynamically, potentially from multiple fonts.
|
||||
#[derive(Clone)]
|
||||
pub struct Font {
|
||||
font: rusttype::Font<'static>,
|
||||
|
@ -56,9 +58,7 @@ pub struct Font {
|
|||
scale: usize,
|
||||
/// NUM_CHARS big
|
||||
glyph_infos: Vec<GlyphInfo>,
|
||||
atlas_width: usize,
|
||||
atlas_height: usize,
|
||||
atlas: Vec<u8>,
|
||||
atlas: TextureAtlas, // TODO: Arc<Mutex<TextureAtlas>>
|
||||
}
|
||||
|
||||
impl Font {
|
||||
|
@ -88,18 +88,11 @@ impl Font {
|
|||
})
|
||||
.collect();
|
||||
|
||||
// TODO: decide dynamically?
|
||||
let atlas_width = 128;
|
||||
|
||||
let mut atlas_height = 8;
|
||||
let mut atlas = vec![0; atlas_width * atlas_height];
|
||||
let mut atlas = TextureAtlas::new(128, 8); // TODO: better default?
|
||||
|
||||
// Make one white pixel for use for various stuff:
|
||||
atlas[0] = 255;
|
||||
|
||||
let mut cursor_x = 1;
|
||||
let mut cursor_y = 0;
|
||||
let mut row_height = 1;
|
||||
let pos = atlas.allocate((1, 1));
|
||||
atlas[pos] = 255;
|
||||
|
||||
let mut glyph_infos = vec![];
|
||||
|
||||
|
@ -109,29 +102,14 @@ impl Font {
|
|||
let glyph_height = bb.height() as usize;
|
||||
assert!(glyph_width >= 1);
|
||||
assert!(glyph_height >= 1);
|
||||
assert!(glyph_width <= atlas_width);
|
||||
if cursor_x + glyph_width > atlas_width {
|
||||
// New row:
|
||||
cursor_x = 0;
|
||||
cursor_y += row_height;
|
||||
row_height = 0;
|
||||
}
|
||||
|
||||
row_height = row_height.max(glyph_height);
|
||||
while cursor_y + row_height >= atlas_height {
|
||||
atlas_height *= 2;
|
||||
}
|
||||
if atlas_width * atlas_height > atlas.len() {
|
||||
atlas.resize(atlas_width * atlas_height, 0);
|
||||
}
|
||||
let glyph_pos = atlas.allocate((glyph_width, glyph_height));
|
||||
|
||||
glyph.draw(|x, y, v| {
|
||||
if v > 0.0 {
|
||||
let x = x as usize;
|
||||
let y = y as usize;
|
||||
let px = cursor_x + x as usize;
|
||||
let py = cursor_y + y as usize;
|
||||
atlas[py * atlas_width + px] = (v * 255.0).round() as u8;
|
||||
let px = glyph_pos.0 + x as usize;
|
||||
let py = glyph_pos.1 + y as usize;
|
||||
atlas[(px, py)] = (v * 255.0).round() as u8;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -141,15 +119,13 @@ impl Font {
|
|||
advance_width: glyph.unpositioned().h_metrics().advance_width,
|
||||
uv: Some(UvRect {
|
||||
offset: (bb.min.x as i16, offset_y as i16),
|
||||
min: (cursor_x as u16, cursor_y as u16),
|
||||
min: (glyph_pos.0 as u16, glyph_pos.1 as u16),
|
||||
max: (
|
||||
(cursor_x + glyph_width - 1) as u16,
|
||||
(cursor_y + glyph_height - 1) as u16,
|
||||
(glyph_pos.0 + glyph_width - 1) as u16,
|
||||
(glyph_pos.1 + glyph_height - 1) as u16,
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
cursor_x += glyph_width;
|
||||
} else {
|
||||
// No bounding box. Maybe a space?
|
||||
glyph_infos.push(GlyphInfo {
|
||||
|
@ -164,8 +140,6 @@ impl Font {
|
|||
font,
|
||||
scale,
|
||||
glyph_infos,
|
||||
atlas_width,
|
||||
atlas_height,
|
||||
atlas,
|
||||
}
|
||||
}
|
||||
|
@ -179,19 +153,7 @@ impl Font {
|
|||
}
|
||||
|
||||
pub fn texture(&self) -> (u16, u16, &[u8]) {
|
||||
(
|
||||
self.atlas_width as u16,
|
||||
self.atlas_height as u16,
|
||||
&self.atlas,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pixel(&self, x: u16, y: u16) -> u8 {
|
||||
let x = x as usize;
|
||||
let y = y as usize;
|
||||
assert!(x < self.atlas_width);
|
||||
assert!(y < self.atlas_height);
|
||||
self.atlas[y * self.atlas_width + x]
|
||||
self.atlas.texture()
|
||||
}
|
||||
|
||||
pub fn uv_rect(&self, c: char) -> Option<UvRect> {
|
||||
|
@ -317,15 +279,6 @@ impl Font {
|
|||
(text_fragments, bounding_size)
|
||||
}
|
||||
|
||||
pub fn debug_print_atlas_ascii_art(&self) {
|
||||
for y in 0..self.atlas_height {
|
||||
println!(
|
||||
"{}",
|
||||
as_ascii(&self.atlas[y * self.atlas_width..(y + 1) * self.atlas_width])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_print_all_chars(&self) {
|
||||
let max_width = 160;
|
||||
let scale = Scale::uniform(self.scale as f32);
|
||||
|
@ -349,7 +302,7 @@ impl Font {
|
|||
if let Some(uv) = glyph.uv {
|
||||
for x in uv.min.0..=uv.max.0 {
|
||||
for y in uv.min.1..=uv.max.1 {
|
||||
let pixel = self.pixel(x as u16, y as u16);
|
||||
let pixel = self.atlas[(x as usize, y as usize)];
|
||||
let rx = uv.offset.0 + x as i16 - uv.min.0 as i16;
|
||||
let ry = uv.offset.1 + y as i16 - uv.min.1 as i16;
|
||||
let px = (cursor_x + rx as f32).round();
|
||||
|
@ -393,7 +346,6 @@ mod tests {
|
|||
#[test]
|
||||
fn font_test() {
|
||||
let font = Font::new(13);
|
||||
font.debug_print_atlas_ascii_art();
|
||||
font.debug_print_all_chars();
|
||||
panic!();
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ mod layout;
|
|||
pub mod math;
|
||||
mod painter;
|
||||
mod style;
|
||||
mod texture_atlas;
|
||||
pub mod types;
|
||||
pub mod widgets;
|
||||
|
||||
|
|
80
emigui/src/texture_atlas.rs
Normal file
80
emigui/src/texture_atlas.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
/// A texture pixels, used for fonts.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct TextureAtlas {
|
||||
width: usize,
|
||||
height: usize,
|
||||
pixels: Vec<u8>,
|
||||
|
||||
/// Used for when adding new rects
|
||||
cursor: (usize, usize),
|
||||
row_height: usize,
|
||||
}
|
||||
|
||||
impl TextureAtlas {
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
TextureAtlas {
|
||||
width: width,
|
||||
height: height,
|
||||
pixels: vec![0; width * height],
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn pixels(&self) -> &[u8] {
|
||||
&self.pixels
|
||||
}
|
||||
|
||||
pub fn texture(&self) -> (u16, u16, &[u8]) {
|
||||
(self.width() as u16, self.height() as u16, self.pixels())
|
||||
}
|
||||
|
||||
/// Returns the coordinates of where the rect ended up.
|
||||
pub fn allocate(&mut self, (w, h): (usize, usize)) -> (usize, usize) {
|
||||
assert!(w <= self.width);
|
||||
if self.cursor.0 + w > self.width {
|
||||
// New row:
|
||||
self.cursor.0 = 0;
|
||||
self.cursor.1 += self.row_height;
|
||||
self.row_height = 0;
|
||||
}
|
||||
|
||||
self.row_height = self.row_height.max(h);
|
||||
while self.cursor.1 + self.row_height >= self.height {
|
||||
self.height *= 2;
|
||||
}
|
||||
|
||||
if self.width * self.height > self.pixels.len() {
|
||||
self.pixels.resize(self.width * self.height, 0);
|
||||
}
|
||||
|
||||
let pos = self.cursor;
|
||||
self.cursor.0 += w;
|
||||
(pos.0 as usize, pos.1 as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<(usize, usize)> for TextureAtlas {
|
||||
type Output = u8;
|
||||
|
||||
fn index(&self, (x, y): (usize, usize)) -> &u8 {
|
||||
assert!(x < self.width);
|
||||
assert!(y < self.height);
|
||||
&self.pixels[y * self.width + x]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::IndexMut<(usize, usize)> for TextureAtlas {
|
||||
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut u8 {
|
||||
assert!(x < self.width);
|
||||
assert!(y < self.height);
|
||||
&mut self.pixels[y * self.width + x]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue