mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 06:40:06 -04:00
Make Font+FontImpl resolution and size independent
This commit is contained in:
@@ -641,7 +641,10 @@ impl ContextImpl {
|
|||||||
// Preload the most common characters for the most common fonts.
|
// Preload the most common characters for the most common fonts.
|
||||||
// This is not very important to do, but may save a few GPU operations.
|
// This is not very important to do, but may save a few GPU operations.
|
||||||
for font_id in self.memory.options.style().text_styles.values() {
|
for font_id in self.memory.options.style().text_styles.values() {
|
||||||
fonts.fonts.font(font_id).preload_common_characters();
|
fonts
|
||||||
|
.fonts
|
||||||
|
.font(&font_id.family)
|
||||||
|
.preload_common_characters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1585,7 +1588,7 @@ impl Context {
|
|||||||
|
|
||||||
let font_id = TextStyle::Body.resolve(&self.style());
|
let font_id = TextStyle::Body.resolve(&self.style());
|
||||||
self.fonts_mut(|f| {
|
self.fonts_mut(|f| {
|
||||||
let font = f.fonts.font(&font_id);
|
let font = f.fonts.font(&font_id.family);
|
||||||
font.has_glyphs(alt)
|
font.has_glyphs(alt)
|
||||||
&& font.has_glyphs(ctrl)
|
&& font.has_glyphs(ctrl)
|
||||||
&& font.has_glyphs(shift)
|
&& font.has_glyphs(shift)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ impl crate::View for FontBook {
|
|||||||
let available_glyphs = self
|
let available_glyphs = self
|
||||||
.available_glyphs
|
.available_glyphs
|
||||||
.entry(self.font_id.family.clone())
|
.entry(self.font_id.family.clone())
|
||||||
.or_insert_with(|| available_characters(ui, self.font_id.family.clone()));
|
.or_insert_with(|| available_characters(ui, &self.font_id.family));
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
@@ -140,10 +140,10 @@ fn char_info_ui(ui: &mut egui::Ui, chr: char, glyph_info: &GlyphInfo, font_id: e
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, GlyphInfo> {
|
fn available_characters(ui: &egui::Ui, family: &egui::FontFamily) -> BTreeMap<char, GlyphInfo> {
|
||||||
ui.fonts_mut(|f| {
|
ui.fonts_mut(|f| {
|
||||||
f.fonts
|
f.fonts
|
||||||
.font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters
|
.font(family)
|
||||||
.characters()
|
.characters()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(chr, _fonts)| !chr.is_whitespace() && !chr.is_ascii_control())
|
.filter(|(chr, _fonts)| !chr.is_whitespace() && !chr.is_ascii_control())
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use emath::{GuiRounding as _, Vec2, vec2};
|
use ab_glyph::{Font as _, PxScaleFont, ScaleFont as _};
|
||||||
|
use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
TextureAtlas,
|
TextureAtlas,
|
||||||
@@ -34,7 +35,7 @@ impl UvRect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct GlyphInfo {
|
pub struct GlyphInfo {
|
||||||
/// Used for pair-kerning.
|
/// Used for pair-kerning.
|
||||||
///
|
///
|
||||||
@@ -42,24 +43,39 @@ pub struct GlyphInfo {
|
|||||||
/// Use `ab_glyph::GlyphId(0)` if you just want to have an id, and don't care.
|
/// Use `ab_glyph::GlyphId(0)` if you just want to have an id, and don't care.
|
||||||
pub(crate) id: ab_glyph::GlyphId,
|
pub(crate) id: ab_glyph::GlyphId,
|
||||||
|
|
||||||
|
/// Unscaled.
|
||||||
|
pub advance_width_unscaled: OrderedFloat<f32>,
|
||||||
|
|
||||||
|
/// Whether this glyph has any outlines.
|
||||||
|
pub visible: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GlyphInfo {
|
||||||
|
/// Basically a zero-width space.
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
id: ab_glyph::GlyphId(0),
|
||||||
|
advance_width_unscaled: 0.0.into(),
|
||||||
|
visible: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||||
|
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,
|
||||||
|
|
||||||
/// Unit: points.
|
/// Unit: points.
|
||||||
pub advance_width: f32,
|
pub advance_width: f32,
|
||||||
|
|
||||||
/// Texture coordinates.
|
/// UV rectangle for drawing.
|
||||||
pub uv_rect: UvRect,
|
pub uv_rect: UvRect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GlyphInfo {
|
|
||||||
/// Basically a zero-width space.
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
id: ab_glyph::GlyphId(0),
|
|
||||||
advance_width: 0.0,
|
|
||||||
uv_rect: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// A specific font with a size.
|
/// A specific font with a size.
|
||||||
@@ -67,72 +83,51 @@ impl Default for GlyphInfo {
|
|||||||
pub struct FontImpl {
|
pub struct FontImpl {
|
||||||
name: String,
|
name: String,
|
||||||
ab_glyph_font: ab_glyph::FontArc,
|
ab_glyph_font: ab_glyph::FontArc,
|
||||||
|
tweak: FontTweak,
|
||||||
/// Maximum character height
|
|
||||||
scale_in_pixels: u32,
|
|
||||||
|
|
||||||
height_in_points: f32,
|
|
||||||
|
|
||||||
// move each character by this much (hack)
|
|
||||||
y_offset_in_points: f32,
|
|
||||||
|
|
||||||
ascent: f32,
|
|
||||||
pixels_per_point: f32,
|
|
||||||
glyph_info_cache: RwLock<ahash::HashMap<char, GlyphInfo>>, // TODO(emilk): standard Mutex
|
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>>,
|
atlas: Arc<Mutex<TextureAtlas>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait FontExt {
|
||||||
|
fn pt_scaled(&self, scale: f32) -> PxScaleFont<&'_ Self>;
|
||||||
|
|
||||||
|
fn pt_scale_factor(&self, scale: f32) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FontExt for T
|
||||||
|
where
|
||||||
|
T: ab_glyph::Font,
|
||||||
|
{
|
||||||
|
fn pt_scaled(&self, scale: f32) -> PxScaleFont<&'_ Self> {
|
||||||
|
PxScaleFont {
|
||||||
|
font: self,
|
||||||
|
scale: self.pt_scale_factor(scale).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pt_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)")
|
||||||
|
});
|
||||||
|
let font_scaling = self.height_unscaled() / units_per_em;
|
||||||
|
scale * font_scaling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FontImpl {
|
impl FontImpl {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
atlas: Arc<Mutex<TextureAtlas>>,
|
atlas: Arc<Mutex<TextureAtlas>>,
|
||||||
pixels_per_point: f32,
|
|
||||||
name: String,
|
name: String,
|
||||||
ab_glyph_font: ab_glyph::FontArc,
|
ab_glyph_font: ab_glyph::FontArc,
|
||||||
scale_in_pixels: f32,
|
|
||||||
tweak: FontTweak,
|
tweak: FontTweak,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(
|
|
||||||
scale_in_pixels > 0.0,
|
|
||||||
"scale_in_pixels is smaller than 0, got: {scale_in_pixels:?}"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
pixels_per_point > 0.0,
|
|
||||||
"pixels_per_point must be greater than 0, got: {pixels_per_point:?}"
|
|
||||||
);
|
|
||||||
|
|
||||||
use ab_glyph::{Font as _, ScaleFont as _};
|
|
||||||
let scaled = ab_glyph_font.as_scaled(scale_in_pixels);
|
|
||||||
let ascent = (scaled.ascent() / pixels_per_point).round_ui();
|
|
||||||
let descent = (scaled.descent() / pixels_per_point).round_ui();
|
|
||||||
let line_gap = (scaled.line_gap() / pixels_per_point).round_ui();
|
|
||||||
|
|
||||||
// Tweak the scale as the user desired
|
|
||||||
let scale_in_pixels = scale_in_pixels * tweak.scale;
|
|
||||||
let scale_in_points = scale_in_pixels / pixels_per_point;
|
|
||||||
|
|
||||||
let y_offset_points =
|
|
||||||
((scale_in_points * tweak.y_offset_factor) + tweak.y_offset).round_ui();
|
|
||||||
|
|
||||||
// Center scaled glyphs properly:
|
|
||||||
let height = ascent + descent;
|
|
||||||
let y_offset_points = y_offset_points - (1.0 - tweak.scale) * 0.5 * height;
|
|
||||||
|
|
||||||
// Round to an even number of physical pixels to get even kerning.
|
|
||||||
// See https://github.com/emilk/egui/issues/382
|
|
||||||
let scale_in_pixels = scale_in_pixels.round() as u32;
|
|
||||||
|
|
||||||
// Round to closest pixel:
|
|
||||||
let y_offset_in_points = (y_offset_points * pixels_per_point).round() / pixels_per_point;
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
ab_glyph_font,
|
ab_glyph_font,
|
||||||
scale_in_pixels,
|
tweak,
|
||||||
height_in_points: ascent - descent + line_gap,
|
|
||||||
y_offset_in_points,
|
|
||||||
ascent,
|
|
||||||
pixels_per_point,
|
|
||||||
glyph_info_cache: Default::default(),
|
glyph_info_cache: Default::default(),
|
||||||
|
glyph_alloc_cache: Default::default(),
|
||||||
atlas,
|
atlas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +154,6 @@ impl FontImpl {
|
|||||||
|
|
||||||
/// An un-ordered iterator over all supported characters.
|
/// An un-ordered iterator over all supported characters.
|
||||||
fn characters(&self) -> impl Iterator<Item = char> + '_ {
|
fn characters(&self) -> impl Iterator<Item = char> + '_ {
|
||||||
use ab_glyph::Font as _;
|
|
||||||
self.ab_glyph_font
|
self.ab_glyph_font
|
||||||
.codepoint_ids()
|
.codepoint_ids()
|
||||||
.map(|(_, chr)| chr)
|
.map(|(_, chr)| chr)
|
||||||
@@ -181,7 +175,9 @@ impl FontImpl {
|
|||||||
if c == '\t' {
|
if c == '\t' {
|
||||||
if let Some(space) = self.glyph_info(' ') {
|
if let Some(space) = self.glyph_info(' ') {
|
||||||
let glyph_info = GlyphInfo {
|
let glyph_info = GlyphInfo {
|
||||||
advance_width: crate::text::TAB_SIZE as f32 * space.advance_width,
|
advance_width_unscaled: (crate::text::TAB_SIZE as f32
|
||||||
|
* space.advance_width_unscaled.0)
|
||||||
|
.into(),
|
||||||
..space
|
..space
|
||||||
};
|
};
|
||||||
self.glyph_info_cache.write().insert(c, glyph_info);
|
self.glyph_info_cache.write().insert(c, glyph_info);
|
||||||
@@ -195,10 +191,10 @@ impl FontImpl {
|
|||||||
// https://en.wikipedia.org/wiki/Thin_space
|
// https://en.wikipedia.org/wiki/Thin_space
|
||||||
|
|
||||||
if let Some(space) = self.glyph_info(' ') {
|
if let Some(space) = self.glyph_info(' ') {
|
||||||
let em = self.height_in_points; // TODO(emilk): is this right?
|
let em = self.ab_glyph_font.units_per_em().unwrap_or(1.0);
|
||||||
let advance_width = f32::min(em / 6.0, space.advance_width * 0.5);
|
let advance_width = f32::min(em / 6.0, space.advance_width_unscaled.0 * 0.5);
|
||||||
let glyph_info = GlyphInfo {
|
let glyph_info = GlyphInfo {
|
||||||
advance_width,
|
advance_width_unscaled: advance_width.into(),
|
||||||
..space
|
..space
|
||||||
};
|
};
|
||||||
self.glyph_info_cache.write().insert(c, glyph_info);
|
self.glyph_info_cache.write().insert(c, glyph_info);
|
||||||
@@ -213,13 +209,16 @@ impl FontImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add new character:
|
// Add new character:
|
||||||
use ab_glyph::Font as _;
|
|
||||||
let glyph_id = self.ab_glyph_font.glyph_id(c);
|
let glyph_id = self.ab_glyph_font.glyph_id(c);
|
||||||
|
|
||||||
if glyph_id.0 == 0 {
|
if glyph_id.0 == 0 {
|
||||||
None // unsupported character
|
None // unsupported character
|
||||||
} else {
|
} else {
|
||||||
let glyph_info = self.allocate_glyph(glyph_id);
|
let glyph_info = GlyphInfo {
|
||||||
|
id: glyph_id,
|
||||||
|
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.write().insert(c, glyph_info);
|
||||||
Some(glyph_info)
|
Some(glyph_info)
|
||||||
}
|
}
|
||||||
@@ -230,43 +229,80 @@ impl FontImpl {
|
|||||||
&self,
|
&self,
|
||||||
last_glyph_id: ab_glyph::GlyphId,
|
last_glyph_id: ab_glyph::GlyphId,
|
||||||
glyph_id: ab_glyph::GlyphId,
|
glyph_id: ab_glyph::GlyphId,
|
||||||
|
font_size: f32,
|
||||||
|
pixels_per_point: f32,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
use ab_glyph::{Font as _, ScaleFont as _};
|
// Round to an even number of physical pixels to get even kerning.
|
||||||
|
// See https://github.com/emilk/egui/issues/382
|
||||||
self.ab_glyph_font
|
self.ab_glyph_font
|
||||||
.as_scaled(self.scale_in_pixels as f32)
|
.pt_scaled((font_size * self.tweak.scale * pixels_per_point).round())
|
||||||
.kern(last_glyph_id, glyph_id)
|
.kern(last_glyph_id, glyph_id)
|
||||||
/ self.pixels_per_point
|
/ pixels_per_point
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Height of one row of text in points.
|
/// Height of one row of text in points.
|
||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn row_height(&self) -> f32 {
|
pub fn row_height(&self, font_size: f32) -> f32 {
|
||||||
self.height_in_points
|
let font = self.ab_glyph_font.pt_scaled(font_size);
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
font.ascent().round_ui() - font.descent().round_ui() + font.line_gap().round_ui()
|
||||||
pub fn pixels_per_point(&self) -> f32 {
|
|
||||||
self.pixels_per_point
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the distance from the top to the baseline.
|
/// This is the distance from the top to the baseline.
|
||||||
///
|
///
|
||||||
/// Unit: points.
|
/// Unit: points.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn ascent(&self) -> f32 {
|
pub fn ascent(&self, font_size: f32) -> f32 {
|
||||||
self.ascent
|
self.ab_glyph_font.pt_scaled(font_size).ascent().round_ui()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_glyph(&self, glyph_id: ab_glyph::GlyphId) -> GlyphInfo {
|
pub fn allocate_glyph(
|
||||||
assert!(glyph_id.0 != 0, "Can't allocate glyph for id 0");
|
&self,
|
||||||
use ab_glyph::{Font as _, ScaleFont as _};
|
glyph_info: GlyphInfo,
|
||||||
|
font_size: f32,
|
||||||
|
pixels_per_point: f32,
|
||||||
|
) -> GlyphAllocation {
|
||||||
|
if !glyph_info.visible {
|
||||||
|
return GlyphAllocation::default();
|
||||||
|
}
|
||||||
|
// Round to an even number of physical pixels to get even kerning.
|
||||||
|
// See https://github.com/emilk/egui/issues/382
|
||||||
|
let scale = self
|
||||||
|
.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())) {
|
||||||
|
std::collections::hash_map::Entry::Occupied(glyph_alloc) => {
|
||||||
|
return *glyph_alloc.get();
|
||||||
|
}
|
||||||
|
std::collections::hash_map::Entry::Vacant(entry) => entry,
|
||||||
|
};
|
||||||
|
|
||||||
let glyph = glyph_id.with_scale_and_position(
|
assert!(glyph_info.id.0 != 0, "Can't allocate glyph for id 0");
|
||||||
self.scale_in_pixels as f32,
|
|
||||||
ab_glyph::Point { x: 0.0, y: 0.0 },
|
let glyph = glyph_info
|
||||||
);
|
.id
|
||||||
|
.with_scale_and_position(scale, ab_glyph::Point { x: 0.0, y: 0.0 });
|
||||||
|
|
||||||
|
// Tweak the scale as the user desired
|
||||||
|
let y_offset_in_points = {
|
||||||
|
let logically_scaled = self.ab_glyph_font.pt_scaled(font_size * pixels_per_point);
|
||||||
|
let scale_in_points = scale / pixels_per_point;
|
||||||
|
|
||||||
|
let y_offset_points =
|
||||||
|
((scale_in_points * self.tweak.y_offset_factor) + self.tweak.y_offset).round_ui();
|
||||||
|
|
||||||
|
// Center scaled glyphs properly:
|
||||||
|
let height = (logically_scaled.ascent() / pixels_per_point).round_ui()
|
||||||
|
+ (logically_scaled.descent() / pixels_per_point).round_ui();
|
||||||
|
let y_offset_points = y_offset_points - (1.0 - self.tweak.scale) * 0.5 * height;
|
||||||
|
|
||||||
|
// Round to closest pixel:
|
||||||
|
(y_offset_points * pixels_per_point).round() / pixels_per_point
|
||||||
|
};
|
||||||
|
|
||||||
let uv_rect = self.ab_glyph_font.outline_glyph(glyph).map(|glyph| {
|
let uv_rect = self.ab_glyph_font.outline_glyph(glyph).map(|glyph| {
|
||||||
let bb = glyph.px_bounds();
|
let bb = glyph.px_bounds();
|
||||||
@@ -290,11 +326,10 @@ impl FontImpl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let offset_in_pixels = vec2(bb.min.x, bb.min.y);
|
let offset_in_pixels = vec2(bb.min.x, bb.min.y);
|
||||||
let offset =
|
let offset = offset_in_pixels / pixels_per_point + y_offset_in_points * Vec2::Y;
|
||||||
offset_in_pixels / self.pixels_per_point + self.y_offset_in_points * Vec2::Y;
|
|
||||||
UvRect {
|
UvRect {
|
||||||
offset,
|
offset,
|
||||||
size: vec2(glyph_width as f32, glyph_height as f32) / self.pixels_per_point,
|
size: vec2(glyph_width as f32, glyph_height as f32) / pixels_per_point,
|
||||||
min: [glyph_pos.0 as u16, glyph_pos.1 as u16],
|
min: [glyph_pos.0 as u16, glyph_pos.1 as u16],
|
||||||
max: [
|
max: [
|
||||||
(glyph_pos.0 + glyph_width) as u16,
|
(glyph_pos.0 + glyph_width) as u16,
|
||||||
@@ -305,17 +340,15 @@ impl FontImpl {
|
|||||||
});
|
});
|
||||||
let uv_rect = uv_rect.unwrap_or_default();
|
let uv_rect = uv_rect.unwrap_or_default();
|
||||||
|
|
||||||
let advance_width_in_points = self
|
let allocation = GlyphAllocation {
|
||||||
.ab_glyph_font
|
id: glyph_info.id,
|
||||||
.as_scaled(self.scale_in_pixels as f32)
|
advance_width: (glyph_info.advance_width_unscaled.0 * scale
|
||||||
.h_advance(glyph_id)
|
/ self.ab_glyph_font.height_unscaled())
|
||||||
/ self.pixels_per_point;
|
/ pixels_per_point,
|
||||||
|
|
||||||
GlyphInfo {
|
|
||||||
id: glyph_id,
|
|
||||||
advance_width: advance_width_in_points,
|
|
||||||
uv_rect,
|
uv_rect,
|
||||||
}
|
};
|
||||||
|
entry.insert(allocation);
|
||||||
|
allocation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,8 +363,6 @@ pub struct Font {
|
|||||||
characters: Option<BTreeMap<char, Vec<String>>>,
|
characters: Option<BTreeMap<char, Vec<String>>>,
|
||||||
|
|
||||||
replacement_glyph: (FontIndex, GlyphInfo),
|
replacement_glyph: (FontIndex, GlyphInfo),
|
||||||
pixels_per_point: f32,
|
|
||||||
row_height: f32,
|
|
||||||
glyph_info_cache: ahash::HashMap<char, (FontIndex, GlyphInfo)>,
|
glyph_info_cache: ahash::HashMap<char, (FontIndex, GlyphInfo)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,21 +373,14 @@ impl Font {
|
|||||||
fonts,
|
fonts,
|
||||||
characters: None,
|
characters: None,
|
||||||
replacement_glyph: Default::default(),
|
replacement_glyph: Default::default(),
|
||||||
pixels_per_point: 1.0,
|
|
||||||
row_height: 0.0,
|
|
||||||
glyph_info_cache: Default::default(),
|
glyph_info_cache: Default::default(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let pixels_per_point = fonts[0].pixels_per_point();
|
|
||||||
let row_height = fonts[0].row_height();
|
|
||||||
|
|
||||||
let mut slf = Self {
|
let mut slf = Self {
|
||||||
fonts,
|
fonts,
|
||||||
characters: None,
|
characters: None,
|
||||||
replacement_glyph: Default::default(),
|
replacement_glyph: Default::default(),
|
||||||
pixels_per_point,
|
|
||||||
row_height,
|
|
||||||
glyph_info_cache: Default::default(),
|
glyph_info_cache: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -408,29 +432,20 @@ impl Font {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn round_to_pixel(&self, point: f32) -> f32 {
|
|
||||||
(point * self.pixels_per_point).round() / self.pixels_per_point
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Height of one row of text. In points.
|
/// Height of one row of text. In points.
|
||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn row_height(&self) -> f32 {
|
pub fn row_height(&self, font_size: f32) -> f32 {
|
||||||
self.row_height
|
self.fonts[0].row_height(font_size)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uv_rect(&self, c: char) -> UvRect {
|
|
||||||
self.glyph_info_cache
|
|
||||||
.get(&c)
|
|
||||||
.map(|gi| gi.1.uv_rect)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Width of this character in points.
|
/// Width of this character in points.
|
||||||
pub fn glyph_width(&mut self, c: char) -> f32 {
|
pub fn glyph_width(&mut self, c: char, font_size: f32) -> f32 {
|
||||||
self.glyph_info(c).1.advance_width
|
let (font_index, glyph_info) = self.glyph_info(c);
|
||||||
|
let font = &self.fonts[font_index].ab_glyph_font;
|
||||||
|
glyph_info.advance_width_unscaled.0 * font.pt_scale_factor(font_size)
|
||||||
|
/ font.height_unscaled()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can we display this glyph?
|
/// Can we display this glyph?
|
||||||
@@ -465,11 +480,27 @@ impl Font {
|
|||||||
(Some(font_impl), glyph_info)
|
(Some(font_impl), glyph_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ascent(&self) -> f32 {
|
#[inline]
|
||||||
|
pub(crate) fn font_impl_and_glyph_alloc(
|
||||||
|
&mut self,
|
||||||
|
c: char,
|
||||||
|
font_size: f32,
|
||||||
|
pixels_per_point: f32,
|
||||||
|
) -> (Option<&FontImpl>, GlyphAllocation) {
|
||||||
|
if self.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);
|
||||||
|
(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.fonts.first() {
|
||||||
first.ascent()
|
first.ascent(font_size)
|
||||||
} else {
|
} else {
|
||||||
self.row_height
|
self.row_height(font_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
font::{Font, FontImpl},
|
font::{Font, FontImpl},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use emath::{NumExt as _, OrderedFloat};
|
use emath::NumExt as _;
|
||||||
|
|
||||||
#[cfg(feature = "default_fonts")]
|
#[cfg(feature = "default_fonts")]
|
||||||
use epaint_default_fonts::{EMOJI_ICON, HACK_REGULAR, NOTO_EMOJI_REGULAR, UBUNTU_LIGHT};
|
use epaint_default_fonts::{EMOJI_ICON, HACK_REGULAR, NOTO_EMOJI_REGULAR, UBUNTU_LIGHT};
|
||||||
@@ -619,8 +619,9 @@ pub struct FontsImpl {
|
|||||||
max_texture_side: usize,
|
max_texture_side: usize,
|
||||||
definitions: FontDefinitions,
|
definitions: FontDefinitions,
|
||||||
atlas: Arc<Mutex<TextureAtlas>>,
|
atlas: Arc<Mutex<TextureAtlas>>,
|
||||||
font_impl_cache: FontImplCache,
|
//font_impl_cache: FontImplCache,
|
||||||
sized_family: ahash::HashMap<(OrderedFloat<f32>, FontFamily), Font>,
|
font_impls: ahash::HashMap<String, Arc<FontImpl>>,
|
||||||
|
family_cache: ahash::HashMap<FontFamily, Font>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontsImpl {
|
impl FontsImpl {
|
||||||
@@ -643,16 +644,24 @@ impl FontsImpl {
|
|||||||
|
|
||||||
let atlas = Arc::new(Mutex::new(atlas));
|
let atlas = Arc::new(Mutex::new(atlas));
|
||||||
|
|
||||||
let font_impl_cache =
|
let font_impls = definitions
|
||||||
FontImplCache::new(atlas.clone(), pixels_per_point, &definitions.font_data);
|
.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();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
max_texture_side,
|
max_texture_side,
|
||||||
definitions,
|
definitions,
|
||||||
atlas,
|
atlas,
|
||||||
font_impl_cache,
|
font_impls,
|
||||||
sized_family: Default::default(),
|
family_cache: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,48 +675,47 @@ impl FontsImpl {
|
|||||||
&self.definitions
|
&self.definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the right font implementation from size and [`FontFamily`].
|
/// Get the right font implementation from [`FontFamily`].
|
||||||
pub fn font(&mut self, font_id: &FontId) -> &mut Font {
|
pub fn font(&mut self, family: &FontFamily) -> &mut Font {
|
||||||
let FontId { size, family } = font_id;
|
self.family_cache.entry(family.clone()).or_insert_with(|| {
|
||||||
let mut size = *size;
|
let fonts = &self.definitions.families.get(family);
|
||||||
size = size.at_least(0.1).at_most(2048.0);
|
let fonts =
|
||||||
|
fonts.unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts"));
|
||||||
|
|
||||||
self.sized_family
|
let fonts: Vec<Arc<FontImpl>> = fonts
|
||||||
.entry((OrderedFloat(size), family.clone()))
|
.iter()
|
||||||
.or_insert_with(|| {
|
.map(|font_name| {
|
||||||
let fonts = &self.definitions.families.get(family);
|
self.font_impls
|
||||||
let fonts = fonts
|
.get(font_name)
|
||||||
.unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts"));
|
.unwrap_or_else(|| panic!("No font data found for {font_name:?}"))
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let fonts: Vec<Arc<FontImpl>> = fonts
|
Font::new(fonts)
|
||||||
.iter()
|
})
|
||||||
.map(|font_name| self.font_impl_cache.font_impl(size, font_name))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Font::new(fonts)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Width of this character in points.
|
/// Width of this character in points.
|
||||||
fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
|
fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
|
||||||
self.font(font_id).glyph_width(c)
|
self.font(&font_id.family).glyph_width(c, font_id.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can we display this glyph?
|
/// Can we display this glyph?
|
||||||
pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
|
pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
|
||||||
self.font(font_id).has_glyph(c)
|
self.font(&font_id.family).has_glyph(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can we display all the glyphs in this text?
|
/// Can we display all the glyphs in this text?
|
||||||
pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
|
pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
|
||||||
self.font(font_id).has_glyphs(s)
|
self.font(&font_id.family).has_glyphs(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Height of one row of text in points.
|
/// Height of one row of text in points.
|
||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
fn row_height(&mut self, font_id: &FontId) -> f32 {
|
fn row_height(&mut self, font_id: &FontId) -> f32 {
|
||||||
self.font(font_id).row_height()
|
self.font(&font_id.family).row_height(font_id.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -963,77 +971,6 @@ fn should_cache_each_paragraph_individually(job: &LayoutJob) -> bool {
|
|||||||
job.break_on_newline && job.wrap.max_rows == usize::MAX && job.text.contains('\n')
|
job.break_on_newline && job.wrap.max_rows == usize::MAX && job.text.contains('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
struct FontImplCache {
|
|
||||||
atlas: Arc<Mutex<TextureAtlas>>,
|
|
||||||
pixels_per_point: f32,
|
|
||||||
ab_glyph_fonts: BTreeMap<String, (FontTweak, ab_glyph::FontArc)>,
|
|
||||||
|
|
||||||
/// Map font pixel sizes and names to the cached [`FontImpl`].
|
|
||||||
cache: ahash::HashMap<(u32, String), Arc<FontImpl>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontImplCache {
|
|
||||||
pub fn new(
|
|
||||||
atlas: Arc<Mutex<TextureAtlas>>,
|
|
||||||
pixels_per_point: f32,
|
|
||||||
font_data: &BTreeMap<String, Arc<FontData>>,
|
|
||||||
) -> Self {
|
|
||||||
let ab_glyph_fonts = font_data
|
|
||||||
.iter()
|
|
||||||
.map(|(name, font_data)| {
|
|
||||||
let tweak = font_data.tweak;
|
|
||||||
let ab_glyph = ab_glyph_font_from_font_data(name, font_data);
|
|
||||||
(name.clone(), (tweak, ab_glyph))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
atlas,
|
|
||||||
pixels_per_point,
|
|
||||||
ab_glyph_fonts,
|
|
||||||
cache: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn font_impl(&mut self, scale_in_points: f32, font_name: &str) -> Arc<FontImpl> {
|
|
||||||
use ab_glyph::Font as _;
|
|
||||||
|
|
||||||
let (tweak, ab_glyph_font) = self
|
|
||||||
.ab_glyph_fonts
|
|
||||||
.get(font_name)
|
|
||||||
.unwrap_or_else(|| panic!("No font data found for {font_name:?}"))
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let scale_in_pixels = self.pixels_per_point * scale_in_points;
|
|
||||||
|
|
||||||
// Scale the font properly (see https://github.com/emilk/egui/issues/2068).
|
|
||||||
let units_per_em = ab_glyph_font.units_per_em().unwrap_or_else(|| {
|
|
||||||
panic!("The font unit size of {font_name:?} exceeds the expected range (16..=16384)")
|
|
||||||
});
|
|
||||||
let font_scaling = ab_glyph_font.height_unscaled() / units_per_em;
|
|
||||||
let scale_in_pixels = scale_in_pixels * font_scaling;
|
|
||||||
|
|
||||||
self.cache
|
|
||||||
.entry((
|
|
||||||
(scale_in_pixels * tweak.scale).round() as u32,
|
|
||||||
font_name.to_owned(),
|
|
||||||
))
|
|
||||||
.or_insert_with(|| {
|
|
||||||
Arc::new(FontImpl::new(
|
|
||||||
self.atlas.clone(),
|
|
||||||
self.pixels_per_point,
|
|
||||||
font_name.to_owned(),
|
|
||||||
ab_glyph_font,
|
|
||||||
scale_in_pixels,
|
|
||||||
tweak,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "default_fonts")]
|
#[cfg(feature = "default_fonts")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
@@ -143,11 +143,13 @@ fn layout_section(
|
|||||||
byte_range,
|
byte_range,
|
||||||
format,
|
format,
|
||||||
} = section;
|
} = section;
|
||||||
let font = fonts.font(&format.font_id);
|
let pixels_per_point = fonts.pixels_per_point();
|
||||||
|
let font = fonts.font(&format.font_id.family);
|
||||||
|
let font_size = format.font_id.size;
|
||||||
let line_height = section
|
let line_height = section
|
||||||
.format
|
.format
|
||||||
.line_height
|
.line_height
|
||||||
.unwrap_or_else(|| font.row_height());
|
.unwrap_or_else(|| font.row_height(font_size));
|
||||||
let extra_letter_spacing = section.format.extra_letter_spacing;
|
let extra_letter_spacing = section.format.extra_letter_spacing;
|
||||||
|
|
||||||
let mut paragraph = out_paragraphs.last_mut().unwrap();
|
let mut paragraph = out_paragraphs.last_mut().unwrap();
|
||||||
@@ -165,30 +167,35 @@ fn layout_section(
|
|||||||
paragraph = out_paragraphs.last_mut().unwrap();
|
paragraph = out_paragraphs.last_mut().unwrap();
|
||||||
paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
|
paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
|
||||||
} else {
|
} else {
|
||||||
let (font_impl, glyph_info) = font.font_impl_and_glyph_info(chr);
|
let (font_impl, glyph_alloc) =
|
||||||
if let Some(font_impl) = font_impl {
|
font.font_impl_and_glyph_alloc(chr, font_size, pixels_per_point);
|
||||||
if let Some(last_glyph_id) = last_glyph_id {
|
|
||||||
paragraph.cursor_x += font_impl.pair_kerning(last_glyph_id, glyph_info.id);
|
if let (Some(font_impl), Some(last_glyph_id)) = (font_impl, last_glyph_id) {
|
||||||
paragraph.cursor_x += extra_letter_spacing;
|
paragraph.cursor_x += font_impl.pair_kerning(
|
||||||
}
|
last_glyph_id,
|
||||||
|
glyph_alloc.id,
|
||||||
|
font_size,
|
||||||
|
pixels_per_point,
|
||||||
|
);
|
||||||
|
paragraph.cursor_x += extra_letter_spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
paragraph.glyphs.push(Glyph {
|
paragraph.glyphs.push(Glyph {
|
||||||
chr,
|
chr,
|
||||||
pos: pos2(paragraph.cursor_x, f32::NAN),
|
pos: pos2(paragraph.cursor_x, f32::NAN),
|
||||||
advance_width: glyph_info.advance_width,
|
advance_width: glyph_alloc.advance_width,
|
||||||
line_height,
|
line_height,
|
||||||
font_impl_height: font_impl.map_or(0.0, |f| f.row_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_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)),
|
||||||
font_height: font.row_height(),
|
font_height: font.row_height(font_size),
|
||||||
font_ascent: font.ascent(),
|
font_ascent: font.ascent(font_size),
|
||||||
uv_rect: glyph_info.uv_rect,
|
uv_rect: glyph_alloc.uv_rect,
|
||||||
section_index,
|
section_index,
|
||||||
});
|
});
|
||||||
|
|
||||||
paragraph.cursor_x += glyph_info.advance_width;
|
paragraph.cursor_x += glyph_alloc.advance_width;
|
||||||
paragraph.cursor_x = font.round_to_pixel(paragraph.cursor_x);
|
paragraph.cursor_x = paragraph.cursor_x.round_to_pixels(pixels_per_point);
|
||||||
last_glyph_id = Some(glyph_info.id);
|
last_glyph_id = Some(glyph_alloc.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,70 +416,83 @@ fn replace_last_glyph_with_overflow_character(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn row_height(section: &LayoutSection, font: &Font) -> f32 {
|
fn row_height(section: &LayoutSection, font: &Font, font_size: f32) -> f32 {
|
||||||
section
|
section
|
||||||
.format
|
.format
|
||||||
.line_height
|
.line_height
|
||||||
.unwrap_or_else(|| font.row_height())
|
.unwrap_or_else(|| font.row_height(font_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(overflow_character) = job.wrap.overflow_character else {
|
let Some(overflow_character) = job.wrap.overflow_character else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pixels_per_point = fonts.pixels_per_point();
|
||||||
|
|
||||||
// We always try to just append the character first:
|
// We always try to just append the character first:
|
||||||
if let Some(last_glyph) = row.glyphs.last() {
|
if let Some(last_glyph) = row.glyphs.last() {
|
||||||
let section_index = last_glyph.section_index;
|
let section_index = last_glyph.section_index;
|
||||||
let section = &job.sections[section_index as usize];
|
let section = &job.sections[section_index as usize];
|
||||||
let font = fonts.font(§ion.format.font_id);
|
let font = fonts.font(§ion.format.font_id.family);
|
||||||
let line_height = row_height(section, font);
|
let font_size = section.format.font_id.size;
|
||||||
|
let line_height = row_height(section, font, font_size);
|
||||||
|
|
||||||
let (_, last_glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
let (_, last_glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
||||||
|
|
||||||
let mut x = last_glyph.pos.x + last_glyph.advance_width;
|
let mut x = last_glyph.pos.x + last_glyph.advance_width;
|
||||||
|
|
||||||
let (font_impl, replacement_glyph_info) = font.font_impl_and_glyph_info(overflow_character);
|
let (font_impl, replacement_glyph_alloc) =
|
||||||
|
font.font_impl_and_glyph_alloc(overflow_character, font_size, pixels_per_point);
|
||||||
|
|
||||||
{
|
// Kerning:
|
||||||
// Kerning:
|
x += section.format.extra_letter_spacing;
|
||||||
x += section.format.extra_letter_spacing;
|
if let Some(font_impl) = font_impl {
|
||||||
if let Some(font_impl) = font_impl {
|
x += font_impl.pair_kerning(
|
||||||
x += font_impl.pair_kerning(last_glyph_info.id, replacement_glyph_info.id);
|
last_glyph_info.id,
|
||||||
}
|
replacement_glyph_alloc.id,
|
||||||
|
section.format.font_id.size,
|
||||||
|
pixels_per_point,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.glyphs.push(Glyph {
|
row.glyphs.push(Glyph {
|
||||||
chr: overflow_character,
|
chr: overflow_character,
|
||||||
pos: pos2(x, f32::NAN),
|
pos: pos2(x, f32::NAN),
|
||||||
advance_width: replacement_glyph_info.advance_width,
|
advance_width: replacement_glyph_alloc.advance_width,
|
||||||
line_height,
|
line_height,
|
||||||
font_impl_height: font_impl.map_or(0.0, |f| f.row_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_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)),
|
||||||
font_height: font.row_height(),
|
font_height: font.row_height(font_size),
|
||||||
font_ascent: font.ascent(),
|
font_ascent: font.ascent(font_size),
|
||||||
uv_rect: replacement_glyph_info.uv_rect,
|
uv_rect: replacement_glyph_alloc.uv_rect,
|
||||||
section_index,
|
section_index,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let section_index = row.section_index_at_start;
|
let section_index = row.section_index_at_start;
|
||||||
let section = &job.sections[section_index as usize];
|
let section = &job.sections[section_index as usize];
|
||||||
let font = fonts.font(§ion.format.font_id);
|
let font = fonts.font(§ion.format.font_id.family);
|
||||||
let line_height = row_height(section, font);
|
let font_size = section.format.font_id.size;
|
||||||
|
let line_height = row_height(section, font, font_size);
|
||||||
|
|
||||||
let x = 0.0; // TODO(emilk): heed paragraph leading_space 😬
|
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 (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()
|
||||||
|
};
|
||||||
|
|
||||||
row.glyphs.push(Glyph {
|
row.glyphs.push(Glyph {
|
||||||
chr: overflow_character,
|
chr: overflow_character,
|
||||||
pos: pos2(x, f32::NAN),
|
pos: pos2(x, f32::NAN),
|
||||||
advance_width: replacement_glyph_info.advance_width,
|
advance_width: glyph_alloc.advance_width,
|
||||||
line_height,
|
line_height,
|
||||||
font_impl_height: font_impl.map_or(0.0, |f| f.row_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_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)),
|
||||||
font_height: font.row_height(),
|
font_height: font.row_height(font_size),
|
||||||
font_ascent: font.ascent(),
|
font_ascent: font.ascent(font_size),
|
||||||
uv_rect: replacement_glyph_info.uv_rect,
|
uv_rect: glyph_alloc.uv_rect,
|
||||||
section_index,
|
section_index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -498,30 +518,47 @@ fn replace_last_glyph_with_overflow_character(
|
|||||||
|
|
||||||
let section = &job.sections[last_glyph.section_index as usize];
|
let section = &job.sections[last_glyph.section_index as usize];
|
||||||
let extra_letter_spacing = section.format.extra_letter_spacing;
|
let extra_letter_spacing = section.format.extra_letter_spacing;
|
||||||
let font = fonts.font(§ion.format.font_id);
|
let pixels_per_point = fonts.pixels_per_point();
|
||||||
|
let font = fonts.font(§ion.format.font_id.family);
|
||||||
|
let font_size = section.format.font_id.size;
|
||||||
|
|
||||||
if let Some(prev_glyph) = prev_glyph {
|
if let Some(prev_glyph) = prev_glyph {
|
||||||
let prev_glyph_id = font.font_impl_and_glyph_info(prev_glyph.chr).1.id;
|
let prev_glyph_id = font
|
||||||
|
.font_impl_and_glyph_alloc(prev_glyph.chr, font_size, pixels_per_point)
|
||||||
|
.1
|
||||||
|
.id;
|
||||||
|
|
||||||
// Undo kerning with previous glyph:
|
// Undo kerning with previous glyph:
|
||||||
let (font_impl, glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
let (font_impl, glyph_alloc) =
|
||||||
|
font.font_impl_and_glyph_alloc(last_glyph.chr, font_size, pixels_per_point);
|
||||||
last_glyph.pos.x -= extra_letter_spacing;
|
last_glyph.pos.x -= extra_letter_spacing;
|
||||||
if let Some(font_impl) = font_impl {
|
if let Some(font_impl) = font_impl {
|
||||||
last_glyph.pos.x -= font_impl.pair_kerning(prev_glyph_id, glyph_info.id);
|
last_glyph.pos.x -= font_impl.pair_kerning(
|
||||||
|
prev_glyph_id,
|
||||||
|
glyph_alloc.id,
|
||||||
|
font_size,
|
||||||
|
pixels_per_point,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the glyph:
|
// Replace the glyph:
|
||||||
last_glyph.chr = overflow_character;
|
last_glyph.chr = overflow_character;
|
||||||
let (font_impl, glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
let (font_impl, glyph_alloc) =
|
||||||
last_glyph.advance_width = glyph_info.advance_width;
|
font.font_impl_and_glyph_alloc(last_glyph.chr, font_size, pixels_per_point);
|
||||||
last_glyph.font_impl_ascent = font_impl.map_or(0.0, |f| f.ascent());
|
last_glyph.advance_width = glyph_alloc.advance_width;
|
||||||
last_glyph.font_impl_height = font_impl.map_or(0.0, |f| f.row_height());
|
last_glyph.font_impl_ascent = font_impl.map_or(0.0, |f| f.ascent(font_size));
|
||||||
last_glyph.uv_rect = glyph_info.uv_rect;
|
last_glyph.font_impl_height = font_impl.map_or(0.0, |f| f.row_height(font_size));
|
||||||
|
last_glyph.uv_rect = glyph_alloc.uv_rect;
|
||||||
|
|
||||||
// Reapply kerning:
|
// Reapply kerning:
|
||||||
last_glyph.pos.x += extra_letter_spacing;
|
last_glyph.pos.x += extra_letter_spacing;
|
||||||
if let Some(font_impl) = font_impl {
|
if let Some(font_impl) = font_impl {
|
||||||
last_glyph.pos.x += font_impl.pair_kerning(prev_glyph_id, glyph_info.id);
|
last_glyph.pos.x += font_impl.pair_kerning(
|
||||||
|
prev_glyph_id,
|
||||||
|
glyph_alloc.id,
|
||||||
|
font_size,
|
||||||
|
pixels_per_point,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we're within width budget:
|
// Check if we're within width budget:
|
||||||
@@ -532,13 +569,20 @@ fn replace_last_glyph_with_overflow_character(
|
|||||||
// We didn't fit - pop the last glyph and try again.
|
// We didn't fit - pop the last glyph and try again.
|
||||||
row.glyphs.pop();
|
row.glyphs.pop();
|
||||||
} else {
|
} else {
|
||||||
|
let Some(section) = &job.sections.get(last_glyph.section_index as usize) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let pixels_per_point = fonts.pixels_per_point();
|
||||||
|
let font = fonts.font(§ion.format.font_id.family);
|
||||||
|
let font_size = section.format.font_id.size;
|
||||||
// Just replace and be done with it.
|
// Just replace and be done with it.
|
||||||
last_glyph.chr = overflow_character;
|
last_glyph.chr = overflow_character;
|
||||||
let (font_impl, glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
let (font_impl, glyph_alloc) =
|
||||||
last_glyph.advance_width = glyph_info.advance_width;
|
font.font_impl_and_glyph_alloc(last_glyph.chr, font_size, pixels_per_point);
|
||||||
last_glyph.font_impl_ascent = font_impl.map_or(0.0, |f| f.ascent());
|
last_glyph.advance_width = glyph_alloc.advance_width;
|
||||||
last_glyph.font_impl_height = font_impl.map_or(0.0, |f| f.row_height());
|
last_glyph.font_impl_ascent = font_impl.map_or(0.0, |f| f.ascent(font_size));
|
||||||
last_glyph.uv_rect = glyph_info.uv_rect;
|
last_glyph.font_impl_height = font_impl.map_or(0.0, |f| f.row_height(font_size));
|
||||||
|
last_glyph.uv_rect = glyph_alloc.uv_rect;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user