1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Replace ab_glyph with Skrifa + vello_cpu; enable font hinting (#7694)

<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md)
before opening a Pull Request!

* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.

Please be patient! I will review your PR, but my time is limited!
-->

* Closes N/A
* [x] I have followed the instructions in the PR template

I'll probably come back to this and clean it up a bit. This PR
reimplements ab_glyph's functionality on top of Skrifa, a somewhat
lower-level font API that's being used in Chrome now.

Skrifa doesn't perform rasterization itself, so I'm using
[vello_cpu](https://github.com/linebender/vello) from the Linebender
project for rasterization. It's still in its early days, but I believe
it's already quite fast. It also supports color and gradient fills, so
color emoji support will be easier.

Skrifa also supports font hinting, which should make text look a bit
nicer / less blurry.

Here's the current ab_glyph rendering:

<img width="1592" height="1068" alt="image"
src="https://github.com/user-attachments/assets/2385b66e-23f8-4c6e-b8c2-ea90e0eea4e4"
/>

Here's Skrifa *without* hinting--it looks almost identical, but there
are some subpixel differences, probably due to rasterizer behavior:

<img width="1592" height="1068" alt="image"
src="https://github.com/user-attachments/assets/a815f3e9-65ac-4940-bc00-571177bef53d"
/>

Here's Skrifa  *with* hinting:

<img width="1592" height="1068" alt="image"
src="https://github.com/user-attachments/assets/d6cc0669-3537-4377-bba9-ed5ef09664db"
/>

Hinting does make the horizontal strokes look a bit bolder, which makes
me wonder once again about increasing the font weight from "light" to
"regular".

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
valadaptive
2025-12-06 10:11:33 -05:00
committed by GitHub
parent 2174b309bd
commit 609dd2d28e
172 changed files with 928 additions and 643 deletions

View File

@@ -20,7 +20,7 @@ use crate::{
ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText,
SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui,
ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair, ViewportIdSet,
ViewportOutput, Widget as _, WidgetRect, WidgetText,
ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText,
animation_manager::AnimationManager,
containers::{self, area::AreaState},
data::output::PlatformOutput,
@@ -34,8 +34,7 @@ use crate::{
os::OperatingSystem,
output::FullOutput,
pass_state::PassState,
plugin,
plugin::TypedPluginHandle,
plugin::{self, TypedPluginHandle},
resize, response, scroll_area,
util::IdTypeMap,
viewport::ViewportClass,
@@ -564,7 +563,10 @@ impl ContextImpl {
log::trace!("Adding new fonts");
}
let text_alpha_from_coverage = self.memory.options.style().visuals.text_alpha_from_coverage;
let Visuals {
mut text_options, ..
} = self.memory.options.style().visuals;
text_options.max_texture_side = max_texture_side;
let mut is_new = false;
@@ -573,16 +575,12 @@ impl ContextImpl {
is_new = true;
profiling::scope!("Fonts::new");
Fonts::new(
max_texture_side,
text_alpha_from_coverage,
self.font_definitions.clone(),
)
Fonts::new(text_options, self.font_definitions.clone())
});
{
profiling::scope!("Fonts::begin_pass");
fonts.begin_pass(max_texture_side, text_alpha_from_coverage);
fonts.begin_pass(text_options);
}
}
@@ -2006,15 +2004,12 @@ impl Context {
pub fn set_fonts(&self, font_definitions: FontDefinitions) {
profiling::function_scope!();
let mut update_fonts = true;
self.read(|ctx| {
if let Some(current_fonts) = ctx.fonts.as_ref() {
// NOTE: this comparison is expensive since it checks TTF data for equality
if current_fonts.definitions() == &font_definitions {
update_fonts = false; // no need to update
}
}
let update_fonts = self.read(|ctx| {
// NOTE: this comparison is expensive since it checks TTF data for equality
// TODO(valadaptive): add_font only checks the *names* for equality. Change this?
ctx.fonts
.as_ref()
.is_none_or(|fonts| fonts.definitions() != &font_definitions)
});
if update_fonts {

View File

@@ -3,7 +3,7 @@
#![allow(clippy::if_same_then_else)]
use emath::Align;
use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, text::FontTweak};
use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions, text::FontTweak};
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};
use crate::{
@@ -948,8 +948,11 @@ pub struct Visuals {
/// this is more to provide a convenient summary of the rest of the settings.
pub dark_mode: bool,
/// ADVANCED: Controls how we render text.
pub text_alpha_from_coverage: AlphaFromCoverage,
/// Controls how we render text.
///
/// The [`TextOptions::max_texture_side`] is ignored and overruled by
/// [`crate::RawInput::max_texture_side`].
pub text_options: TextOptions,
/// Override default text color for all text.
///
@@ -1407,7 +1410,10 @@ impl Visuals {
pub fn dark() -> Self {
Self {
dark_mode: true,
text_alpha_from_coverage: AlphaFromCoverage::DARK_MODE_DEFAULT,
text_options: TextOptions {
alpha_from_coverage: AlphaFromCoverage::DARK_MODE_DEFAULT,
..Default::default()
},
override_text_color: None,
weak_text_alpha: 0.6,
weak_text_color: None,
@@ -1470,7 +1476,10 @@ impl Visuals {
pub fn light() -> Self {
Self {
dark_mode: false,
text_alpha_from_coverage: AlphaFromCoverage::LIGHT_MODE_DEFAULT,
text_options: TextOptions {
alpha_from_coverage: AlphaFromCoverage::LIGHT_MODE_DEFAULT,
..Default::default()
},
widgets: Widgets::light(),
selection: Selection::light(),
hyperlink_color: Color32::from_rgb(0, 155, 255),
@@ -2107,7 +2116,7 @@ impl Visuals {
pub fn ui(&mut self, ui: &mut crate::Ui) {
let Self {
dark_mode,
text_alpha_from_coverage,
text_options,
override_text_color: _,
weak_text_alpha,
weak_text_color,
@@ -2207,7 +2216,7 @@ impl Visuals {
});
});
ui.collapsing("Text color", |ui| {
ui.collapsing("Text rendering", |ui| {
fn ui_text_color(ui: &mut Ui, color: &mut Color32, label: impl Into<RichText>) {
ui.label(label.into().color(*color));
ui.color_edit_button_srgba(color);
@@ -2259,7 +2268,15 @@ impl Visuals {
ui.add_space(4.0);
text_alpha_from_coverage_ui(ui, text_alpha_from_coverage);
let TextOptions {
max_texture_side: _,
alpha_from_coverage,
font_hinting,
} = text_options;
text_alpha_from_coverage_ui(ui, alpha_from_coverage);
ui.checkbox(font_hinting, "Enable font hinting");
});
ui.collapsing("Text cursor", |ui| {
@@ -2370,9 +2387,9 @@ impl Visuals {
}
}
fn text_alpha_from_coverage_ui(ui: &mut Ui, text_alpha_from_coverage: &mut AlphaFromCoverage) {
fn text_alpha_from_coverage_ui(ui: &mut Ui, alpha_from_coverage: &mut AlphaFromCoverage) {
let mut dark_mode_special =
*text_alpha_from_coverage == AlphaFromCoverage::TwoCoverageMinusCoverageSq;
*alpha_from_coverage == AlphaFromCoverage::TwoCoverageMinusCoverageSq;
ui.horizontal(|ui| {
ui.label("Text rendering:");
@@ -2380,9 +2397,9 @@ fn text_alpha_from_coverage_ui(ui: &mut Ui, text_alpha_from_coverage: &mut Alpha
ui.checkbox(&mut dark_mode_special, "Dark-mode special");
if dark_mode_special {
*text_alpha_from_coverage = AlphaFromCoverage::TwoCoverageMinusCoverageSq;
*alpha_from_coverage = AlphaFromCoverage::DARK_MODE_DEFAULT;
} else {
let mut gamma = match text_alpha_from_coverage {
let mut gamma = match alpha_from_coverage {
AlphaFromCoverage::Linear => 1.0,
AlphaFromCoverage::Gamma(gamma) => *gamma,
AlphaFromCoverage::TwoCoverageMinusCoverageSq => 0.5, // approximately the same
@@ -2396,9 +2413,9 @@ fn text_alpha_from_coverage_ui(ui: &mut Ui, text_alpha_from_coverage: &mut Alpha
);
if gamma == 1.0 {
*text_alpha_from_coverage = AlphaFromCoverage::Linear;
*alpha_from_coverage = AlphaFromCoverage::Linear;
} else {
*text_alpha_from_coverage = AlphaFromCoverage::Gamma(gamma);
*alpha_from_coverage = AlphaFromCoverage::Gamma(gamma);
}
}
});
@@ -2812,6 +2829,7 @@ impl Widget for &mut FontTweak {
scale,
y_offset_factor,
y_offset,
hinting_override,
} = self;
ui.label("Scale");
@@ -2827,6 +2845,19 @@ impl Widget for &mut FontTweak {
ui.add(DragValue::new(y_offset).speed(-0.02));
ui.end_row();
ui.label("hinting_override");
ComboBox::from_id_salt("hinting_override")
.selected_text(match hinting_override {
None => "None",
Some(true) => "Enable",
Some(false) => "Disable",
})
.show_ui(ui, |ui| {
ui.selectable_value(hinting_override, None, "None");
ui.selectable_value(hinting_override, Some(true), "Enable");
ui.selectable_value(hinting_override, Some(false), "Disable");
});
if ui.button("Reset").clicked() {
*self = Default::default();
}

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:784cbcdfd8deaf61e7b663f9416d67724e6a6a189a20ba3351908aa5c5f2deff
size 336159
oid sha256:7051c34854469652d2d953f3110ebcf6fd60f8ee9a2b0c134d9f7255ef180ce5
size 335353

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4cdde1dda0e64f584c769c72f5910a7035e6a4a86a074b590e88365f12570109
size 94062
oid sha256:49823cfa4dfba54e54d0122f2bbb246c1daad2ca3ba15071c1ca44eeb3662855
size 92791

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:824d941ea538fd44fc374f5df1893eee2309004c0ee5e69a97f1c84a74b2b423
size 182128
oid sha256:1b65b6b1a3afe41337b8fe537525677284e49bd90be29cddb837787162ee452a
size 169596

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:44ea7ac8c8e22eb51fbcb63f00c8510de0e6ae126d19ab44c5d708d979b5362b
size 100345
oid sha256:9c3af0c37a6997abe549dd28450c41d3d18bbc99d9577997d493566fbb7f9277
size 96709

View File

@@ -161,15 +161,11 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{
let pixels_per_point = 1.0;
let max_texture_side = 8 * 1024;
let wrap_width = 512.0;
let font_id = egui::FontId::default();
let text_color = egui::Color32::WHITE;
let mut fonts = egui::epaint::text::Fonts::new(
max_texture_side,
egui::epaint::AlphaFromCoverage::default(),
egui::FontDefinitions::default(),
);
let mut fonts =
egui::epaint::text::Fonts::new(Default::default(), egui::FontDefinitions::default());
{
c.bench_function("text_layout_uncached", |b| {
b.iter(|| {
@@ -209,7 +205,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let mut rng = rand::rng();
b.iter(|| {
fonts.begin_pass(max_texture_side, egui::epaint::AlphaFromCoverage::default());
fonts.begin_pass(egui::epaint::TextOptions::default());
// Delete a random character, simulating a user making an edit in a long file:
let mut new_string = string.clone();

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30929184fab7e7d5975243d86bcab79cd9f7a0c5d57dd9ae827464ff6570be7b
size 31795
oid sha256:0c6f6847df5b3bfdcb020c1f897a57ffe0971e9de1e6977b19d3909730e1b9a5
size 30957

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cb944eca56724f6a2106ea8db2043dc94c0ea40bdd4cdeb0e520790f97cc9598
size 27049
oid sha256:43ef176837f05d1795eddda2fee344e935ff6d53edc26548c97eea191d4c6ca2
size 25839

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e4a6476a2bb8980a9207868b77a253c65c0ba8433f843bb17e622856695b720
size 27686
oid sha256:55b899e115bbb7a17e0e40216479f8fb3a343deddf929e4af6af137a3bf6d4b8
size 25632

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5c1951b99908326b3f05ebb72aa4d02d0f297bdd925f38ded09041fae45400c1
size 85217
oid sha256:8ed04e25b2e961f44e2911a037c93729b0cb4de82b2e51cab145abbcb7b4efea
size 74543

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c303fa620a2c7bc491a0ac1f9afdf9601b352e0e5163526c5f8732edf6bd6b3
size 63404
oid sha256:1bc9711ea98472bae9267190a91d3240146f4ce9a0caf0cf462d322581ec96aa
size 60508

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0df751bac5947c9bf6f82d075cf5670a562742b80d6c512bcd642da5ed449d26
size 25975
oid sha256:111caa91ae0658acc17a7dd49582b780ae706ba4ac7812d2a63e09a18a0be967
size 25585

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e26e87f2909414b614278a1cf0b485cda425aceb5419906426615dccdcbef2b
size 20877
oid sha256:7e34217e8a006721bc4525277b96cf99618e3275626aa054b97a8cb4c7c963ab
size 19937

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:20e3050bd41c7b9d225feb71f3bea3fdd1b8f749f77c4d140b5e560f53eb32b7
size 10731
oid sha256:e62836d9afa18cf4e486fe2819e652bf5df160026dc258201db0b99a75bdf7f1
size 10125

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1227636b03a7d35db3482b19f6059ec7aaf03ca795edadd5338056be6f6a8f7f
size 126724
oid sha256:c504102299780498c6b3e463e627588653446caa10bdebc4366900d0e46b649c
size 112349

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c01e96bf0aab24dbcfc05f2a6dcb0ffcddff69ec2474797de4fbce0a0670a8cd
size 24964
oid sha256:d44e5abe3f64d5f72bbf7519b6ead816adf1f8ad22711e8ef8f34f9574223d4b
size 24152

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ec357eafd145194f99c36a53a149a8b331fd691c5088df43ee96282b84bc81a4
size 99439
oid sha256:38d3d349f3c31b6e5a5a04984d290e2e36441b2ced7ac2060b6c2311715068d9
size 96806

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b5c95085e3c78b3fa1cc39ebd032834bd5ab5a80c3a2cad482d8a5bcbad004b9
size 18064
oid sha256:1f949b6ce193d360e9624b9e29e21413021828237de944c594beb5c4c3fb60e1
size 17320

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:28a69a52c07344576f2b5335497151e4e923b838dfaec9791402949ffb099c12
size 116116
oid sha256:a61c10399c3d48d2cbbb4c6cb3beeaf5b448d4a4eb56f6709ad22e2a67b6cff0
size 111994

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e1378d865af3df02e12a0c4bc087620a4e9ef0029221db3180cdd2fd34f69d7f
size 24832
oid sha256:17027c0e50ca6f3543897420cab3d94156c514160e7c4db4ecc5db470e801ee0
size 24014

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb3f7b5f790830b46d1410c2bbb5e19c6beb403f8fe979eb8d250fba4f89be3e
size 51670
oid sha256:1525cf27432b6d50a2dac99f400550f5018ddde8f62f77364c536455529e494b
size 49780

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:70b00222e6c63f97bfd8c7a179c15cfaba93f8f2566702d4b03997f4714fe6cb
size 22609
oid sha256:2777a8d4d64983512d42074129d1c9d3bde2e43ec9f3b3b929a2df5320eb01e6
size 21650

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ad26106a86a6236f0db1c51bed754b370530813e9bb6e36c1be2948820fbef25
size 47827
oid sha256:88a10a92d0fd0104c7883434b5e49f424f7100ab5013a456216c6bf5bb1e4076
size 45904

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:afa66ba8daca5c00f9c49b1d9173ad8f5e826247d3a9369d7e7c360cbdfcb72e
size 22928
oid sha256:48867e0418b2002c5e3f6fae69e6a30d0ef69b3d2dc98a1f7ee175064596450f
size 21879

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:170cee9d72a4ab59aa2faf1b77aff4a9eee64f3380aa3f1b256340d88b1dabc2
size 66525
oid sha256:08b787f4e579746d87338b669d5754f8fecfdd1cb7cf23f6f3dcd1e483054dcb
size 63759

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:90ab689d8a5034f5cab2ae2b44a8054d6dd815b3d295bee040c5bfcdf4564dee
size 33063
oid sha256:2ce9633fe06a9bd63d6219f0f7764fa5459a5441a35f385234aa5051495ad48a
size 32357

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf7f0a76424a959ede7afbb0eaf777638038cc6fe208ef710d9d82638d68b4d0
size 37848
oid sha256:dc89d49518a6a41c346cf9146657474f5b8898fa0f53e2aedc9c350c62865c41
size 36721

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2d2370972781f15a1d602deca28bca38f1c077152801870edf2112650b8b1349
size 17708
oid sha256:b040b83a49b599d0833ca3ce53ba974e0672e6a2eb1e711fc87e3a59718a7d89
size 17003

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f7a7d0e2618b852b5966073438c95cb62901d5410c1473639920b0b0bf2ec59b
size 256913
oid sha256:9da1fc5172d2d20ac44048f34b1cead35eb32a9bffebf8d9c031686880c767b7
size 247070

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f8b937a8a63de6fedcd0f9748b1d04cd863331a297bec78906885a0107def32a
size 61242
oid sha256:24088f20928106f51c38197b4c5d61d1c0d32371ca94018dc0f8b4f9e43700d0
size 53228

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a11b0aeb8b8a7ff3acd54b99869af03cd04cc2edf13afc713ce924c52d39262d
size 24826
oid sha256:ecfa78eda551ae362b65118b82054ec640ebd80ddcaf1eee5fbec002bba2bcde
size 24722

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2855bd95ab33b5232edada1f65684bbba2748025b6b64eb9ac68a5f2d10ad4bd
size 34491
oid sha256:dbbd302b6dcd22b89567747ad791f1d05ead01292fb7146ef8ab04e99e2a6c97
size 31886

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be599ae66323140bba4a7d63546acbf84340b57e2d82d4736bf3fe590040319d
size 23623
oid sha256:c9d4f953f7cffc647da604e32caa8b122aec6e940b9af38756c4f8746d1a1b31
size 22691

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01c3cb5e8972e0cab5325328f93af8f51b35a0d61016e74969eab0f7ddea1e02
size 176973
oid sha256:e2fa6340b31dfb2cf9b31b88391555357d84ec4bd062cb1039838d078af07c2f
size 170465

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f2f6cedc262259d52c1fbf4283d99b4b62ec732e8688b1e2799a2581425e0564
size 120342
oid sha256:d1a2c686b37d7d70d09a0236ce83e4c6eed6c72f8e3cc02331a99cc376115234
size 116410

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:142f65cee971f82a4917734c4f49ae233aa9a873028dca8c807d2825672bf2b2
size 26657
oid sha256:d3d99f7790cfe1eb9ff2e2f010756781bb5911cc8102c2d38ee3e82c04f8f944
size 25450

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72f4c6fe4f5ec243506152027e1150f3069caf98511ceef92b8fea4f6a1563d5
size 77614
oid sha256:ab7d1620779aa75f6596d9f84707533f7f6b04bb9c51d8dfea10cfb7abdb7b73
size 73525

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cc24f146adf0282cfb51723b56c76eceb92f2988fc67bbeefd16b93950505922
size 70110
oid sha256:af2373c1ff32cc520f64e14a29a0c386b14df7dba04f6c281c20b537016b98dc
size 68101

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:77aeaa1dcd391a571cb38732686e0b85b2d727975c02507a114d4e932f2c351b
size 65562
oid sha256:9b6eedb91e29999e0300707f88a44ffa941c72639a89383a507ed7e8c5d80731
size 59421

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5f964939ed1b3904706592915ca4fbbb951855ac88b466c51b835cd1c7467fb0
size 21501
oid sha256:c050180f968ac82287212f6f12eb242dd1266fd920f249cc6d48d8c9bfa1abe6
size 20814

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:415b1ce17dd6df7ca7a86fed92750c2ef811ff64720a447ae3ca6be10090666e
size 64624
oid sha256:c105e9267e81541d11e73a10fcb92c80ae8daeba6b9c586800b07264d5143071
size 61536

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0eaf717bf0083737c4186ac39e7baf98f42fffb36b49434a6658eff1430a0ac6
size 13187
oid sha256:185b62db2f890b05a3fb9029dae8e4452a37e3caae5c7b993c9995aefa078eff
size 12813

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:611a2d6c793a85eebe807b2ddd4446cc0bc21e4284343dd756e64f0232fb6815
size 35991
oid sha256:7425b60e0064dfd9e341fe55e059cbb6a372d526433cb3b1c234a105f16fd247
size 34520

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b85a2af24c3361a0008fd0996e8d7244dc3e289646ec7233e8bad39a586c871c
size 44512
oid sha256:4731c35a62a88533940c12a06cf8d31479c59a538ef69109b3a655e2d7ef3e43
size 42109

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:67b709c116f56fba7e4e9f182018e84f46f6c6dd33a51f9d0524125dc2056b8c
size 12950
oid sha256:89074b8dab103a419bc3dac743da4d8c47f435fa55b98d8aab71f6c9fb4d39de
size 12370

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4b0a70c0d66306edbbc6f77d03ea624aa68b846656811d4cc7d76d28572d177b
size 30723
oid sha256:968c478d986fc71d8655492b19e833ca07bc0ab85899dc04022bc7cf1dcf782f
size 29319

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:137ba69ac73a5e9aacc6bf3bbd589e8640b41c50ccfb49edcda4e2d6efed6c09
size 13384
oid sha256:7bd7b54ff60859e4d4793000bef3adbec4c071063bec6bfdbde62516c4fc3478
size 12959

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2f798c666ad21d3f9bb57826e30f2f6ef044543bc05af8c185e0e63c8297e824
size 33181
oid sha256:61e59f8360c567e20bf03b401362de7bb0f87716f13e817cc8da3df742ab68bf
size 31869

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:44fc7d745b478fe937fa7c131871a00b26712a0317aaa027a088782533be6136
size 7125
oid sha256:7389e319d9153af313cc113d97b57d462da00feb0d5f99da211552af3ac7e18a
size 6704

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:abfa00ef9385d380bbd188d6254f92d6839a94f368100e75a2780337438f969f
size 11068
oid sha256:d4480dd34ed36c6bdbc2084843dd136448b3934c22b3df3e40314ba6324b5b39
size 10306

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e5c9015e2005429ba83a407ed1f7d4dfbf30624f666152e82079c6ed3b3cda5
size 17238
oid sha256:624bfa884431c35cc5d852b96653f13da17e60f8545471f9fb1c3bb85b40ffc8
size 16555

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3319a8bca1213fc3a2dd91ead155be1e25045bc614701250bc961848cfc42176
size 7327
oid sha256:bf665389ef43524e097a7ae4eec0aa01bb788bdbb306144f20f9133f74a64b2c
size 6941

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5172aa12f07b4abf4bb217b8952b4cf5cf61b688455751964a1b54433d8c05b1
size 11709
oid sha256:a2480d0f49a929993de70612572b321457b2507c149a25112064cfc27840e6ee
size 11005

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e3eedd952d4416af73179451c0c90bcb76635c9c3c94d37f42bdd228ddbdd03
size 18802
oid sha256:1e0013b499934f47370a3a20b3d3a19f8a8c6db360752a35a3fb1d676d122263
size 18068

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60a44771c6bc9236593717236f9eca40bcb4723bac7567493cab4326f003eba3
size 48693
oid sha256:0054283b203602742d9819ba275c44ea40211ba18bf56fc66dca4fca766184d3
size 47076

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7bc441559ff2d8723cf344113ce5ff8158e41179e4c93abcacbe7b1b13b3723
size 48998
oid sha256:6a40e3e7314cb32398797665b2bebb237f1a9cc79e306df9c29f7f04faa3a435
size 47715

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e092be54efaeb700a63d9b679894647159f39a0d3062692ac7056e98242cbee
size 45364
oid sha256:b1ddff4f50b9a245d270cc13a0ebb9ac71d3590b83d401afb10ab107439cf235
size 43893

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:88930779ac199e42fcc9ee25f29bd120478c129807713218370b617905340087
size 45366
oid sha256:50dea7c459cf291e6c0b3166354a7e622af6682842ec36f295d532df8c064b38
size 43984

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9980486c36a0242f3b043a172c411d4fc9573543d3cd7c1c43bf020c18868a9
size 619816
oid sha256:db510af76578693c85ce78ca91224758a56f7bbf33db3221c9a4edca08b06600
size 590547

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:040e2e486ae4773a084da99513a53c620e8e2bba215183ec26c6e489381d6254
size 823086
oid sha256:cae2b789e8afff23b7545d42a530e6c972d28736bad2bdacbc69f0e7065f85cc
size 740660

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:439a5f942a5f05b9c09685ef90be94c150a21a68d1d235af22372b9b6a7b7389
size 1035734
oid sha256:09d9f567ec371d60881b525ddb462d9135552db97af5921a6eb02aba40e40616
size 971544

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a3a9aa8383abfe4580be2cc9987f8123aeabf36bf8ec06029a9af64b9500ec9
size 1206157
oid sha256:3c383dd89fda6094704027074a72085591339a276d60502626d78e8e527b2e10
size 1076719

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fedd5546e36a89121c0bb0a780b0bbe081611c2c04013064872801181503fb83
size 1231599
oid sha256:0b4559541cf3259496c760a26f8d83e82179cb7e4576333682c5af49ee4a35a7
size 1125331

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:69a7040336fc92c6d7b158283aabbc5817980c2fa84a1120b788516cf437b985
size 1461979
oid sha256:67c8412a1e8fdbfd88f8573797fbf6fbd89c6ce783a074a8e90f7d8d9e67dd57
size 1366351

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1a32d361afa20fc8c20122a89b01fe14b45849da42663991e589579f70fb8577
size 46790
oid sha256:a2b7b54a1af0f5cd31bd64f0506e3035dd423314ce3389e61730fa160434fbf3
size 45074

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c8d55205cf4225123da33895ed45eb186e5e57184ef5928400a4bae3ab6092be
size 88548
oid sha256:7b66a0be67ff2d684a54c2321123521b3ad06dfe5ebffd50e89260d77efcfcc4
size 86833

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:77ab9e2e18c788f8cdbec171269afe4d0a90c52abeda7063cae3766dcaa5e93b
size 120612
oid sha256:19320291c99a23429b114a59de4636689e281e1e68766abe2aa1e56562128e50
size 118919

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:36622a2f934503a7b60ded2f44b002e37eedde22d548dcf5a209f54c19548665
size 53064
oid sha256:5edf089c00715f1456fe7838e85aadcfc42b6216a3fd95b48d9c21fc8d700cba
size 51371

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4adeb7a77a0d0fe85097fcd190a99b49858dce11bde601d30335afcb6cc3d5f6
size 56276
oid sha256:6cd1a10639dcb323bdc3b2c43e0c35665184fc809731ced90088ee9edb9de845
size 54577

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f45249f7cc90433a64856905c727571c4ef20468e2c7d3ac2029e18a0477932d
size 56743
oid sha256:87e34024f701dc93f4026213ac7eb468a2cd6d3393eb0dbec382bf58007f8e61
size 55042

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d437f68c521f3e627a1b50d46605e8b0b343c5fc3716d9669e8c4436c8992b6f
size 37602
oid sha256:d7940ff56796efb27bec66b632ff33aa2ad390c4962a711bf520aee341f035a4
size 35968

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08ba98437403a08cca825ed8e288c9f088a46d9a1081f0b0a4ed7fbb9b49bb85
size 37640
oid sha256:b7bbd16c8aad444f0d11aacf87cf2292d494cc80a1ca46e7e8db86ca3041d35a
size 35931

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14f253fedc94985ff1431f1016d901d747e1f9948531cc6350f6615649f29056
size 4862
oid sha256:0475c5ac04ab8f79b79d43cfdb985f05b61dbe90e81f898a6dc216c308a28841
size 4707

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e1ed0e40d08b2b9ea978a07a4b7bf282c9e2cba8c52896f12210f396241e1b78
size 66859
oid sha256:bbdc4199dee2ae853b8a240cd84528482dc6762233bd0d1249f2daa296b49487
size 64172

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:28f9862dd6f16b99523f5880bf90346fd9455d0d44e7bdd530d523e0b2ef2d2c
size 158892
oid sha256:f6d38b6b47839d0e4eae530d203c83971fba8a41c9caa3d5b5d89ee7ed582613
size 150090

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:98c40c99a237f8388d82259fba4388d7b1759fe7b4bf657d326532934135c934
size 61119
oid sha256:c0635f1564d6c9707efa68003fb8c9b6eb00408aa8f24c972e33c6c79fed5bdf
size 59354

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:90d36311ce5b1dcf81cab22113620a3362f255059d1f52c6794c8249f960549e
size 152215
oid sha256:4288ee4a0d2229d59c31538179cdda50035a3849f69b400127e1618efe30cdc1
size 145224

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f297609dd69fd479377eaea7cd304b717e0523a650dbf76e19c6d1f1c5af1343
size 4518
oid sha256:3ca39801faddae7191ed054029263e8eca488d16e1fcbb40fed482d39fc89e8e
size 4520

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:42911cbb500fa49170aac0da8e4167641c5d7c9724a6accd4d400258fc74e2d7
size 8061
oid sha256:bafe5d7129cd2137b8f7bc9662b894d959b7042c436443f835ecd421a0d9c33f
size 8019

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:de4a197f82befde31b6966f902567c35cef96c7d541fd65b4207c693ab12bace
size 2036
oid sha256:cd2ff48cae729b3f957b1630bef23e94fb2176982c06ec3f123c1a0892fc536d
size 1959

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:94ba2e648c981bf4afbd9b9d01eef0708f7067be6e4cefbdfacc13aa219c6289
size 11253
oid sha256:27e4950f17ab7f68f7e001ca3a4f3fc18943103f4745c87715dcf6c097e92a57
size 11131

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:436999f511dce318f29172f0b7e2007e1f0fedae58f5e0e85e19f1d8e0bee361
size 22273
oid sha256:449cc473469dc80af81fe20e394dd90e67b4ae9c2033ebb7029726274d77d50c
size 21644

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:28435faf5c8c6d880cd50d52050c9f4cd6b992d0c621f01ca28fb5502eed16a1
size 29863
oid sha256:1cbfc767ed169cbddb3c90c2b455daefd85925501e7e33c7a25a34a72fc13eac
size 28512

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9a364b4b8c4ad3e78a80b0c6825d9de28c0e0d2e18dcfcd0ff18652ca86c859
size 34750
oid sha256:f4397b3d86bb5fe76d661cdb109ef71d43e771093bd9267b74722660d312ec7d
size 33253

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2fa5cb5b96232d729f89be8cc92263715fe7197e72343b71e57e53a359afe181
size 19881
oid sha256:e8038005841dbf272375388b224dcc9fc1177b5c113d3e6f6dbc2265c88c7e60
size 19704

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:87c76a9d07174e4e24ad3d08585c1df7bf3628bdc8f183d11beb6f9e14c4b2ec
size 2325
oid sha256:fad8b83e553ffa6bfbc4d47b955f2180859048c3789dda99b640e27665d216c7
size 2244

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f038c7fdf31f35107ec6e29edc0895049160ccbe98d1577c16ae082605b58d52
size 2207
oid sha256:ad75a0e568e04c20d0e3b823c7e4906c39dcd0a69a086d8e30714a9e4530d031
size 2128

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d94d4c3300d406fd1d93ddd90a9a46f99eac81a98a84f4d97a20fe4ef492e5d
size 5674
oid sha256:4216258893fae554f0ab8b3a76ef0905cacb62c70af47fa811ff6f3d99f9f3ab
size 5619

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:460cb2e9c91d139334c797829fd82fa77d593e9e58531e57a6b649c7e8fad228
size 7405
oid sha256:eb8737af84c3d3b0c054b7e2a8bcb04685243d84cb13b72a1372dc40dbfd14fb
size 7267

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:996985b155bd579cc4769d8cde5aa7e87c20ed909176da6b52dddeeb78a1cfba
size 8290
oid sha256:f1651bb1b9bbaa3c65ecd07c39c57527f4beb4c607581a5b2596a49dcf4c5db3
size 7996

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:025942c144891b8862bf931385824e0484e60f4e7766f5d4401511c72ff20756
size 2975
oid sha256:21a92c29e27ef0fdec273ea2d94a2b3e74cdf380ec77f4783daeb008bd51db6d
size 2767

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:025942c144891b8862bf931385824e0484e60f4e7766f5d4401511c72ff20756
size 2975
oid sha256:21a92c29e27ef0fdec273ea2d94a2b3e74cdf380ec77f4783daeb008bd51db6d
size 2767

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e269ede9c0784d00c153d51a13566d9c8f0d61ce11565997691fa63be06ec889
size 5075
oid sha256:f9ca5f8081d677b8bff47813c4eb94319ca03855e780aed834ecc2f3d905a22c
size 4852

View File

@@ -61,12 +61,14 @@ _override_unity = []
emath.workspace = true
ecolor.workspace = true
ab_glyph.workspace = true
ahash.workspace = true
log.workspace = true
nohash-hasher.workspace = true
parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
profiling = { workspace = true }
profiling.workspace = true
self_cell.workspace = true
skrifa.workspace = true
vello_cpu.workspace = true
#! ### Optional dependencies
bytemuck = { workspace = true, optional = true, features = ["derive"] }

View File

@@ -1,8 +1,8 @@
use criterion::{Criterion, criterion_group, criterion_main};
use epaint::{
AlphaFromCoverage, ClippedShape, Color32, Mesh, PathStroke, Pos2, Rect, Shape, Stroke,
TessellationOptions, Tessellator, TextureAtlas, Vec2, pos2, tessellator::Path,
ClippedShape, Color32, Mesh, PathStroke, Pos2, Rect, Shape, Stroke, TessellationOptions,
Tessellator, TextureAtlas, Vec2, pos2, tessellator::Path,
};
use std::hint::black_box;
@@ -68,7 +68,7 @@ fn tessellate_circles(c: &mut Criterion) {
let pixels_per_point = 2.0;
let options = TessellationOptions::default();
let atlas = TextureAtlas::new([4096, 256], AlphaFromCoverage::default());
let atlas = TextureAtlas::new([4096, 256], Default::default());
let font_tex_size = atlas.size();
let prepared_discs = atlas.prepared_discs();

View File

@@ -62,7 +62,7 @@ pub use self::{
stats::PaintStats,
stroke::{PathStroke, Stroke, StrokeKind},
tessellator::{TessellationOptions, Tessellator},
text::{FontFamily, FontId, Fonts, FontsView, Galley},
text::{FontFamily, FontId, Fonts, FontsView, Galley, TextOptions},
texture_atlas::TextureAtlas,
texture_handle::TextureHandle,
textures::TextureManager,

View File

@@ -185,11 +185,7 @@ mod tests {
#[test]
fn text_bounding_box_under_rotation() {
let mut fonts = Fonts::new(
1024,
AlphaFromCoverage::default(),
FontDefinitions::default(),
);
let mut fonts = Fonts::new(TextOptions::default(), FontDefinitions::default());
let font = FontId::monospace(12.0);
let mut t = crate::Shape::text(

View File

@@ -1,13 +1,20 @@
#![allow(clippy::mem_forget)]
use std::collections::BTreeMap;
use ab_glyph::{Font as _, OutlinedGlyph, PxScale};
use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};
use self_cell::self_cell;
use skrifa::{
MetadataProvider as _,
raw::{TableProvider as _, tables::kern::SubtableKind},
};
use vello_cpu::{color, kurbo};
use crate::{
TextureAtlas,
TextOptions, TextureAtlas,
text::{
FontTweak,
fonts::{CachedFamily, FontFaceKey},
fonts::{Blob, CachedFamily, FontFaceKey},
},
};
@@ -43,9 +50,9 @@ pub struct GlyphInfo {
/// Doesn't need to be unique.
///
/// Is `None` for a special "invisible" glyph.
pub(crate) id: Option<ab_glyph::GlyphId>,
pub(crate) id: Option<skrifa::GlyphId>,
/// In [`ab_glyph`]s "unscaled" coordinate system.
/// In [`skrifa`]s "unscaled" coordinate system.
pub advance_width_unscaled: OrderedFloat<f32>,
}
@@ -123,8 +130,8 @@ pub struct GlyphAllocation {
/// Used for pair-kerning.
///
/// Doesn't need to be unique.
/// Use `ab_glyph::GlyphId(0)` if you just want to have an id, and don't care.
pub(crate) id: ab_glyph::GlyphId,
/// Use [`skrifa::GlyphId::NOTDEF`] if you just want to have an id, and don't care.
pub(crate) id: skrifa::GlyphId,
/// Unit: screen pixels.
pub advance_width_px: f32,
@@ -139,7 +146,7 @@ struct GlyphCacheKey(u64);
impl nohash_hasher::IsEnabled for GlyphCacheKey {}
impl GlyphCacheKey {
fn new(glyph_id: ab_glyph::GlyphId, metrics: &ScaledMetrics, bin: SubpixelBin) -> Self {
fn new(glyph_id: skrifa::GlyphId, metrics: &ScaledMetrics, bin: SubpixelBin) -> Self {
let ScaledMetrics {
pixels_per_point,
px_scale_factor,
@@ -164,41 +171,229 @@ impl GlyphCacheKey {
// ----------------------------------------------------------------------------
struct DependentFontData<'a> {
skrifa: skrifa::FontRef<'a>,
charmap: skrifa::charmap::Charmap<'a>,
outline_glyphs: skrifa::outline::OutlineGlyphCollection<'a>,
metrics: skrifa::metrics::Metrics,
glyph_metrics: skrifa::metrics::GlyphMetrics<'a>,
hinting_instance: Option<skrifa::outline::HintingInstance>,
}
self_cell! {
struct FontCell {
owner: Blob,
#[covariant]
dependent: DependentFontData,
}
}
impl FontCell {
fn px_scale_factor(&self, scale: f32) -> f32 {
let units_per_em = self.borrow_dependent().metrics.units_per_em as f32;
scale / units_per_em
}
fn allocate_glyph_uncached(
&mut self,
atlas: &mut TextureAtlas,
metrics: &ScaledMetrics,
glyph_info: &GlyphInfo,
bin: SubpixelBin,
) -> Option<GlyphAllocation> {
let glyph_id = glyph_info.id?;
debug_assert!(
glyph_id != skrifa::GlyphId::NOTDEF,
"Can't allocate glyph for id 0"
);
let mut path = kurbo::BezPath::new();
let mut pen = VelloPen {
path: &mut path,
x_offset: bin.as_float() as f64,
};
self.with_dependent_mut(|_, font_data| {
let outline = font_data.outline_glyphs.get(glyph_id)?;
if let Some(hinting_instance) = &mut font_data.hinting_instance {
let size = skrifa::instance::Size::new(metrics.scale);
if hinting_instance.size() != size {
hinting_instance
.reconfigure(
&font_data.outline_glyphs,
size,
skrifa::instance::LocationRef::default(),
skrifa::outline::Target::Smooth {
mode: skrifa::outline::SmoothMode::Normal,
symmetric_rendering: true,
preserve_linear_metrics: true,
},
)
.ok()?;
}
let draw_settings = skrifa::outline::DrawSettings::hinted(hinting_instance, false);
outline.draw(draw_settings, &mut pen).ok()?;
} else {
let draw_settings = skrifa::outline::DrawSettings::unhinted(
skrifa::instance::Size::new(metrics.scale),
skrifa::instance::LocationRef::default(),
);
outline.draw(draw_settings, &mut pen).ok()?;
}
Some(())
})?;
let bounds = path.control_box().expand();
let width = bounds.width() as u16;
let height = bounds.height() as u16;
let mut ctx = vello_cpu::RenderContext::new(width, height);
ctx.set_transform(kurbo::Affine::translate((-bounds.x0, -bounds.y0)));
ctx.set_paint(color::OpaqueColor::<color::Srgb>::WHITE);
ctx.fill_path(&path);
let mut dest = vello_cpu::Pixmap::new(width, height);
ctx.render_to_pixmap(&mut dest);
let uv_rect = if width == 0 || height == 0 {
UvRect::default()
} else {
let glyph_pos = {
let alpha_from_coverage = atlas.options().alpha_from_coverage;
let (glyph_pos, image) = atlas.allocate((width as usize, height as usize));
let pixels = dest.data_as_u8_slice();
for y in 0..height as usize {
for x in 0..width as usize {
image[(x + glyph_pos.0, y + glyph_pos.1)] = alpha_from_coverage
.color_from_coverage(
pixels[((y * width as usize) + x) * 4 + 3] as f32 / 255.0,
);
}
}
glyph_pos
};
let offset_in_pixels = vec2(bounds.x0 as f32, bounds.y0 as f32);
let offset =
offset_in_pixels / metrics.pixels_per_point + metrics.y_offset_in_points * Vec2::Y;
UvRect {
offset,
size: vec2(width as f32, height as f32) / metrics.pixels_per_point,
min: [glyph_pos.0 as u16, glyph_pos.1 as u16],
max: [
(glyph_pos.0 + width as usize) as u16,
(glyph_pos.1 + height as usize) as u16,
],
}
};
Some(GlyphAllocation {
id: glyph_id,
advance_width_px: glyph_info.advance_width_unscaled.0 * metrics.px_scale_factor,
uv_rect,
})
}
}
struct VelloPen<'a> {
path: &'a mut kurbo::BezPath,
x_offset: f64,
}
impl skrifa::outline::OutlinePen for VelloPen<'_> {
fn move_to(&mut self, x: f32, y: f32) {
self.path.move_to((x as f64 + self.x_offset, -y as f64));
}
fn line_to(&mut self, x: f32, y: f32) {
self.path.line_to((x as f64 + self.x_offset, -y as f64));
}
fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
self.path.quad_to(
(cx0 as f64 + self.x_offset, -cy0 as f64),
(x as f64 + self.x_offset, -y as f64),
);
}
fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
self.path.curve_to(
(cx0 as f64 + self.x_offset, -cy0 as f64),
(cx1 as f64 + self.x_offset, -cy1 as f64),
(x as f64 + self.x_offset, -y as f64),
);
}
fn close(&mut self) {
self.path.close_path();
}
}
/// A specific font face.
/// The interface uses points as the unit for everything.
pub struct FontImpl {
pub struct FontFace {
name: String,
ab_glyph_font: ab_glyph::FontArc,
font: FontCell,
tweak: FontTweak,
glyph_info_cache: ahash::HashMap<char, GlyphInfo>,
glyph_alloc_cache: ahash::HashMap<GlyphCacheKey, GlyphAllocation>,
}
trait FontExt {
fn px_scale_factor(&self, scale: f32) -> f32;
}
impl FontFace {
pub fn new(
options: TextOptions,
name: String,
font_data: Blob,
index: u32,
tweak: FontTweak,
) -> Result<Self, Box<dyn std::error::Error>> {
let font = FontCell::try_new(font_data, |font_data| {
let skrifa_font =
skrifa::FontRef::from_index(AsRef::<[u8]>::as_ref(font_data.as_ref()), index)?;
impl<T> FontExt for T
where
T: ab_glyph::Font,
{
fn px_scale_factor(&self, scale: f32) -> f32 {
let units_per_em = self.units_per_em().unwrap_or_else(|| {
panic!("The font unit size exceeds the expected range (16..=16384)")
});
scale / units_per_em
}
}
let charmap = skrifa_font.charmap();
let glyphs = skrifa_font.outline_glyphs();
let metrics = skrifa_font.metrics(
skrifa::instance::Size::unscaled(),
skrifa::instance::LocationRef::default(),
);
let glyph_metrics = skrifa_font.glyph_metrics(
skrifa::instance::Size::unscaled(),
skrifa::instance::LocationRef::default(),
);
impl FontImpl {
pub fn new(name: String, ab_glyph_font: ab_glyph::FontArc, tweak: FontTweak) -> Self {
Self {
let hinting_enabled = tweak.hinting_override.unwrap_or(options.font_hinting);
let hinting_instance = hinting_enabled
.then(|| {
// It doesn't really matter what we put here for options. Since the size is `unscaled()`, we will
// always reconfigure this hinting instance with the real options when rendering for the first time.
skrifa::outline::HintingInstance::new(
&glyphs,
skrifa::instance::Size::unscaled(),
skrifa::instance::LocationRef::default(),
skrifa::outline::Target::default(),
)
.ok()
})
.flatten();
Ok::<DependentFontData<'_>, Box<dyn std::error::Error>>(DependentFontData {
skrifa: skrifa_font,
charmap,
outline_glyphs: glyphs,
metrics,
glyph_metrics,
hinting_instance,
})
})?;
Ok(Self {
name,
ab_glyph_font,
font,
tweak,
glyph_info_cache: Default::default(),
glyph_alloc_cache: Default::default(),
}
})
}
/// Code points that will always be replaced by the replacement character.
@@ -223,10 +418,11 @@ impl FontImpl {
/// An un-ordered iterator over all supported characters.
fn characters(&self) -> impl Iterator<Item = char> + '_ {
self.ab_glyph_font
.codepoint_ids()
.map(|(_, chr)| chr)
.filter(|&chr| !self.ignore_character(chr))
self.font
.borrow_dependent()
.charmap
.mappings()
.filter_map(|(chr, _)| char::from_u32(chr).filter(|c| !self.ignore_character(*c)))
}
/// `\n` will result in `None`
@@ -258,7 +454,7 @@ impl FontImpl {
// https://en.wikipedia.org/wiki/Thin_space
if let Some(space) = self.glyph_info(' ') {
let em = self.ab_glyph_font.units_per_em().unwrap_or(1.0);
let em = self.font.borrow_dependent().metrics.units_per_em as f32;
let advance_width = f32::min(em / 6.0, space.advance_width_unscaled.0 * 0.5); // TODO(emilk): make configurable
let glyph_info = GlyphInfo {
advance_width_unscaled: advance_width.into(),
@@ -275,52 +471,68 @@ impl FontImpl {
return Some(glyph_info);
}
// Add new character:
let glyph_id = self.ab_glyph_font.glyph_id(c);
let font_data = self.font.borrow_dependent();
if glyph_id.0 == 0 {
None // unsupported character
} else {
let glyph_info = GlyphInfo {
id: Some(glyph_id),
advance_width_unscaled: self.ab_glyph_font.h_advance_unscaled(glyph_id).into(),
};
self.glyph_info_cache.insert(c, glyph_info);
Some(glyph_info)
}
// Add new character:
let glyph_id = font_data
.charmap
.map(c)
.filter(|id| *id != skrifa::GlyphId::NOTDEF)?;
let glyph_info = GlyphInfo {
id: Some(glyph_id),
advance_width_unscaled: font_data
.glyph_metrics
.advance_width(glyph_id)
.unwrap_or_default()
.into(),
};
self.glyph_info_cache.insert(c, glyph_info);
Some(glyph_info)
}
#[inline]
pub(super) fn pair_kerning_pixels(
&self,
metrics: &ScaledMetrics,
last_glyph_id: ab_glyph::GlyphId,
glyph_id: ab_glyph::GlyphId,
last_glyph_id: skrifa::GlyphId,
glyph_id: skrifa::GlyphId,
) -> f32 {
self.ab_glyph_font.kern_unscaled(last_glyph_id, glyph_id) * metrics.px_scale_factor
let skrifa_font = &self.font.borrow_dependent().skrifa;
let Ok(kern) = skrifa_font.kern() else {
return 0.0;
};
kern.subtables()
.find_map(|st| match st.ok()?.kind().ok()? {
SubtableKind::Format0(table_ref) => table_ref.kerning(last_glyph_id, glyph_id),
SubtableKind::Format1(_) => None,
SubtableKind::Format2(subtable2) => subtable2.kerning(last_glyph_id, glyph_id),
SubtableKind::Format3(table_ref) => table_ref.kerning(last_glyph_id, glyph_id),
})
.unwrap_or_default() as f32
* metrics.px_scale_factor
}
#[inline]
pub fn pair_kerning(
&self,
metrics: &ScaledMetrics,
last_glyph_id: ab_glyph::GlyphId,
glyph_id: ab_glyph::GlyphId,
last_glyph_id: skrifa::GlyphId,
glyph_id: skrifa::GlyphId,
) -> f32 {
self.pair_kerning_pixels(metrics, last_glyph_id, glyph_id) / metrics.pixels_per_point
}
#[inline(always)]
pub fn scaled_metrics(&self, pixels_per_point: f32, font_size: f32) -> ScaledMetrics {
let pt_scale_factor = self
.ab_glyph_font
.px_scale_factor(font_size * self.tweak.scale);
let ascent = (self.ab_glyph_font.ascent_unscaled() * pt_scale_factor).round_ui();
let descent = (self.ab_glyph_font.descent_unscaled() * pt_scale_factor).round_ui();
let line_gap = (self.ab_glyph_font.line_gap_unscaled() * pt_scale_factor).round_ui();
let pt_scale_factor = self.font.px_scale_factor(font_size * self.tweak.scale);
let font_data = self.font.borrow_dependent();
let ascent = (font_data.metrics.ascent * pt_scale_factor).round_ui();
let descent = (font_data.metrics.descent * pt_scale_factor).round_ui();
let line_gap = (font_data.metrics.leading * pt_scale_factor).round_ui();
let scale = font_size * self.tweak.scale * pixels_per_point;
let px_scale_factor = self.ab_glyph_font.px_scale_factor(scale);
let px_scale_factor = self.font.px_scale_factor(scale);
let y_offset_in_points = ((font_size * self.tweak.scale * self.tweak.y_offset_factor)
+ self.tweak.y_offset)
@@ -329,6 +541,7 @@ impl FontImpl {
ScaledMetrics {
pixels_per_point,
px_scale_factor,
scale,
y_offset_in_points,
ascent,
row_height: ascent - descent + line_gap,
@@ -370,77 +583,20 @@ impl FontImpl {
std::collections::hash_map::Entry::Vacant(entry) => entry,
};
debug_assert!(glyph_id.0 != 0, "Can't allocate glyph for id 0");
let allocation = self
.font
.allocate_glyph_uncached(atlas, metrics, &glyph_info, bin)
.unwrap_or_default();
let uv_rect = self.ab_glyph_font.outline(glyph_id).map(|outline| {
let glyph = ab_glyph::Glyph {
id: glyph_id,
// We bypass ab-glyph's scaling method because it uses the wrong scale
// (https://github.com/alexheretic/ab-glyph/issues/15), and this field is never accessed when
// rasterizing. We can just put anything here.
scale: PxScale::from(0.0),
position: ab_glyph::Point {
x: bin.as_float(),
y: 0.0,
},
};
let outlined = OutlinedGlyph::new(
glyph,
outline,
ab_glyph::PxScaleFactor {
horizontal: metrics.px_scale_factor,
vertical: metrics.px_scale_factor,
},
);
let bb = outlined.px_bounds();
let glyph_width = bb.width() as usize;
let glyph_height = bb.height() as usize;
if glyph_width == 0 || glyph_height == 0 {
UvRect::default()
} else {
let glyph_pos = {
let text_alpha_from_coverage = atlas.text_alpha_from_coverage;
let (glyph_pos, image) = atlas.allocate((glyph_width, glyph_height));
outlined.draw(|x, y, v| {
if 0.0 < v {
let px = glyph_pos.0 + x as usize;
let py = glyph_pos.1 + y as usize;
image[(px, py)] = text_alpha_from_coverage.color_from_coverage(v);
}
});
glyph_pos
};
let offset_in_pixels = vec2(bb.min.x, bb.min.y);
let offset = offset_in_pixels / metrics.pixels_per_point
+ metrics.y_offset_in_points * Vec2::Y;
UvRect {
offset,
size: vec2(glyph_width as f32, glyph_height as f32) / metrics.pixels_per_point,
min: [glyph_pos.0 as u16, glyph_pos.1 as u16],
max: [
(glyph_pos.0 + glyph_width) as u16,
(glyph_pos.1 + glyph_height) as u16,
],
}
}
});
let uv_rect = uv_rect.unwrap_or_default();
let allocation = GlyphAllocation {
id: glyph_id,
advance_width_px,
uv_rect,
};
entry.insert(allocation);
(allocation, h_pos_round)
}
}
// TODO(emilk): rename?
/// Wrapper over multiple [`FontImpl`] (e.g. a primary + fallbacks for emojis)
/// Wrapper over multiple [`FontFace`] (e.g. a primary + fallbacks for emojis)
pub struct Font<'a> {
pub(super) fonts_by_id: &'a mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
pub(super) fonts_by_id: &'a mut nohash_hasher::IntMap<FontFaceKey, FontFace>,
pub(super) cached_family: &'a mut CachedFamily,
pub(super) atlas: &'a mut TextureAtlas,
}
@@ -471,7 +627,7 @@ impl Font<'_> {
.fonts
.first()
.and_then(|key| self.fonts_by_id.get(key))
.map(|font_impl| font_impl.scaled_metrics(pixels_per_point, font_size))
.map(|font_face| font_face.scaled_metrics(pixels_per_point, font_size))
.unwrap_or_default()
}
@@ -479,7 +635,7 @@ impl Font<'_> {
pub fn glyph_width(&mut self, c: char, font_size: f32) -> f32 {
let (key, glyph_info) = self.glyph_info(c);
if let Some(font) = &self.fonts_by_id.get(&key) {
glyph_info.advance_width_unscaled.0 * font.ab_glyph_font.px_scale_factor(font_size)
glyph_info.advance_width_unscaled.0 * font.font.px_scale_factor(font_size)
} else {
0.0
}
@@ -524,7 +680,10 @@ pub struct ScaledMetrics {
/// Translates "unscaled" units to physical (screen) pixels.
pub px_scale_factor: f32,
/// Vertical offset, in UI points.
/// Absolute scale in screen pixels, for skrifa.
pub scale: f32,
/// Vertical offset, in UI points (not screen-space).
pub y_offset_in_points: f32,
/// This is the distance from the top to the baseline.
@@ -540,7 +699,7 @@ pub struct ScaledMetrics {
/// Code points that will always be invisible (zero width).
///
/// See also [`FontImpl::ignore_character`].
/// See also [`FontFace::ignore_character`].
#[inline]
fn invisible_char(c: char) -> bool {
if c == '\r' {

View File

@@ -1,4 +1,5 @@
use std::{
borrow::Cow,
collections::BTreeMap,
sync::{
Arc,
@@ -7,10 +8,10 @@ use std::{
};
use crate::{
AlphaFromCoverage, TextureAtlas,
TextureAtlas,
text::{
Galley, LayoutJob, LayoutSection,
font::{Font, FontImpl, GlyphInfo},
Galley, LayoutJob, LayoutSection, TextOptions,
font::{Font, FontFace, GlyphInfo},
},
};
use emath::{NumExt as _, OrderedFloat};
@@ -116,7 +117,7 @@ impl std::fmt::Display for FontFamily {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct FontData {
/// The content of a `.ttf` or `.otf` file.
pub font: std::borrow::Cow<'static, [u8]>,
pub font: Cow<'static, [u8]>,
/// Which font face in the file to use.
/// When in doubt, use `0`.
@@ -129,7 +130,7 @@ pub struct FontData {
impl FontData {
pub fn from_static(font: &'static [u8]) -> Self {
Self {
font: std::borrow::Cow::Borrowed(font),
font: Cow::Borrowed(font),
index: 0,
tweak: Default::default(),
}
@@ -137,7 +138,7 @@ impl FontData {
pub fn from_owned(font: Vec<u8>) -> Self {
Self {
font: std::borrow::Cow::Owned(font),
font: Cow::Owned(font),
index: 0,
tweak: Default::default(),
}
@@ -184,6 +185,11 @@ pub struct FontTweak {
///
/// Example value: `2.0`.
pub y_offset: f32,
/// Override the global font hinting setting for this specific font.
///
/// `None` means use the global setting.
pub hinting_override: Option<bool>,
}
impl Default for FontTweak {
@@ -192,24 +198,20 @@ impl Default for FontTweak {
scale: 1.0,
y_offset_factor: 0.0,
y_offset: 0.0,
hinting_override: None,
}
}
}
// ----------------------------------------------------------------------------
fn ab_glyph_font_from_font_data(name: &str, data: &FontData) -> ab_glyph::FontArc {
match &data.font {
std::borrow::Cow::Borrowed(bytes) => {
ab_glyph::FontRef::try_from_slice_and_index(bytes, data.index)
.map(ab_glyph::FontArc::from)
}
std::borrow::Cow::Owned(bytes) => {
ab_glyph::FontVec::try_from_vec_and_index(bytes.clone(), data.index)
.map(ab_glyph::FontArc::from)
}
pub type Blob = Arc<dyn AsRef<[u8]> + Send + Sync>;
fn blob_from_font_data(data: &FontData) -> Blob {
match data.clone().font {
Cow::Borrowed(bytes) => Arc::new(bytes) as Blob,
Cow::Owned(bytes) => Arc::new(bytes) as Blob,
}
.unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"))
}
/// Describes the font data and the sizes to use.
@@ -438,7 +440,7 @@ pub(super) struct CachedFamily {
impl CachedFamily {
fn new(
fonts: Vec<FontFaceKey>,
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontFace>,
) -> Self {
if fonts.is_empty() {
return Self {
@@ -476,11 +478,11 @@ impl CachedFamily {
pub(crate) fn glyph_info_no_cache_or_fallback(
&mut self,
c: char,
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontFace>,
) -> Option<(FontFaceKey, GlyphInfo)> {
for font_key in &self.fonts {
let font_impl = fonts_by_id.get_mut(font_key).expect("Nonexistent font ID");
if let Some(glyph_info) = font_impl.glyph_info(c) {
let font_face = fonts_by_id.get_mut(font_key).expect("Nonexistent font ID");
if let Some(glyph_info) = font_face.glyph_info(c) {
self.glyph_info_cache.insert(c, (*font_key, glyph_info));
return Some((*font_key, glyph_info));
}
@@ -508,43 +510,29 @@ pub struct Fonts {
impl Fonts {
/// Create a new [`Fonts`] for text layout.
/// This call is expensive, so only create one [`Fonts`] and then reuse it.
///
/// * `max_texture_side`: largest supported texture size (one side).
pub fn new(
max_texture_side: usize,
text_alpha_from_coverage: AlphaFromCoverage,
definitions: FontDefinitions,
) -> Self {
pub fn new(options: TextOptions, definitions: FontDefinitions) -> Self {
Self {
fonts: FontsImpl::new(max_texture_side, text_alpha_from_coverage, definitions),
fonts: FontsImpl::new(options, definitions),
galley_cache: Default::default(),
}
}
/// Call at the start of each frame with the latest known
/// `pixels_per_point`, `max_texture_side`, and `text_alpha_from_coverage`.
/// Call at the start of each frame with the latest known [`TextOptions`].
///
/// Call after painting the previous frame, but before using [`Fonts`] for the new frame.
///
/// This function will react to changes in `pixels_per_point`, `max_texture_side`, and `text_alpha_from_coverage`,
/// This function will react to changes in [`TextOptions`],
/// as well as notice when the font atlas is getting full, and handle that.
pub fn begin_pass(
&mut self,
max_texture_side: usize,
text_alpha_from_coverage: AlphaFromCoverage,
) {
let max_texture_side_changed = self.fonts.max_texture_side != max_texture_side;
let text_alpha_from_coverage_changed =
self.fonts.atlas.text_alpha_from_coverage != text_alpha_from_coverage;
pub fn begin_pass(&mut self, options: TextOptions) {
let text_options_changed = self.fonts.options() != &options;
let font_atlas_almost_full = self.fonts.atlas.fill_ratio() > 0.8;
let needs_recreate =
max_texture_side_changed || text_alpha_from_coverage_changed || font_atlas_almost_full;
let needs_recreate = text_options_changed || font_atlas_almost_full;
if needs_recreate {
let definitions = self.fonts.definitions.clone();
*self = Self {
fonts: FontsImpl::new(max_texture_side, text_alpha_from_coverage, definitions),
fonts: FontsImpl::new(options, definitions),
galley_cache: Default::default(),
};
}
@@ -558,8 +546,8 @@ impl Fonts {
}
#[inline]
pub fn max_texture_side(&self) -> usize {
self.fonts.max_texture_side
pub fn options(&self) -> &TextOptions {
self.texture_atlas().options()
}
#[inline]
@@ -628,8 +616,8 @@ pub struct FontsView<'a> {
impl FontsView<'_> {
#[inline]
pub fn max_texture_side(&self) -> usize {
self.fonts.max_texture_side
pub fn options(&self) -> &TextOptions {
self.fonts.options()
}
#[inline]
@@ -671,6 +659,7 @@ impl FontsView<'_> {
/// Height of one row of text in points.
///
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
#[inline]
pub fn row_height(&mut self, font_id: &FontId) -> f32 {
self.fonts
.font(&font_id.family)
@@ -716,6 +705,7 @@ impl FontsView<'_> {
/// Will wrap text at the given width and line break at `\n`.
///
/// The implementation uses memoization so repeated calls are cheap.
#[inline]
pub fn layout(
&mut self,
text: String,
@@ -730,6 +720,7 @@ impl FontsView<'_> {
/// Will line break at `\n`.
///
/// The implementation uses memoization so repeated calls are cheap.
#[inline]
pub fn layout_no_wrap(
&mut self,
text: String,
@@ -743,6 +734,7 @@ impl FontsView<'_> {
/// Like [`Self::layout`], made for when you want to pick a color for the text later.
///
/// The implementation uses memoization so repeated calls are cheap.
#[inline]
pub fn layout_delayed_color(
&mut self,
text: String,
@@ -759,10 +751,9 @@ impl FontsView<'_> {
///
/// Required in order to paint text.
pub struct FontsImpl {
max_texture_side: usize,
definitions: FontDefinitions,
atlas: TextureAtlas,
fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontImpl>,
fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontFace>,
fonts_by_name: ahash::HashMap<String, FontFaceKey>,
family_cache: ahash::HashMap<FontFamily, CachedFamily>,
}
@@ -770,36 +761,36 @@ pub struct FontsImpl {
impl FontsImpl {
/// Create a new [`FontsImpl`] for text layout.
/// This call is expensive, so only create one [`FontsImpl`] and then reuse it.
pub fn new(
max_texture_side: usize,
text_alpha_from_coverage: AlphaFromCoverage,
definitions: FontDefinitions,
) -> Self {
let texture_width = max_texture_side.at_most(16 * 1024);
pub fn new(options: TextOptions, definitions: FontDefinitions) -> Self {
let texture_width = options.max_texture_side.at_most(16 * 1024);
let initial_height = 32; // Keep initial font atlas small, so it is fast to upload to GPU. This will expand as needed anyways.
let atlas = TextureAtlas::new([texture_width, initial_height], text_alpha_from_coverage);
let atlas = TextureAtlas::new([texture_width, initial_height], options);
let mut fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontImpl> = Default::default();
let mut font_impls: ahash::HashMap<String, FontFaceKey> = Default::default();
let mut fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontFace> = Default::default();
let mut fonts_by_name: ahash::HashMap<String, FontFaceKey> = Default::default();
for (name, font_data) in &definitions.font_data {
let tweak = font_data.tweak;
let ab_glyph = ab_glyph_font_from_font_data(name, font_data);
let font_impl = FontImpl::new(name.clone(), ab_glyph, tweak);
let blob = blob_from_font_data(font_data);
let font_face = FontFace::new(options, name.clone(), blob, font_data.index, tweak)
.unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"));
let key = FontFaceKey::new();
fonts_by_id.insert(key, font_impl);
font_impls.insert(name.clone(), key);
fonts_by_id.insert(key, font_face);
fonts_by_name.insert(name.clone(), key);
}
Self {
max_texture_side,
definitions,
atlas,
fonts_by_id,
fonts_by_name: font_impls,
fonts_by_name,
family_cache: Default::default(),
}
}
pub fn options(&self) -> &TextOptions {
self.atlas.options()
}
/// Get the right font implementation from [`FontFamily`].
pub fn font(&mut self, family: &FontFamily) -> Font<'_> {
let cached_family = self.family_cache.entry(family.clone()).or_insert_with(|| {
@@ -1192,12 +1183,7 @@ mod tests {
#[test]
fn test_split_paragraphs() {
for pixels_per_point in [1.0, 2.0_f32.sqrt(), 2.0] {
let max_texture_side = 4096;
let mut fonts = FontsImpl::new(
max_texture_side,
AlphaFromCoverage::default(),
FontDefinitions::default(),
);
let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());
for halign in [Align::Min, Align::Center, Align::Max] {
for justify in [false, true] {
@@ -1255,11 +1241,7 @@ mod tests {
let rounded_output_to_gui = [false, true];
for pixels_per_point in pixels_per_point {
let mut fonts = FontsImpl::new(
1024,
AlphaFromCoverage::default(),
FontDefinitions::default(),
);
let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());
for &max_width in &max_widths {
for round_output_to_gui in rounded_output_to_gui {
@@ -1306,7 +1288,7 @@ mod tests {
#[test]
fn test_fallback_glyph_width() {
let mut fonts = Fonts::new(1024, AlphaFromCoverage::default(), FontDefinitions::empty());
let mut fonts = Fonts::new(TextOptions::default(), FontDefinitions::empty());
let mut view = fonts.with_pixels_per_point(1.0);
let width = view.glyph_width(&FontId::new(12.0, FontFamily::Proportional), ' ');

Some files were not shown because too many files have changed in this diff Show More