beamterm_renderer/gl/
atlas.rs1use std::collections::HashMap;
2
3use beamterm_data::{FontAtlasData, FontStyle};
4use compact_str::{CompactString, ToCompactString};
5use web_sys::console;
6
7use crate::{error::Error, gl::GL};
8
9#[derive(Debug)]
22pub struct FontAtlas {
23 texture: crate::gl::texture::Texture,
25 glyph_coords: HashMap<CompactString, u16>,
27 cell_size: (i32, i32),
29 num_slices: u32,
31}
32
33impl FontAtlas {
34 pub fn load_default(gl: &web_sys::WebGl2RenderingContext) -> Result<Self, Error> {
36 let config = FontAtlasData::default();
37 Self::load(gl, config)
38 }
39
40 pub fn load(
42 gl: &web_sys::WebGl2RenderingContext,
43 config: FontAtlasData,
44 ) -> Result<Self, Error> {
45 let texture = crate::gl::texture::Texture::from_font_atlas_data(gl, GL::RGBA, &config)?;
46 let num_slices = config.texture_dimensions.2;
47
48 let texture_layers = config.glyphs.iter().map(|g| g.id as i32).max().unwrap_or(0) + 1;
49 console::log_1(
50 &format!("Creating atlas grid with {}/{texture_layers} layers", config.glyphs.len())
51 .into(),
52 );
53
54 let (cell_width, cell_height) = config.cell_size;
55 let mut layers = HashMap::new();
56
57 config.glyphs.iter()
60 .filter(|g| g.style == FontStyle::Normal) .filter(|g| !g.is_ascii()) .for_each(|g| {
63 layers.insert(g.symbol.to_compact_string(), g.id);
64 });
65
66 Ok(Self {
67 texture,
68 glyph_coords: layers,
69 cell_size: (cell_width, cell_height),
70 num_slices: num_slices as u32,
71 })
72 }
73
74 pub fn bind(&self, gl: &web_sys::WebGl2RenderingContext, texture_unit: u32) {
76 self.texture.bind(gl, texture_unit);
77 }
78
79 pub fn cell_size(&self) -> (i32, i32) {
80 let (w, h) = self.cell_size;
81 (w - 2 * FontAtlasData::PADDING, h - 2 * FontAtlasData::PADDING)
82 }
84
85 pub(super) fn get_glyph_coord(&self, key: &str, font_style: FontStyle) -> Option<u16> {
87 if key.len() == 1 {
88 let ch = key.chars().next().unwrap();
89 if ch.is_ascii() {
90 let id = ch as u16 | font_style.style_mask();
92 return Some(id);
93 }
94 }
95
96 self.glyph_coords.get(key).copied().map(|id| id | font_style.style_mask())
97 }
98
99 pub(super) fn get_base_glyph_id(&self, key: &str) -> Option<u16> {
101 if key.len() == 1 {
102 let ch = key.chars().next().unwrap();
103 if ch.is_ascii() {
104 let id = ch as u16;
106 return Some(id);
107 }
108 }
109
110 self.glyph_coords.get(key).copied()
111 }
112}