mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
Remove more Arc<Mutex<...>> from font code
By making `Font` a view type and indexing by font ID, we can avoid wrapping `FontImpl` and `TextureAtlas` in an `Arc<Mutex<...>>`.
This commit is contained in:
@@ -1588,7 +1588,7 @@ impl Context {
|
||||
|
||||
let font_id = TextStyle::Body.resolve(&self.style());
|
||||
self.fonts_mut(|f| {
|
||||
let font = f.fonts.font(&font_id.family);
|
||||
let mut font = f.fonts.font(&font_id.family);
|
||||
font.has_glyphs(alt)
|
||||
&& font.has_glyphs(ctrl)
|
||||
&& font.has_glyphs(shift)
|
||||
@@ -2466,14 +2466,15 @@ impl ContextImpl {
|
||||
|
||||
self.memory.end_pass(&viewport.this_pass.used_ids);
|
||||
|
||||
if let Some(fonts) = self.fonts.get(&pixels_per_point.into()) {
|
||||
let num_font_envs = self.fonts.len();
|
||||
if let Some(fonts) = self.fonts.get_mut(&pixels_per_point.into()) {
|
||||
let tex_mngr = &mut self.tex_manager.0.write();
|
||||
if let Some(font_image_delta) = fonts.font_image_delta() {
|
||||
// A partial font atlas update, e.g. a new glyph has been entered.
|
||||
tex_mngr.set(TextureId::default(), font_image_delta);
|
||||
}
|
||||
|
||||
if 1 < self.fonts.len() {
|
||||
if 1 < num_font_envs {
|
||||
// We have multiple different `pixels_per_point`,
|
||||
// e.g. because we have many viewports spread across
|
||||
// monitors with different DPI scaling.
|
||||
@@ -2693,10 +2694,6 @@ impl Context {
|
||||
.1
|
||||
.texture_atlas()
|
||||
};
|
||||
let (font_tex_size, prepared_discs) = {
|
||||
let atlas = texture_atlas.lock();
|
||||
(atlas.size(), atlas.prepared_discs())
|
||||
};
|
||||
|
||||
let paint_stats = PaintStats::from_shapes(&shapes);
|
||||
let clipped_primitives = {
|
||||
@@ -2704,8 +2701,8 @@ impl Context {
|
||||
tessellator::Tessellator::new(
|
||||
pixels_per_point,
|
||||
tessellation_options,
|
||||
font_tex_size,
|
||||
prepared_discs,
|
||||
texture_atlas.size(),
|
||||
texture_atlas.prepared_discs(),
|
||||
)
|
||||
.tessellate_shapes(shapes)
|
||||
};
|
||||
|
||||
@@ -227,7 +227,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, text_color, wrap_width);
|
||||
let font_image_size = fonts.font_image_size();
|
||||
let prepared_discs = fonts.texture_atlas().lock().prepared_discs();
|
||||
let prepared_discs = fonts.texture_atlas().prepared_discs();
|
||||
let mut tessellator = egui::epaint::Tessellator::new(
|
||||
1.0,
|
||||
Default::default(),
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ab_glyph::{Font as _, PxScaleFont, ScaleFont as _};
|
||||
use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};
|
||||
|
||||
use crate::{
|
||||
TextureAtlas,
|
||||
mutex::{Mutex, RwLock},
|
||||
text::FontTweak,
|
||||
text::{
|
||||
FontTweak,
|
||||
fonts::{CachedFamily, FontFaceKey},
|
||||
},
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -84,9 +85,8 @@ pub struct FontImpl {
|
||||
name: String,
|
||||
ab_glyph_font: ab_glyph::FontArc,
|
||||
tweak: FontTweak,
|
||||
glyph_info_cache: RwLock<ahash::HashMap<char, GlyphInfo>>, // TODO(emilk): standard Mutex
|
||||
glyph_alloc_cache: RwLock<ahash::HashMap<(GlyphInfo, OrderedFloat<f32>), GlyphAllocation>>, // TODO(emilk): standard Mutex
|
||||
atlas: Arc<Mutex<TextureAtlas>>,
|
||||
glyph_info_cache: ahash::HashMap<char, GlyphInfo>,
|
||||
glyph_alloc_cache: ahash::HashMap<(GlyphInfo, OrderedFloat<f32>), GlyphAllocation>,
|
||||
}
|
||||
|
||||
trait FontExt {
|
||||
@@ -116,19 +116,13 @@ where
|
||||
}
|
||||
|
||||
impl FontImpl {
|
||||
pub fn new(
|
||||
atlas: Arc<Mutex<TextureAtlas>>,
|
||||
name: String,
|
||||
ab_glyph_font: ab_glyph::FontArc,
|
||||
tweak: FontTweak,
|
||||
) -> Self {
|
||||
pub fn new(name: String, ab_glyph_font: ab_glyph::FontArc, tweak: FontTweak) -> Self {
|
||||
Self {
|
||||
name,
|
||||
ab_glyph_font,
|
||||
tweak,
|
||||
glyph_info_cache: Default::default(),
|
||||
glyph_alloc_cache: Default::default(),
|
||||
atlas,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,9 +155,9 @@ impl FontImpl {
|
||||
}
|
||||
|
||||
/// `\n` will result in `None`
|
||||
fn glyph_info(&self, c: char) -> Option<GlyphInfo> {
|
||||
fn glyph_info(&mut self, c: char) -> Option<GlyphInfo> {
|
||||
{
|
||||
if let Some(glyph_info) = self.glyph_info_cache.read().get(&c) {
|
||||
if let Some(glyph_info) = self.glyph_info_cache.get(&c) {
|
||||
return Some(*glyph_info);
|
||||
}
|
||||
}
|
||||
@@ -180,7 +174,7 @@ impl FontImpl {
|
||||
.into(),
|
||||
..space
|
||||
};
|
||||
self.glyph_info_cache.write().insert(c, glyph_info);
|
||||
self.glyph_info_cache.insert(c, glyph_info);
|
||||
return Some(glyph_info);
|
||||
}
|
||||
}
|
||||
@@ -197,14 +191,14 @@ impl FontImpl {
|
||||
advance_width_unscaled: advance_width.into(),
|
||||
..space
|
||||
};
|
||||
self.glyph_info_cache.write().insert(c, glyph_info);
|
||||
self.glyph_info_cache.insert(c, glyph_info);
|
||||
return Some(glyph_info);
|
||||
}
|
||||
}
|
||||
|
||||
if invisible_char(c) {
|
||||
let glyph_info = GlyphInfo::default();
|
||||
self.glyph_info_cache.write().insert(c, glyph_info);
|
||||
self.glyph_info_cache.insert(c, glyph_info);
|
||||
return Some(glyph_info);
|
||||
}
|
||||
|
||||
@@ -219,7 +213,7 @@ impl FontImpl {
|
||||
advance_width_unscaled: self.ab_glyph_font.h_advance_unscaled(glyph_id).into(),
|
||||
visible: true,
|
||||
};
|
||||
self.glyph_info_cache.write().insert(c, glyph_info);
|
||||
self.glyph_info_cache.insert(c, glyph_info);
|
||||
Some(glyph_info)
|
||||
}
|
||||
}
|
||||
@@ -259,8 +253,9 @@ impl FontImpl {
|
||||
}
|
||||
|
||||
pub fn allocate_glyph(
|
||||
&self,
|
||||
&mut self,
|
||||
glyph_info: GlyphInfo,
|
||||
atlas: &mut TextureAtlas,
|
||||
font_size: f32,
|
||||
pixels_per_point: f32,
|
||||
) -> GlyphAllocation {
|
||||
@@ -273,8 +268,7 @@ impl FontImpl {
|
||||
.ab_glyph_font
|
||||
.pt_scale_factor(font_size * self.tweak.scale * pixels_per_point)
|
||||
.round();
|
||||
let mut cache = self.glyph_alloc_cache.write();
|
||||
let entry = match cache.entry((glyph_info, scale.into())) {
|
||||
let entry = match self.glyph_alloc_cache.entry((glyph_info, scale.into())) {
|
||||
std::collections::hash_map::Entry::Occupied(glyph_alloc) => {
|
||||
return *glyph_alloc.get();
|
||||
}
|
||||
@@ -312,7 +306,6 @@ impl FontImpl {
|
||||
UvRect::default()
|
||||
} else {
|
||||
let glyph_pos = {
|
||||
let atlas = &mut self.atlas.lock();
|
||||
let text_alpha_from_coverage = atlas.text_alpha_from_coverage;
|
||||
let (glyph_pos, image) = atlas.allocate((glyph_width, glyph_height));
|
||||
glyph.draw(|x, y, v| {
|
||||
@@ -352,56 +345,15 @@ impl FontImpl {
|
||||
}
|
||||
}
|
||||
|
||||
type FontIndex = usize;
|
||||
|
||||
// TODO(emilk): rename?
|
||||
/// Wrapper over multiple [`FontImpl`] (e.g. a primary + fallbacks for emojis)
|
||||
pub struct Font {
|
||||
fonts: Vec<Arc<FontImpl>>,
|
||||
|
||||
/// Lazily calculated.
|
||||
characters: Option<BTreeMap<char, Vec<String>>>,
|
||||
|
||||
replacement_glyph: (FontIndex, GlyphInfo),
|
||||
glyph_info_cache: ahash::HashMap<char, (FontIndex, GlyphInfo)>,
|
||||
pub struct Font<'a> {
|
||||
pub(super) fonts_by_id: &'a mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
|
||||
pub(super) cached_family: &'a mut CachedFamily,
|
||||
pub(super) atlas: &'a mut TextureAtlas,
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn new(fonts: Vec<Arc<FontImpl>>) -> Self {
|
||||
if fonts.is_empty() {
|
||||
return Self {
|
||||
fonts,
|
||||
characters: None,
|
||||
replacement_glyph: Default::default(),
|
||||
glyph_info_cache: Default::default(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut slf = Self {
|
||||
fonts,
|
||||
characters: None,
|
||||
replacement_glyph: Default::default(),
|
||||
glyph_info_cache: Default::default(),
|
||||
};
|
||||
|
||||
const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square
|
||||
const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback
|
||||
|
||||
let replacement_glyph = slf
|
||||
.glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR)
|
||||
.or_else(|| slf.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR))
|
||||
.unwrap_or_else(|| {
|
||||
#[cfg(feature = "log")]
|
||||
log::warn!(
|
||||
"Failed to find replacement characters {PRIMARY_REPLACEMENT_CHAR:?} or {FALLBACK_REPLACEMENT_CHAR:?}. Will use empty glyph."
|
||||
);
|
||||
(0, GlyphInfo::default())
|
||||
});
|
||||
slf.replacement_glyph = replacement_glyph;
|
||||
|
||||
slf
|
||||
}
|
||||
|
||||
impl Font<'_> {
|
||||
pub fn preload_characters(&mut self, s: &str) {
|
||||
for c in s.chars() {
|
||||
self.glyph_info(c);
|
||||
@@ -421,9 +373,10 @@ impl Font {
|
||||
|
||||
/// All supported characters, and in which font they are available in.
|
||||
pub fn characters(&mut self) -> &BTreeMap<char, Vec<String>> {
|
||||
self.characters.get_or_insert_with(|| {
|
||||
self.cached_family.characters.get_or_insert_with(|| {
|
||||
let mut characters: BTreeMap<char, Vec<String>> = Default::default();
|
||||
for font in &self.fonts {
|
||||
for font_id in &self.cached_family.fonts {
|
||||
let font = self.fonts_by_id.get(font_id).expect("Nonexistent font ID");
|
||||
for chr in font.characters() {
|
||||
characters.entry(chr).or_default().push(font.name.clone());
|
||||
}
|
||||
@@ -437,20 +390,27 @@ impl Font {
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
#[inline(always)]
|
||||
pub fn row_height(&self, font_size: f32) -> f32 {
|
||||
self.fonts[0].row_height(font_size)
|
||||
let Some(first_font) = self.fonts_by_id.get(&self.cached_family.fonts[0]) else {
|
||||
return 0.0;
|
||||
};
|
||||
first_font.row_height(font_size)
|
||||
}
|
||||
|
||||
/// Width of this character in points.
|
||||
pub fn glyph_width(&mut self, c: char, font_size: f32) -> f32 {
|
||||
let (font_index, glyph_info) = self.glyph_info(c);
|
||||
let font = &self.fonts[font_index].ab_glyph_font;
|
||||
let (key, glyph_info) = self.glyph_info(c);
|
||||
let font = &self
|
||||
.fonts_by_id
|
||||
.get(&key)
|
||||
.expect("Nonexistent font ID")
|
||||
.ab_glyph_font;
|
||||
glyph_info.advance_width_unscaled.0 * font.pt_scale_factor(font_size)
|
||||
/ font.height_unscaled()
|
||||
}
|
||||
|
||||
/// Can we display this glyph?
|
||||
pub fn has_glyph(&mut self, c: char) -> bool {
|
||||
self.glyph_info(c) != self.replacement_glyph // TODO(emilk): this is a false negative if the user asks about the replacement character itself 🤦♂️
|
||||
self.glyph_info(c) != self.cached_family.replacement_glyph // TODO(emilk): this is a false negative if the user asks about the replacement character itself 🤦♂️
|
||||
}
|
||||
|
||||
/// Can we display all the glyphs in this text?
|
||||
@@ -459,24 +419,30 @@ impl Font {
|
||||
}
|
||||
|
||||
/// `\n` will (intentionally) show up as the replacement character.
|
||||
fn glyph_info(&mut self, c: char) -> (FontIndex, GlyphInfo) {
|
||||
if let Some(font_index_glyph_info) = self.glyph_info_cache.get(&c) {
|
||||
fn glyph_info(&mut self, c: char) -> (FontFaceKey, GlyphInfo) {
|
||||
if let Some(font_index_glyph_info) = self.cached_family.glyph_info_cache.get(&c) {
|
||||
return *font_index_glyph_info;
|
||||
}
|
||||
|
||||
let font_index_glyph_info = self.glyph_info_no_cache_or_fallback(c);
|
||||
let font_index_glyph_info = font_index_glyph_info.unwrap_or(self.replacement_glyph);
|
||||
self.glyph_info_cache.insert(c, font_index_glyph_info);
|
||||
let font_index_glyph_info =
|
||||
font_index_glyph_info.unwrap_or(self.cached_family.replacement_glyph);
|
||||
self.cached_family
|
||||
.glyph_info_cache
|
||||
.insert(c, font_index_glyph_info);
|
||||
font_index_glyph_info
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn font_impl_and_glyph_info(&mut self, c: char) -> (Option<&FontImpl>, GlyphInfo) {
|
||||
if self.fonts.is_empty() {
|
||||
return (None, self.replacement_glyph.1);
|
||||
pub(crate) fn font_impl_and_glyph_info(
|
||||
&mut self,
|
||||
c: char,
|
||||
) -> (Option<&mut FontImpl>, GlyphInfo) {
|
||||
if self.cached_family.fonts.is_empty() {
|
||||
return (None, self.cached_family.replacement_glyph.1);
|
||||
}
|
||||
let (font_index, glyph_info) = self.glyph_info(c);
|
||||
let font_impl = &self.fonts[font_index];
|
||||
let (key, glyph_info) = self.glyph_info(c);
|
||||
let font_impl = self.fonts_by_id.get_mut(&key).expect("Nonexistent font ID");
|
||||
(Some(font_impl), glyph_info)
|
||||
}
|
||||
|
||||
@@ -487,28 +453,39 @@ impl Font {
|
||||
font_size: f32,
|
||||
pixels_per_point: f32,
|
||||
) -> (Option<&FontImpl>, GlyphAllocation) {
|
||||
if self.fonts.is_empty() {
|
||||
if self.cached_family.fonts.is_empty() {
|
||||
return (None, Default::default());
|
||||
}
|
||||
let (font_index, glyph_info) = self.glyph_info(c);
|
||||
let font_impl = &self.fonts[font_index];
|
||||
let allocated_glyph = font_impl.allocate_glyph(glyph_info, font_size, pixels_per_point);
|
||||
let (key, glyph_info) = self.glyph_info(c);
|
||||
let font_impl = self.fonts_by_id.get_mut(&key).expect("Nonexistent font ID");
|
||||
let allocated_glyph =
|
||||
font_impl.allocate_glyph(glyph_info, self.atlas, font_size, pixels_per_point);
|
||||
(Some(font_impl), allocated_glyph)
|
||||
}
|
||||
|
||||
pub(crate) fn ascent(&self, font_size: f32) -> f32 {
|
||||
if let Some(first) = self.fonts.first() {
|
||||
if let Some(first) = self.cached_family.fonts.first() {
|
||||
let first = self.fonts_by_id.get(first).expect("Nonexistent font ID");
|
||||
first.ascent(font_size)
|
||||
} else {
|
||||
self.row_height(font_size)
|
||||
}
|
||||
}
|
||||
|
||||
fn glyph_info_no_cache_or_fallback(&mut self, c: char) -> Option<(FontIndex, GlyphInfo)> {
|
||||
for (font_index, font_impl) in self.fonts.iter().enumerate() {
|
||||
pub(crate) fn glyph_info_no_cache_or_fallback(
|
||||
&mut self,
|
||||
c: char,
|
||||
) -> Option<(FontFaceKey, GlyphInfo)> {
|
||||
for font_key in &self.cached_family.fonts {
|
||||
let font_impl = self
|
||||
.fonts_by_id
|
||||
.get_mut(font_key)
|
||||
.expect("Nonexistent font ID");
|
||||
if let Some(glyph_info) = font_impl.glyph_info(c) {
|
||||
self.glyph_info_cache.insert(c, (font_index, glyph_info));
|
||||
return Some((font_index, glyph_info));
|
||||
self.cached_family
|
||||
.glyph_info_cache
|
||||
.insert(c, (*font_key, glyph_info));
|
||||
return Some((*font_key, glyph_info));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicU64, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
AlphaFromCoverage, TextureAtlas,
|
||||
mutex::Mutex,
|
||||
text::{
|
||||
Galley, LayoutJob, LayoutSection,
|
||||
font::{Font, FontImpl},
|
||||
font::{Font, FontImpl, GlyphInfo},
|
||||
},
|
||||
};
|
||||
use emath::NumExt as _;
|
||||
@@ -399,6 +404,79 @@ impl FontDefinitions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique ID for looking up a single font face/file.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub(crate) struct FontFaceKey(pub u64);
|
||||
|
||||
static KEY_COUNTER: AtomicU64 = AtomicU64::new(1);
|
||||
impl FontFaceKey {
|
||||
fn new() -> Self {
|
||||
Self(KEY_COUNTER.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl nohash_hasher::IsEnabled for FontFaceKey {}
|
||||
|
||||
/// Cached data for working with a font family (e.g. doing character lookups).
|
||||
#[derive(Debug)]
|
||||
pub(super) struct CachedFamily {
|
||||
pub fonts: Vec<FontFaceKey>,
|
||||
|
||||
/// Lazily calculated.
|
||||
pub characters: Option<BTreeMap<char, Vec<String>>>,
|
||||
|
||||
pub replacement_glyph: (FontFaceKey, GlyphInfo),
|
||||
|
||||
pub glyph_info_cache: ahash::HashMap<char, (FontFaceKey, GlyphInfo)>,
|
||||
}
|
||||
|
||||
impl CachedFamily {
|
||||
fn new(
|
||||
fonts: Vec<FontFaceKey>,
|
||||
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
|
||||
atlas: &mut TextureAtlas,
|
||||
) -> Self {
|
||||
if fonts.is_empty() {
|
||||
return Self {
|
||||
fonts,
|
||||
characters: None,
|
||||
replacement_glyph: Default::default(),
|
||||
glyph_info_cache: Default::default(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut slf = Self {
|
||||
fonts,
|
||||
characters: None,
|
||||
replacement_glyph: Default::default(),
|
||||
glyph_info_cache: Default::default(),
|
||||
};
|
||||
|
||||
const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square
|
||||
const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback
|
||||
|
||||
let mut font = Font {
|
||||
fonts_by_id,
|
||||
cached_family: &mut slf,
|
||||
atlas,
|
||||
};
|
||||
|
||||
let replacement_glyph = font
|
||||
.glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR)
|
||||
.or_else(|| font.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR))
|
||||
.unwrap_or_else(|| {
|
||||
#[cfg(feature = "log")]
|
||||
log::warn!(
|
||||
"Failed to find replacement characters {PRIMARY_REPLACEMENT_CHAR:?} or {FALLBACK_REPLACEMENT_CHAR:?}. Will use empty glyph."
|
||||
);
|
||||
(FontFaceKey::default(), GlyphInfo::default())
|
||||
});
|
||||
slf.replacement_glyph = replacement_glyph;
|
||||
|
||||
slf
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// The collection of fonts used by `epaint`.
|
||||
@@ -454,8 +532,8 @@ impl Fonts {
|
||||
let pixels_per_point_changed = self.fonts.pixels_per_point != pixels_per_point;
|
||||
let max_texture_side_changed = self.fonts.max_texture_side != max_texture_side;
|
||||
let text_alpha_from_coverage_changed =
|
||||
self.fonts.atlas.lock().text_alpha_from_coverage != text_alpha_from_coverage;
|
||||
let font_atlas_almost_full = self.fonts.atlas.lock().fill_ratio() > 0.8;
|
||||
self.fonts.atlas.text_alpha_from_coverage != text_alpha_from_coverage;
|
||||
let font_atlas_almost_full = self.fonts.atlas.fill_ratio() > 0.8;
|
||||
let needs_recreate = pixels_per_point_changed
|
||||
|| max_texture_side_changed
|
||||
|| text_alpha_from_coverage_changed
|
||||
@@ -479,8 +557,8 @@ impl Fonts {
|
||||
}
|
||||
|
||||
/// Call at the end of each frame (before painting) to get the change to the font texture since last call.
|
||||
pub fn font_image_delta(&self) -> Option<crate::ImageDelta> {
|
||||
self.fonts.atlas.lock().take_delta()
|
||||
pub fn font_image_delta(&mut self) -> Option<crate::ImageDelta> {
|
||||
self.fonts.atlas.take_delta()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -495,20 +573,20 @@ impl Fonts {
|
||||
|
||||
/// The font atlas.
|
||||
/// Pass this to [`crate::Tessellator`].
|
||||
pub fn texture_atlas(&self) -> Arc<Mutex<TextureAtlas>> {
|
||||
self.fonts.atlas.clone()
|
||||
pub fn texture_atlas(&self) -> &TextureAtlas {
|
||||
&self.fonts.atlas
|
||||
}
|
||||
|
||||
/// The full font atlas image.
|
||||
#[inline]
|
||||
pub fn image(&self) -> crate::ColorImage {
|
||||
self.fonts.atlas.lock().image().clone()
|
||||
self.fonts.atlas.image().clone()
|
||||
}
|
||||
|
||||
/// Current size of the font image.
|
||||
/// Pass this to [`crate::Tessellator`].
|
||||
pub fn font_image_size(&self) -> [usize; 2] {
|
||||
self.fonts.atlas.lock().size()
|
||||
self.fonts.atlas.size()
|
||||
}
|
||||
|
||||
/// Width of this character in points.
|
||||
@@ -564,7 +642,7 @@ impl Fonts {
|
||||
/// This increases as new fonts and/or glyphs are used,
|
||||
/// but can also decrease in a call to [`Self::begin_pass`].
|
||||
pub fn font_atlas_fill_ratio(&self) -> f32 {
|
||||
self.fonts.atlas.lock().fill_ratio()
|
||||
self.fonts.atlas.fill_ratio()
|
||||
}
|
||||
|
||||
/// Will wrap text at the given width and line break at `\n`.
|
||||
@@ -618,10 +696,10 @@ pub struct FontsImpl {
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
definitions: FontDefinitions,
|
||||
atlas: Arc<Mutex<TextureAtlas>>,
|
||||
//font_impl_cache: FontImplCache,
|
||||
font_impls: ahash::HashMap<String, Arc<FontImpl>>,
|
||||
family_cache: ahash::HashMap<FontFamily, Font>,
|
||||
atlas: TextureAtlas,
|
||||
fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontImpl>,
|
||||
font_impls: ahash::HashMap<String, FontFaceKey>,
|
||||
family_cache: ahash::HashMap<FontFamily, CachedFamily>,
|
||||
}
|
||||
|
||||
impl FontsImpl {
|
||||
@@ -642,24 +720,23 @@ impl FontsImpl {
|
||||
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 = Arc::new(Mutex::new(atlas));
|
||||
|
||||
let font_impls = definitions
|
||||
.font_data
|
||||
.iter()
|
||||
.map(|(name, font_data)| {
|
||||
let tweak = font_data.tweak;
|
||||
let ab_glyph = ab_glyph_font_from_font_data(name, font_data);
|
||||
let font_impl = FontImpl::new(atlas.clone(), name.clone(), ab_glyph, tweak);
|
||||
(name.clone(), Arc::new(font_impl))
|
||||
})
|
||||
.collect();
|
||||
let mut fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontImpl> = Default::default();
|
||||
let mut font_impls: 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 key = FontFaceKey::new();
|
||||
fonts_by_id.insert(key, font_impl);
|
||||
font_impls.insert(name.clone(), key);
|
||||
}
|
||||
|
||||
Self {
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
definitions,
|
||||
atlas,
|
||||
fonts_by_id,
|
||||
font_impls,
|
||||
family_cache: Default::default(),
|
||||
}
|
||||
@@ -676,24 +753,29 @@ impl FontsImpl {
|
||||
}
|
||||
|
||||
/// Get the right font implementation from [`FontFamily`].
|
||||
pub fn font(&mut self, family: &FontFamily) -> &mut Font {
|
||||
self.family_cache.entry(family.clone()).or_insert_with(|| {
|
||||
pub fn font(&mut self, family: &FontFamily) -> Font<'_> {
|
||||
let cached_family = self.family_cache.entry(family.clone()).or_insert_with(|| {
|
||||
let fonts = &self.definitions.families.get(family);
|
||||
let fonts =
|
||||
fonts.unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts"));
|
||||
|
||||
let fonts: Vec<Arc<FontImpl>> = fonts
|
||||
let fonts: Vec<FontFaceKey> = fonts
|
||||
.iter()
|
||||
.map(|font_name| {
|
||||
self.font_impls
|
||||
*self
|
||||
.font_impls
|
||||
.get(font_name)
|
||||
.unwrap_or_else(|| panic!("No font data found for {font_name:?}"))
|
||||
.clone()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Font::new(fonts)
|
||||
})
|
||||
CachedFamily::new(fonts, &mut self.fonts_by_id, &mut self.atlas)
|
||||
});
|
||||
Font {
|
||||
fonts_by_id: &mut self.fonts_by_id,
|
||||
cached_family,
|
||||
atlas: &mut self.atlas,
|
||||
}
|
||||
}
|
||||
|
||||
/// Width of this character in points.
|
||||
|
||||
@@ -144,7 +144,7 @@ fn layout_section(
|
||||
format,
|
||||
} = section;
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
let font = fonts.font(&format.font_id.family);
|
||||
let mut font = fonts.font(&format.font_id.family);
|
||||
let font_size = format.font_id.size;
|
||||
let line_height = section
|
||||
.format
|
||||
@@ -416,7 +416,7 @@ fn replace_last_glyph_with_overflow_character(
|
||||
}
|
||||
}
|
||||
|
||||
fn row_height(section: &LayoutSection, font: &Font, font_size: f32) -> f32 {
|
||||
fn row_height(section: &LayoutSection, font: &Font<'_>, font_size: f32) -> f32 {
|
||||
section
|
||||
.format
|
||||
.line_height
|
||||
@@ -433,9 +433,9 @@ fn replace_last_glyph_with_overflow_character(
|
||||
if let Some(last_glyph) = row.glyphs.last() {
|
||||
let section_index = last_glyph.section_index;
|
||||
let section = &job.sections[section_index as usize];
|
||||
let font = fonts.font(§ion.format.font_id.family);
|
||||
let mut font = fonts.font(§ion.format.font_id.family);
|
||||
let font_size = section.format.font_id.size;
|
||||
let line_height = row_height(section, font, font_size);
|
||||
let line_height = row_height(section, &font, font_size);
|
||||
|
||||
let (_, last_glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
||||
|
||||
@@ -470,29 +470,25 @@ fn replace_last_glyph_with_overflow_character(
|
||||
} else {
|
||||
let section_index = row.section_index_at_start;
|
||||
let section = &job.sections[section_index as usize];
|
||||
let font = fonts.font(§ion.format.font_id.family);
|
||||
let mut font = fonts.font(§ion.format.font_id.family);
|
||||
let font_size = section.format.font_id.size;
|
||||
let line_height = row_height(section, font, font_size);
|
||||
let line_height = row_height(section, &font, font_size);
|
||||
|
||||
let x = 0.0; // TODO(emilk): heed paragraph leading_space 😬
|
||||
|
||||
let (font_impl, replacement_glyph_info) = font.font_impl_and_glyph_info(overflow_character);
|
||||
let glyph_alloc = if let Some(font_impl) = font_impl {
|
||||
font_impl.allocate_glyph(replacement_glyph_info, font_size, pixels_per_point)
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let (mut font_impl, replacement_glyph_alloc) =
|
||||
font.font_impl_and_glyph_alloc(overflow_character, font_size, pixels_per_point);
|
||||
|
||||
row.glyphs.push(Glyph {
|
||||
chr: overflow_character,
|
||||
pos: pos2(x, f32::NAN),
|
||||
advance_width: glyph_alloc.advance_width,
|
||||
advance_width: replacement_glyph_alloc.advance_width,
|
||||
line_height,
|
||||
font_impl_height: font_impl.map_or(0.0, |f| f.row_height(font_size)),
|
||||
font_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)),
|
||||
font_impl_height: font_impl.as_mut().map_or(0.0, |f| f.row_height(font_size)),
|
||||
font_impl_ascent: font_impl.as_mut().map_or(0.0, |f| f.ascent(font_size)),
|
||||
font_height: font.row_height(font_size),
|
||||
font_ascent: font.ascent(font_size),
|
||||
uv_rect: glyph_alloc.uv_rect,
|
||||
uv_rect: replacement_glyph_alloc.uv_rect,
|
||||
section_index,
|
||||
});
|
||||
}
|
||||
@@ -519,7 +515,7 @@ fn replace_last_glyph_with_overflow_character(
|
||||
let section = &job.sections[last_glyph.section_index as usize];
|
||||
let extra_letter_spacing = section.format.extra_letter_spacing;
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
let font = fonts.font(§ion.format.font_id.family);
|
||||
let mut font = fonts.font(§ion.format.font_id.family);
|
||||
let font_size = section.format.font_id.size;
|
||||
|
||||
if let Some(prev_glyph) = prev_glyph {
|
||||
@@ -573,7 +569,7 @@ fn replace_last_glyph_with_overflow_character(
|
||||
return;
|
||||
};
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
let font = fonts.font(§ion.format.font_id.family);
|
||||
let mut font = fonts.font(§ion.format.font_id.family);
|
||||
let font_size = section.format.font_id.size;
|
||||
// Just replace and be done with it.
|
||||
last_glyph.chr = overflow_character;
|
||||
|
||||
Reference in New Issue
Block a user