Skip to content

Commit

Permalink
Use ab_glyph instead of rusttype for font rendering (emilk#490)
Browse files Browse the repository at this point in the history
* Use ab_glyph instead of rusttype for font rendering

* address review feedback
  • Loading branch information
bnjbvr authored Jun 24, 2021
1 parent 63bddb6 commit e22c242
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 54 deletions.
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Examples: `Vec2, Pos2, Rect, lerp, remap`

Example: `Shape::Circle { center, radius, fill, stroke }`

Depends on `emath`, [`rusttype`](https://crates.io/crates/rusttype), [`atomic_refcell`](https://crates.io/crates/atomic_refcell), [`ahash`](https://crates.io/crates/ahash).
Depends on `emath`, [`ab_glyph`](https://crates.io/crates/ab_glyph), [`atomic_refcell`](https://crates.io/crates/atomic_refcell), [`ahash`](https://crates.io/crates/ahash).

### `epi`
Depends only on `egui`.
Expand Down
31 changes: 28 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ ui.label(format!("Hello '{}', age {}", name, age));
* Extensible: [easy to write your own widgets for egui](https://github.com/emilk/egui/blob/master/egui_demo_lib/src/apps/demo/toggle_switch.rs)
* Modular: You should be able to use small parts of egui and combine them in new ways
* Safe: there is no `unsafe` code in egui
* Minimal dependencies: [`ahash`](https://crates.io/crates/ahash) [`atomic_refcell`](https://crates.io/crates/atomic_refcell) [`ordered-float`](https://crates.io/crates/ordered-float) [`rusttype`](https://crates.io/crates/rusttype).
* Minimal dependencies: [`ahash`](https://crates.io/crates/ahash) [`atomic_refcell`](https://crates.io/crates/atomic_refcell) [`ordered-float`](https://crates.io/crates/ordered-float) [`ab_glyph`](https://crates.io/crates/ab_glyph).

egui is *not* a framework. egui is a library you call into, not an environment you program for.

Expand Down
2 changes: 1 addition & 1 deletion epaint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ include = [
[dependencies]
emath = { version = "0.12.0", path = "../emath" }

ab_glyph = "0.2.11"
ahash = { version = "0.7", features = ["std"], default-features = false }
atomic_refcell = { version = "0.1", optional = true } # Used instead of parking_lot when you are always using epaint in a single thread. About as fast as parking_lot. Panics on multi-threaded use.
cint = { version = "^0.2.2", optional = true }
ordered-float = { version = "2", default-features = false }
parking_lot = { version = "0.11", optional = true } # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
rusttype = "0.9"
serde = { version = "1", features = ["derive"], optional = true }

[features]
Expand Down
62 changes: 30 additions & 32 deletions epaint/src/text/font.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
use std::sync::Arc;

use {
ahash::AHashMap,
rusttype::{point, Scale},
};

use crate::{
mutex::{Mutex, RwLock},
text::{
Expand All @@ -13,7 +6,9 @@ use crate::{
},
TextureAtlas,
};
use ahash::AHashMap;
use emath::{vec2, Vec2};
use std::sync::Arc;

// ----------------------------------------------------------------------------

Expand All @@ -32,7 +27,7 @@ pub struct UvRect {

#[derive(Clone, Copy, Debug)]
pub struct GlyphInfo {
id: rusttype::GlyphId,
id: ab_glyph::GlyphId,

/// Unit: points.
pub advance_width: f32,
Expand All @@ -44,7 +39,7 @@ pub struct GlyphInfo {
impl Default for GlyphInfo {
fn default() -> Self {
Self {
id: rusttype::GlyphId(0),
id: ab_glyph::GlyphId(0),
advance_width: 0.0,
uv_rect: None,
}
Expand All @@ -56,7 +51,7 @@ impl Default for GlyphInfo {
/// A specific font with a size.
/// The interface uses points as the unit for everything.
pub struct FontImpl {
rusttype_font: Arc<rusttype::Font<'static>>,
ab_glyph_font: ab_glyph::FontArc,
/// Maximum character height
scale_in_pixels: f32,
height_in_points: f32,
Expand All @@ -71,7 +66,7 @@ impl FontImpl {
pub fn new(
atlas: Arc<Mutex<TextureAtlas>>,
pixels_per_point: f32,
rusttype_font: Arc<rusttype::Font<'static>>,
ab_glyph_font: ab_glyph::FontArc,
scale_in_points: f32,
y_offset: f32,
) -> FontImpl {
Expand All @@ -96,7 +91,7 @@ impl FontImpl {
let y_offset = (y_offset * pixels_per_point).round() / pixels_per_point;

Self {
rusttype_font,
ab_glyph_font,
scale_in_pixels,
height_in_points,
y_offset,
Expand All @@ -115,8 +110,9 @@ impl FontImpl {
}

// Add new character:
let glyph = self.rusttype_font.glyph(c);
if glyph.id().0 == 0 {
use ab_glyph::Font as _;
let glyph_id = self.ab_glyph_font.glyph_id(c);
if glyph_id.0 == 0 {
if invisible_char(c) {
// hack
let glyph_info = GlyphInfo::default();
Expand All @@ -128,7 +124,8 @@ impl FontImpl {
} else {
let mut glyph_info = allocate_glyph(
&mut self.atlas.lock(),
glyph,
&self.ab_glyph_font,
glyph_id,
self.scale_in_pixels,
self.y_offset,
self.pixels_per_point,
Expand All @@ -147,12 +144,13 @@ impl FontImpl {

pub fn pair_kerning(
&self,
last_glyph_id: rusttype::GlyphId,
glyph_id: rusttype::GlyphId,
last_glyph_id: ab_glyph::GlyphId,
glyph_id: ab_glyph::GlyphId,
) -> f32 {
let scale_in_pixels = Scale::uniform(self.scale_in_pixels);
self.rusttype_font
.pair_kerning(scale_in_pixels, last_glyph_id, glyph_id)
use ab_glyph::{Font as _, ScaleFont};
self.ab_glyph_font
.as_scaled(self.scale_in_pixels)
.kern(last_glyph_id, glyph_id)
/ self.pixels_per_point
}

Expand Down Expand Up @@ -618,20 +616,22 @@ fn invisible_char(c: char) -> bool {

fn allocate_glyph(
atlas: &mut TextureAtlas,
glyph: rusttype::Glyph<'static>,
font: &ab_glyph::FontArc,
glyph_id: ab_glyph::GlyphId,
scale_in_pixels: f32,
y_offset: f32,
pixels_per_point: f32,
) -> GlyphInfo {
assert!(glyph.id().0 != 0);
assert!(glyph_id.0 != 0);
use ab_glyph::{Font as _, ScaleFont};

let glyph = glyph.scaled(Scale::uniform(scale_in_pixels));
let glyph = glyph.positioned(point(0.0, 0.0));
let glyph =
glyph_id.with_scale_and_position(scale_in_pixels, ab_glyph::Point { x: 0.0, y: 0.0 });

let uv_rect = if let Some(bb) = glyph.pixel_bounding_box() {
let uv_rect = font.outline_glyph(glyph).and_then(|glyph| {
let bb = glyph.px_bounds();
let glyph_width = bb.width() as usize;
let glyph_height = bb.height() as usize;

if glyph_width == 0 || glyph_height == 0 {
None
} else {
Expand All @@ -658,15 +658,13 @@ fn allocate_glyph(
),
})
}
} else {
// No bounding box. Maybe a space?
None
};
});

let advance_width_in_points = glyph.unpositioned().h_metrics().advance_width / pixels_per_point;
let advance_width_in_points =
font.as_scaled(scale_in_pixels).h_advance(glyph_id) / pixels_per_point;

GlyphInfo {
id: glyph.id(),
id: glyph_id,
advance_width: advance_width_in_points,
uv_rect,
}
Expand Down
27 changes: 11 additions & 16 deletions epaint/src/text/fonts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ pub enum FontFamily {
/// The data of a `.ttf` or `.otf` file.
pub type FontData = std::borrow::Cow<'static, [u8]>;

fn rusttype_font_from_font_data(name: &str, data: &FontData) -> rusttype::Font<'static> {
fn ab_glyph_font_from_font_data(name: &str, data: &FontData) -> ab_glyph::FontArc {
match data {
std::borrow::Cow::Borrowed(bytes) => rusttype::Font::try_from_bytes(bytes),
std::borrow::Cow::Owned(bytes) => rusttype::Font::try_from_vec(bytes.clone()),
std::borrow::Cow::Borrowed(bytes) => ab_glyph::FontArc::try_from_slice(bytes),
std::borrow::Cow::Owned(bytes) => ab_glyph::FontArc::try_from_vec(bytes.clone()),
}
.unwrap_or_else(|| panic!("Error parsing {:?} TTF/OTF font file", name))
.unwrap_or_else(|err| panic!("Error parsing {:?} TTF/OTF font file: {}", name, err))
}

/// Describes the font data and the sizes to use.
Expand Down Expand Up @@ -484,7 +484,7 @@ impl GalleyCache {
struct FontImplCache {
atlas: Arc<Mutex<TextureAtlas>>,
pixels_per_point: f32,
rusttype_fonts: BTreeMap<String, Arc<rusttype::Font<'static>>>,
ab_glyph_fonts: BTreeMap<String, ab_glyph::FontArc>,

/// Map font names and size to the cached `FontImpl`.
/// Can't have f32 in a HashMap or BTreeMap, so let's do a linear search
Expand All @@ -497,27 +497,22 @@ impl FontImplCache {
pixels_per_point: f32,
definitions: &super::FontDefinitions,
) -> Self {
let rusttype_fonts = definitions
let ab_glyph_fonts = definitions
.font_data
.iter()
.map(|(name, font_data)| {
(
name.clone(),
Arc::new(rusttype_font_from_font_data(name, font_data)),
)
})
.map(|(name, font_data)| (name.clone(), ab_glyph_font_from_font_data(name, font_data)))
.collect();

Self {
atlas,
pixels_per_point,
rusttype_fonts,
ab_glyph_fonts,
cache: Default::default(),
}
}

pub fn rusttype_font(&self, font_name: &str) -> Arc<rusttype::Font<'static>> {
self.rusttype_fonts
pub fn ab_glyph_font(&self, font_name: &str) -> ab_glyph::FontArc {
self.ab_glyph_fonts
.get(font_name)
.unwrap_or_else(|| panic!("No font data found for {:?}", font_name))
.clone()
Expand Down Expand Up @@ -546,7 +541,7 @@ impl FontImplCache {
let font_impl = Arc::new(FontImpl::new(
self.atlas.clone(),
self.pixels_per_point,
self.rusttype_font(font_name),
self.ab_glyph_font(font_name),
scale_in_points,
y_offset,
));
Expand Down

0 comments on commit e22c242

Please sign in to comment.