tick system, AVX2 UTF-8 decoder, uh faster in general

This commit is contained in:
Zacharias-Brohn
2025-12-22 00:22:55 +01:00
parent f6a5e23f3d
commit 73b52ab341
30 changed files with 10231 additions and 5210 deletions
+301
View File
@@ -0,0 +1,301 @@
//! Font loading and discovery using fontconfig.
//!
//! This module provides font loading utilities including:
//! - Finding fonts by family name with style variants (regular, bold, italic, bold-italic)
//! - Finding fonts that contain specific characters (for fallback)
//! - Loading font data for use with ab_glyph and rustybuzz
use ab_glyph::FontRef;
use fontconfig::Fontconfig;
use std::ffi::CStr;
use std::path::PathBuf;
// ═══════════════════════════════════════════════════════════════════════════════
// FONT VARIANT
// ═══════════════════════════════════════════════════════════════════════════════
/// A font variant with its data and parsed references.
pub struct FontVariant {
/// Owned font data (kept alive for the lifetime of the font references).
#[allow(dead_code)]
data: Box<[u8]>,
/// ab_glyph font reference for rasterization.
font: FontRef<'static>,
/// rustybuzz face for text shaping.
face: rustybuzz::Face<'static>,
}
impl FontVariant {
/// Get a reference to the ab_glyph font.
pub fn font(&self) -> &FontRef<'static> {
&self.font
}
/// Get a reference to the rustybuzz face.
pub fn face(&self) -> &rustybuzz::Face<'static> {
&self.face
}
/// Clone the font reference (ab_glyph FontRef is Clone).
pub fn clone_font(&self) -> FontRef<'static> {
self.font.clone()
}
/// Clone the font data.
pub fn clone_data(&self) -> Box<[u8]> {
self.data.clone()
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// FONT DISCOVERY
// ═══════════════════════════════════════════════════════════════════════════════
/// Find a font that contains the given character using fontconfig.
/// Returns the path to the font file if found.
///
/// Note: For emoji, use `find_color_font_for_char` from the color_font module instead,
/// which explicitly requests color fonts.
pub fn find_font_for_char(_fc: &Fontconfig, c: char) -> Option<PathBuf> {
use fontconfig_sys as fcsys;
use fcsys::*;
unsafe {
// Create a pattern
let pat = FcPatternCreate();
if pat.is_null() {
return None;
}
// Create a charset with the target character
let charset = FcCharSetCreate();
if charset.is_null() {
FcPatternDestroy(pat);
return None;
}
// Add the character to the charset
FcCharSetAddChar(charset, c as u32);
// Add the charset to the pattern
let fc_charset_cstr = CStr::from_bytes_with_nul(b"charset\0").unwrap();
FcPatternAddCharSet(pat, fc_charset_cstr.as_ptr(), charset);
// Run substitutions
FcConfigSubstitute(std::ptr::null_mut(), pat, FcMatchPattern);
FcDefaultSubstitute(pat);
// Find matching font
let mut result = FcResultNoMatch;
let matched = FcFontMatch(std::ptr::null_mut(), pat, &mut result);
let font_result = if !matched.is_null() && result == FcResultMatch {
// Get the file path from the matched pattern
let mut file_ptr: *mut FcChar8 = std::ptr::null_mut();
let fc_file_cstr = CStr::from_bytes_with_nul(b"file\0").unwrap();
if FcPatternGetString(matched, fc_file_cstr.as_ptr(), 0, &mut file_ptr) == FcResultMatch
{
let path_cstr = CStr::from_ptr(file_ptr as *const i8);
Some(PathBuf::from(path_cstr.to_string_lossy().into_owned()))
} else {
None
}
} else {
None
};
// Cleanup
if !matched.is_null() {
FcPatternDestroy(matched);
}
FcCharSetDestroy(charset);
FcPatternDestroy(pat);
font_result
}
}
/// Find font files for a font family using fontconfig.
/// Returns paths for (regular, bold, italic, bold_italic).
/// Any variant that can't be found will be None.
pub fn find_font_family_variants(family: &str) -> [Option<PathBuf>; 4] {
use fontconfig_sys as fcsys;
use fcsys::*;
use fcsys::constants::{FC_FAMILY, FC_WEIGHT, FC_SLANT, FC_FILE};
use std::ffi::CString;
let mut results: [Option<PathBuf>; 4] = [None, None, None, None];
// Style queries: (weight, slant) pairs for each variant
// FC_WEIGHT_REGULAR = 80, FC_WEIGHT_BOLD = 200
// FC_SLANT_ROMAN = 0, FC_SLANT_ITALIC = 100
let styles: [(i32, i32); 4] = [
(80, 0), // Regular
(200, 0), // Bold
(80, 100), // Italic
(200, 100), // BoldItalic
];
unsafe {
let family_cstr = match CString::new(family) {
Ok(s) => s,
Err(_) => return results,
};
for (idx, (weight, slant)) in styles.iter().enumerate() {
let pat = FcPatternCreate();
if pat.is_null() {
continue;
}
// Set family name
FcPatternAddString(pat, FC_FAMILY.as_ptr() as *const i8, family_cstr.as_ptr() as *const u8);
// Set weight
FcPatternAddInteger(pat, FC_WEIGHT.as_ptr() as *const i8, *weight);
// Set slant
FcPatternAddInteger(pat, FC_SLANT.as_ptr() as *const i8, *slant);
FcConfigSubstitute(std::ptr::null_mut(), pat, FcMatchPattern);
FcDefaultSubstitute(pat);
let mut result: FcResult = FcResultMatch;
let matched = FcFontMatch(std::ptr::null_mut(), pat, &mut result);
if result == FcResultMatch && !matched.is_null() {
let mut file_ptr: *mut u8 = std::ptr::null_mut();
if FcPatternGetString(matched, FC_FILE.as_ptr() as *const i8, 0, &mut file_ptr) == FcResultMatch {
if !file_ptr.is_null() {
let path_cstr = std::ffi::CStr::from_ptr(file_ptr as *const i8);
if let Ok(path_str) = path_cstr.to_str() {
results[idx] = Some(PathBuf::from(path_str));
}
}
}
FcPatternDestroy(matched);
}
FcPatternDestroy(pat);
}
}
results
}
// ═══════════════════════════════════════════════════════════════════════════════
// FONT LOADING
// ═══════════════════════════════════════════════════════════════════════════════
/// Try to load a font file and create both ab_glyph and rustybuzz handles.
/// Returns None if the file doesn't exist or can't be parsed.
pub fn load_font_variant(path: &std::path::Path) -> Option<FontVariant> {
let data = std::fs::read(path).ok()?.into_boxed_slice();
// Parse with ab_glyph
let font: FontRef<'static> = {
let font = FontRef::try_from_slice(&data).ok()?;
// SAFETY: We keep data alive in the FontVariant struct
unsafe { std::mem::transmute(font) }
};
// Parse with rustybuzz
let face: rustybuzz::Face<'static> = {
let face = rustybuzz::Face::from_slice(&data, 0)?;
// SAFETY: We keep data alive in the FontVariant struct
unsafe { std::mem::transmute(face) }
};
Some(FontVariant { data, font, face })
}
/// Load font variants for a font family.
/// Returns array of font variants, with index 0 being the regular font.
/// Falls back to hardcoded paths if fontconfig fails.
pub fn load_font_family(font_family: Option<&str>) -> (Box<[u8]>, FontRef<'static>, [Option<FontVariant>; 4]) {
// Try to use fontconfig to find the font family
if let Some(family) = font_family {
let paths = find_font_family_variants(family);
log::info!("Font family '{}' resolved to:", family);
for (i, path) in paths.iter().enumerate() {
let style = match i {
0 => "Regular",
1 => "Bold",
2 => "Italic",
3 => "BoldItalic",
_ => "Unknown",
};
if let Some(p) = path {
log::info!(" {}: {:?}", style, p);
}
}
// Load the regular font (required)
if let Some(regular_path) = &paths[0] {
if let Some(regular) = load_font_variant(regular_path) {
let primary_font = regular.clone_font();
let font_data = regular.clone_data();
// Load other variants
let variants: [Option<FontVariant>; 4] = [
Some(regular),
paths[1].as_ref().and_then(|p| load_font_variant(p)),
paths[2].as_ref().and_then(|p| load_font_variant(p)),
paths[3].as_ref().and_then(|p| load_font_variant(p)),
];
return (font_data, primary_font, variants);
}
}
log::warn!("Failed to load font family '{}', falling back to defaults", family);
}
// Fallback: try hardcoded paths
let fallback_fonts = [
("/usr/share/fonts/TTF/0xProtoNerdFont-Regular.ttf",
"/usr/share/fonts/TTF/0xProtoNerdFont-Bold.ttf",
"/usr/share/fonts/TTF/0xProtoNerdFont-Italic.ttf",
"/usr/share/fonts/TTF/0xProtoNerdFont-BoldItalic.ttf"),
("/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf",
"/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Bold.ttf",
"/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Italic.ttf",
"/usr/share/fonts/TTF/JetBrainsMonoNerdFont-BoldItalic.ttf"),
("/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf",
"/usr/share/fonts/TTF/JetBrainsMono-Bold.ttf",
"/usr/share/fonts/TTF/JetBrainsMono-Italic.ttf",
"/usr/share/fonts/TTF/JetBrainsMono-BoldItalic.ttf"),
];
for (regular, bold, italic, bold_italic) in fallback_fonts {
let regular_path = std::path::Path::new(regular);
if let Some(regular_variant) = load_font_variant(regular_path) {
let primary_font = regular_variant.clone_font();
let font_data = regular_variant.clone_data();
let variants: [Option<FontVariant>; 4] = [
Some(regular_variant),
load_font_variant(std::path::Path::new(bold)),
load_font_variant(std::path::Path::new(italic)),
load_font_variant(std::path::Path::new(bold_italic)),
];
log::info!("Loaded font from fallback paths:");
log::info!(" Regular: {}", regular);
if variants[1].is_some() { log::info!(" Bold: {}", bold); }
if variants[2].is_some() { log::info!(" Italic: {}", italic); }
if variants[3].is_some() { log::info!(" BoldItalic: {}", bold_italic); }
return (font_data, primary_font, variants);
}
}
// Last resort: try NotoSansMono
let noto_regular = std::path::Path::new("/usr/share/fonts/noto/NotoSansMono-Regular.ttf");
if let Some(regular_variant) = load_font_variant(noto_regular) {
let primary_font = regular_variant.clone_font();
let font_data = regular_variant.clone_data();
let variants: [Option<FontVariant>; 4] = [Some(regular_variant), None, None, None];
log::info!("Loaded NotoSansMono as fallback");
return (font_data, primary_font, variants);
}
panic!("Failed to load any monospace font");
}