Browse Source

Use ab_glyph instead of rusttype for font rendering (#490)

* Use ab_glyph instead of rusttype for font rendering

* address review feedback
pull/522/head
Benjamin Bouvier 3 years ago
committed by GitHub
parent
commit
e22c242d17
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ARCHITECTURE.md
  2. 31
      Cargo.lock
  3. 2
      README.md
  4. 2
      epaint/Cargo.toml
  5. 62
      epaint/src/text/font.rs
  6. 27
      epaint/src/text/fonts.rs

2
ARCHITECTURE.md

@ -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`.

31
Cargo.lock

@ -1,5 +1,15 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ab_glyph"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af0ac006645f86f20f6c6fa4dcaef920bf803df819123626f9440e35835e7d80"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser 0.12.0",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.4"
@ -850,13 +860,13 @@ dependencies = [
name = "epaint"
version = "0.12.0"
dependencies = [
"ab_glyph",
"ahash",
"atomic_refcell",
"cint",
"emath",
"ordered-float",
"parking_lot",
"rusttype",
"serde",
]
@ -1721,7 +1731,16 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3"
dependencies = [
"ttf-parser",
"ttf-parser 0.6.2",
]
[[package]]
name = "owned_ttf_parser"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3c7a20e3f122223e68eef6ca58e39bc1ea8a1d83418ba4c2c1ba189d2ee355"
dependencies = [
"ttf-parser 0.12.2",
]
[[package]]
@ -2003,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
"owned_ttf_parser 0.6.0",
]
[[package]]
@ -2336,6 +2355,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
[[package]]
name = "ttf-parser"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c56097738aec26a3f347edf99f5c84d9d4e3a4b8ce5513ebca85cb621fc7c50"
[[package]]
name = "tts"
version = "0.16.0"

2
README.md

@ -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.

2
epaint/Cargo.toml

@ -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]

62
epaint/src/text/font.rs

@ -1,10 +1,3 @@
use std::sync::Arc;
use {
ahash::AHashMap,
rusttype::{point, Scale},
};
use crate::{
mutex::{Mutex, RwLock},
text::{
@ -13,7 +6,9 @@ use crate::{
},
TextureAtlas,
};
use ahash::AHashMap;
use emath::{vec2, Vec2};
use std::sync::Arc;
// ----------------------------------------------------------------------------
@ -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,
@ -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,
}
@ -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,
@ -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 {
@ -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,
@ -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();
@ -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,
@ -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
}
@ -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 {
@ -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,
}

27
epaint/src/text/fonts.rs

@ -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.
@ -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
@ -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()
@ -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,
));

Loading…
Cancel
Save