Theory a font renderer

This commit is contained in:
Gwilym Kuiper 2022-04-04 22:06:08 +01:00
parent c3a64690cd
commit c5cebb9520
3 changed files with 92 additions and 0 deletions

View file

@ -17,3 +17,4 @@ syn = "1"
proc-macro2 = "1"
quote = "1"
asefile = "0.3.4"
fontdue = "0.7"

View file

@ -0,0 +1,36 @@
use crate::ByteString;
use quote::quote;
use proc_macro2::TokenStream;
struct LetterData {
width: usize,
rendered: Vec<u8>,
}
pub fn load_font(font_data: &[u8], pixels_per_em: f32) -> TokenStream {
let font = fontdue::Font::from_bytes(font_data, Default::default()).expect("Invalid font data");
let font = (0..128)
.map(|i| font.rasterize(char::from_u32(i).unwrap(), pixels_per_em))
.map(|(metrics, bitmap)| {
let width = metrics.width;
LetterData {
width,
rendered: bitmap,
}
})
.map(|letter_data| {
let data_raw = ByteString(&letter_data.rendered);
let width = letter_data.width as u8;
quote!(agb::display::FontLetter {
width: #width,
data: #data_raw,
})
});
quote![
#(#font),*
]
}

View file

@ -3,7 +3,9 @@ use proc_macro::TokenStream;
use proc_macro2::Literal;
use syn::parse::Parser;
use syn::{parse_macro_input, punctuated::Punctuated, LitStr};
use syn::{Expr, ExprLit, Lit};
use std::fs::File;
use std::path::PathBuf;
use std::{iter, path::Path, str};
@ -12,6 +14,7 @@ use quote::{format_ident, quote, ToTokens};
mod aseprite;
mod colour;
mod config;
mod font_loader;
mod image_loader;
mod palette16;
mod rust_generator;
@ -309,6 +312,58 @@ fn palete_tile_data(
(palette_data, tile_data, assignments)
}
#[proc_macro]
pub fn include_font(input: TokenStream) -> TokenStream {
let parser = Punctuated::<Expr, syn::Token![,]>::parse_separated_nonempty;
let parsed = match parser.parse(input) {
Ok(e) => e,
Err(e) => return e.to_compile_error().into(),
};
let all_args: Vec<_> = parsed.into_iter().collect();
if all_args.len() != 2 {
panic!("Include_font requires 2 arguments, got {}", all_args.len());
}
let filename = match &all_args[0] {
Expr::Lit(ExprLit {
lit: Lit::Str(str_lit),
..
}) => str_lit.value(),
_ => panic!("Expected literal string as first argument to include_font"),
};
let font_size = match &all_args[1] {
Expr::Lit(ExprLit {
lit: Lit::Float(value),
..
}) => value.base10_parse::<f32>().expect("Invalid float literal"),
Expr::Lit(ExprLit {
lit: Lit::Int(value),
..
}) => value
.base10_parse::<i32>()
.expect("Invalid integer literal") as f32,
_ => panic!("Expected literal float or integer as second argument to include_font"),
};
let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
let path = Path::new(&root).join(&*filename);
let file_content = std::fs::read(&path).expect("Failed to read ttf file");
let rendered = font_loader::load_font(&file_content, font_size);
let include_path = path.to_string_lossy();
quote!({
let _ = include_bytes!(#include_path);
#rendered
})
.into()
}
#[cfg(test)]
mod tests {
use asefile::AnimationDirection;