Skip to content

Commit

Permalink
Fix font atlas generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
lte678 committed Oct 24, 2024
1 parent f0d058e commit 735575f
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ Cargo.lock
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/


font_atlas.png
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ sdl2 = { version = "0.37.0", default-features = false, features = ["gfx"] }
itertools = "0.8.2"
glium = "0.35.0"
clap = { version="4.5.16", features=["derive"] }
font-kit = "0.14.2"
# font-kit = "0.14.2"
font-kit = { path = "/home/leon/Projects/font-kit" }
pathfinder_geometry = "0.5.1"
log = "0.4.22"
env_logger = "0.11.5"
image = "0.25.2"
freetype-sys = "0.20"
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ fn gpu_mode() {
println!("Initialized GPU context.");

let font = load_default_fonts();
let font_atlas: font_kit::canvas::Canvas = generate_atlas(&font, 32.0);
let font_atlas: font_kit::canvas::Canvas = generate_atlas(&font, 32.0, None);
let font_atlas_img = ImageBuffer::<Rgb<u8>, Vec<u8>>::from_raw(
font_atlas.size.x() as u32, font_atlas.size.y() as u32, font_atlas.pixels
).unwrap();
Expand Down
36 changes: 22 additions & 14 deletions src/text_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,62 +51,70 @@ pub fn load_default_fonts() -> Font {
}


fn estimate_atlas_size(font: &Font, size: f32) -> (i32, i32){
/// Estimates the size of a square atlas necessary to contain all the font letters.
/// Padding is one-sided.
fn estimate_atlas_size(font: &Font, size: f32, padding: i32) -> (i32, i32){
let mut total_length = 0;
let mut max_height = 0;
for c in PrintableChars::new() {
let glyph_id = font.glyph_for_char(c).expect(&format!("Could not lookup glyph for '{}' ({})", c, c as u32));
// This function is not accurate. A test shows that it may underpredict the raster bounds by up to 2 px.
let raster_rect = font.raster_bounds(
glyph_id, size,
Transform2F::default(),
HintingOptions::Vertical(size),
RasterizationOptions::SubpixelAa
).unwrap();
total_length += raster_rect.width();
total_length += raster_rect.width() + padding;
max_height = max(max_height, raster_rect.height());
}
let total_area = total_length * max_height;
// Add three extra rows: One for characters that dont fit at the end of the line,
// and another for a possibly cut of row at the bottom, and one for margin :)
let side_length_guess = (total_area as f32).sqrt();
let side_length = (total_area as f32 + side_length_guess * (max_height as f32) * 3.0).sqrt() as i32;
let side_length = (total_area as f32 + side_length_guess * (max_height as f32) * 2.0).sqrt() as i32;
return (side_length, side_length)
}


pub fn generate_atlas(font: &Font, size: f32) -> Canvas {
let (canvas_w, canvas_h) = estimate_atlas_size(&font, size);
pub fn generate_atlas(font: &Font, size: f32, padding: Option<i32>) -> Canvas {
// 2px has been shown to be a safe minimum padding.
let padding = padding.unwrap_or(2);
let (canvas_w, canvas_h) = estimate_atlas_size(&font, size, padding);
log::debug!("Creating font atlas with dimensions {}x{} for \"{}\", {:.1}px", canvas_w, canvas_h, font.full_name(), size);
let mut canvas = Canvas::new(Vector2I::new(canvas_w, canvas_h), Format::Rgb24);
let mut transform_vec = Vector2F::new(0.0, -size);
let mut transform_vec = Vector2F::new(0.0, 0.0);

for c in PrintableChars::new() {

let glyph_id = font.glyph_for_char(c).unwrap();

// Predict raster width
let raster_rect = font.raster_bounds(
glyph_id, size,
Transform2F::from_translation(transform_vec),
Transform2F::default(),
HintingOptions::Vertical(size),
RasterizationOptions::SubpixelAa
).unwrap();
// Skip drawing this glyph if it is zero-width.
if raster_rect.width() == 0 { continue; }

let new_x = transform_vec.x() + raster_rect.width() as f32;
if new_x > canvas_w as f32 {
// Calculate offset
let mut new_x = transform_vec.x() as i32 + raster_rect.width() + padding;
if new_x >= canvas_w {
new_x = raster_rect.width() + padding;
transform_vec.set_x(0.0);
transform_vec.set_y(transform_vec.y() - size);
transform_vec.set_y(transform_vec.y() + size);
}

font.rasterize_glyph(
&mut canvas,
glyph_id,
size,
Transform2F::from_translation(transform_vec),
Transform2F::from_translation(-raster_rect.origin().to_f32()) * Transform2F::from_translation(transform_vec),
HintingOptions::Vertical(size),
RasterizationOptions::SubpixelAa,
).unwrap();

transform_vec.set_x(new_x);
transform_vec.set_x(new_x as f32);
}
canvas
}

0 comments on commit 735575f

Please sign in to comment.