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 underline: beamterm_data::LineDecoration,
33 strikethrough: beamterm_data::LineDecoration,
35}
36
37impl FontAtlas {
38 pub fn load_default(gl: &web_sys::WebGl2RenderingContext) -> Result<Self, Error> {
40 let config = FontAtlasData::default();
41 Self::load(gl, config)
42 }
43
44 pub fn load(
46 gl: &web_sys::WebGl2RenderingContext,
47 config: FontAtlasData,
48 ) -> Result<Self, Error> {
49 let texture = crate::gl::texture::Texture::from_font_atlas_data(gl, GL::RGBA, &config)?;
50 let num_slices = config.texture_dimensions.2;
51
52 let texture_layers = config.glyphs.iter().map(|g| g.id as i32).max().unwrap_or(0) + 1;
53 console::log_1(
54 &format!("Creating atlas grid with {}/{texture_layers} layers", config.glyphs.len())
55 .into(),
56 );
57
58 let (cell_width, cell_height) = config.cell_size;
59 let mut layers = HashMap::new();
60
61 config.glyphs.iter()
64 .filter(|g| g.style == FontStyle::Normal) .filter(|g| !g.is_ascii()) .for_each(|g| {
67 layers.insert(g.symbol.to_compact_string(), g.id);
68 });
69
70 Ok(Self {
71 texture,
72 glyph_coords: layers,
73 cell_size: (cell_width, cell_height),
74 num_slices: num_slices as u32,
75 underline: config.underline,
76 strikethrough: config.strikethrough,
77 })
78 }
79
80 pub fn bind(&self, gl: &web_sys::WebGl2RenderingContext, texture_unit: u32) {
82 self.texture.bind(gl, texture_unit);
83 }
84
85 pub fn cell_size(&self) -> (i32, i32) {
86 let (w, h) = self.cell_size;
87 (w - 2 * FontAtlasData::PADDING, h - 2 * FontAtlasData::PADDING)
88 }
89
90 pub fn underline(&self) -> beamterm_data::LineDecoration {
92 self.underline
93 }
94
95 pub fn strikethrough(&self) -> beamterm_data::LineDecoration {
97 self.strikethrough
98 }
99
100 pub(super) fn get_base_glyph_id(&self, key: &str) -> Option<u16> {
102 if key.len() == 1 {
103 let ch = key.chars().next().unwrap();
104 if ch.is_ascii() {
105 let id = ch as u16;
107 return Some(id);
108 }
109 }
110
111 self.glyph_coords.get(key).copied()
112 }
113}