Use ab_glyph instead of rusttype for font rendering (#490)
* Use ab_glyph instead of rusttype for font rendering * address review feedback
This commit is contained in:
parent
63bddb67f8
commit
e22c242d17
6 changed files with 72 additions and 54 deletions
|
@ -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
generated
31
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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…
Reference in a new issue