diff options
| author | LLLL Colonq <llll@colonq> | 2025-01-15 21:21:22 -0500 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2025-01-15 21:21:22 -0500 |
| commit | 82d1c94d999654bda5d40108393eade80b77342c (patch) | |
| tree | 2a05c0ffe34ba47933ca39ebef3c3609fb6a5d2a /src/font.rs | |
| parent | f1b47ccb8a92280df51bb28b495829f8f7f8127e (diff) | |
Update
Diffstat (limited to 'src/font.rs')
| -rw-r--r-- | src/font.rs | 137 |
1 files changed, 133 insertions, 4 deletions
diff --git a/src/font.rs b/src/font.rs index 393b1f5..fb8f133 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,21 +1,24 @@ +use std::collections::HashMap; + use crate::{context, texture, shader}; +use glow::HasContext; pub const CHAR_WIDTH: i32 = 7; pub const CHAR_HEIGHT: i32 = 9; pub const FONT_WIDTH: i32 = 112; pub const FONT_HEIGHT: i32 = 54; -pub struct Font { +pub struct Bitmap { pub shader: shader::Shader, pub font: texture::Texture, } -impl Font { +impl Bitmap { pub fn new(ctx: &context::Context) -> Self { let shader = shader::Shader::new_nolib( &ctx, - include_str!("assets/shaders/text/vert.glsl"), - include_str!("assets/shaders/text/frag.glsl"), + include_str!("assets/shaders/bitmap/vert.glsl"), + include_str!("assets/shaders/bitmap/frag.glsl"), ); let font = texture::Texture::new(ctx, include_bytes!("assets/fonts/simple.png")); Self { @@ -85,3 +88,129 @@ impl Font { self.render_text_helper(ctx, pos, text, &glam::Vec3::new(1.0, 1.0, 1.0)); } } + +pub struct AtlasInfo { + pub pos: usize, +} + +pub struct TrueType { + pub shader: shader::Shader, + pub font: fontdue::Font, + pub atlas: texture::Texture, + pub atlaswidth: usize, + pub cellwidth: usize, + pub cellheight: usize, + pub info: HashMap<char, AtlasInfo>, +} + +impl TrueType { + pub fn new(ctx: &context::Context) -> Self { + let shader = shader::Shader::new_nolib( + &ctx, + include_str!("assets/shaders/truetype/vert.glsl"), + include_str!("assets/shaders/truetype/frag.glsl"), + ); + let size = 20.0; + let font = fontdue::Font::from_bytes( + include_bytes!("assets/fonts/ComicNeue-Regular.ttf") as &[u8], + fontdue::FontSettings::default(), + ).expect("failed to load font"); + let mut chardata = HashMap::new(); + for ci in 0..128 { + if let Some(c) = char::from_u32(ci) { + if !c.is_ascii_graphic() { continue; } + let res = font.rasterize(c, size); + chardata.insert(c, res); + } + } + let mut cellwidth = 0; + let mut cellbase = 0; + let mut cellextra = 0; + for (_, (m, _)) in &chardata { + if m.width > cellwidth { cellwidth = m.width } + if m.height > cellbase { cellbase = m.height } + let extra = (-m.ymin.min(0)) as usize; + if extra > cellextra { cellextra = extra } + } + let mut cellheight = cellbase + cellextra; + cellwidth = cellwidth.next_power_of_two(); + cellheight = cellheight.next_power_of_two(); + let atlaswidth = (chardata.len() * cellwidth).next_power_of_two(); + let mut info = HashMap::new(); + let mut atlas_bmp: Vec<u8> = vec![0; atlaswidth * cellheight]; + for (i, (c, (m, bmp))) in chardata.iter().enumerate() { + let by = ((cellbase as i32) - (m.height as i32) - m.ymin) as usize; + let bx = cellwidth * i; + info.insert(*c, AtlasInfo { + pos: cellwidth * i, + }); + for x in 0..m.width { + for y in 0..m.height { + atlas_bmp[bx + x + (by + y) * atlaswidth] = bmp[x + y * m.width]; + } + } + } + let atlas = texture::Texture::new_empty(ctx); + unsafe { + ctx.gl.bind_texture(glow::TEXTURE_2D, Some(atlas.tex)); + ctx.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); + ctx.gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + glow::R8 as i32, + atlaswidth as i32, + cellheight as i32, + 0, + glow::RED, + glow::UNSIGNED_BYTE, + Some(&atlas_bmp), + ); + ctx.gl.generate_mipmap(glow::TEXTURE_2D); + } + Self { shader, font, atlas, atlaswidth, cellwidth, cellheight, info, } + } + + pub fn render_text(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str) { + self.shader.bind(ctx); + unsafe { + ctx.gl.active_texture(glow::TEXTURE0); + ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.atlas.tex)); + } + self.shader.set_mat4( + ctx, "view", + &glam::Mat4::from_scale( + glam::Vec3::new( + 2.0 / context::RENDER_WIDTH, + 2.0 / context::RENDER_HEIGHT, + 1.0, + ), + ), + ); + let width = text.len() * self.cellwidth; + self.shader.set_mat4( + ctx, "position", + &glam::Mat4::from_scale_rotation_translation( + glam::Vec3::new(width as f32 / 2.0, self.cellheight as f32 / 2.0, 1.0), + glam::Quat::IDENTITY, + glam::Vec3::new( + -context::RENDER_WIDTH / 2.0 + pos.x + width as f32 / 2.0, + context::RENDER_HEIGHT / 2.0 - pos.y - self.cellheight as f32 / 2.0, + 0.0, + ), + ) + ); + let len = text.len().min(256); + let textvals: Vec<i32> = text.chars().take(len).map(|c| { + if let Some(i) = self.info.get(&c) { + i.pos as i32 + } else { + 0 + } + }).collect(); + self.shader.set_i32_array(ctx, "text[0]", &textvals); + self.shader.set_i32(ctx, "atlas_width", self.atlaswidth as i32); + self.shader.set_i32(ctx, "cell_width", self.cellwidth as i32); + self.shader.set_i32(ctx, "text_width", width as i32); + ctx.render_no_geometry(); + } +} |
