2020-10-13 22:29:11 +00:00
|
|
|
|
use crate::{
|
2022-04-15 13:18:04 +00:00
|
|
|
|
mutex::{Mutex, RwLock},
|
2021-01-10 14:42:46 +00:00
|
|
|
|
TextureAtlas,
|
2020-10-13 22:29:11 +00:00
|
|
|
|
};
|
2021-01-10 14:42:46 +00:00
|
|
|
|
use emath::{vec2, Vec2};
|
2021-06-24 15:35:56 +00:00
|
|
|
|
use std::collections::BTreeSet;
|
2022-04-15 13:18:04 +00:00
|
|
|
|
use std::sync::Arc;
|
2020-05-19 20:28:57 +00:00
|
|
|
|
|
2019-01-10 20:09:37 +00:00
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
2022-12-05 08:29:59 +00:00
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
2022-02-22 12:20:46 +00:00
|
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
2019-01-05 20:23:53 +00:00
|
|
|
|
pub struct UvRect {
|
2019-01-19 16:10:28 +00:00
|
|
|
|
/// X/Y offset for nice rendering (unit: points).
|
|
|
|
|
pub offset: Vec2,
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
|
|
|
|
|
/// Screen size (in points) of this glyph.
|
|
|
|
|
/// Note that the height is different from the font height.
|
2019-01-19 16:10:28 +00:00
|
|
|
|
pub size: Vec2,
|
2019-01-04 13:14:32 +00:00
|
|
|
|
|
2019-01-19 16:10:28 +00:00
|
|
|
|
/// Top left corner UV in texture.
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
pub min: [u16; 2],
|
2019-01-04 13:14:32 +00:00
|
|
|
|
|
2019-01-19 16:10:28 +00:00
|
|
|
|
/// Bottom right corner (exclusive).
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
pub max: [u16; 2],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UvRect {
|
|
|
|
|
pub fn is_nothing(&self) -> bool {
|
|
|
|
|
self.min == self.max
|
|
|
|
|
}
|
2019-01-04 13:14:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:58:26 +00:00
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
2019-01-05 20:23:53 +00:00
|
|
|
|
pub struct GlyphInfo {
|
2023-02-08 09:01:47 +00:00
|
|
|
|
/// 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.
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
pub(crate) id: ab_glyph::GlyphId,
|
2019-01-05 20:23:53 +00:00
|
|
|
|
|
2019-01-19 16:10:28 +00:00
|
|
|
|
/// Unit: points.
|
2019-01-05 20:23:53 +00:00
|
|
|
|
pub advance_width: f32,
|
|
|
|
|
|
2022-12-14 14:15:29 +00:00
|
|
|
|
/// Texture coordinates.
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
pub uv_rect: UvRect,
|
2019-01-05 20:23:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-12 18:30:01 +00:00
|
|
|
|
impl Default for GlyphInfo {
|
2022-12-14 14:15:29 +00:00
|
|
|
|
/// Basically a zero-width space.
|
2020-12-12 18:30:01 +00:00
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
2021-06-24 10:13:57 +00:00
|
|
|
|
id: ab_glyph::GlyphId(0),
|
2020-12-12 18:30:01 +00:00
|
|
|
|
advance_width: 0.0,
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
uv_rect: Default::default(),
|
2020-12-12 18:30:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
/// A specific font with a size.
|
2019-01-19 16:10:28 +00:00
|
|
|
|
/// The interface uses points as the unit for everything.
|
2020-12-11 22:39:32 +00:00
|
|
|
|
pub struct FontImpl {
|
2022-02-22 08:47:31 +00:00
|
|
|
|
name: String,
|
2021-06-24 10:13:57 +00:00
|
|
|
|
ab_glyph_font: ab_glyph::FontArc,
|
2019-01-04 13:14:32 +00:00
|
|
|
|
/// Maximum character height
|
2022-01-24 13:32:36 +00:00
|
|
|
|
scale_in_pixels: u32,
|
2021-01-10 09:20:50 +00:00
|
|
|
|
height_in_points: f32,
|
|
|
|
|
// move each character by this much (hack)
|
|
|
|
|
y_offset: f32,
|
2019-01-19 16:10:28 +00:00
|
|
|
|
pixels_per_point: f32,
|
2022-08-19 09:46:38 +00:00
|
|
|
|
glyph_info_cache: RwLock<ahash::HashMap<char, GlyphInfo>>, // TODO(emilk): standard Mutex
|
2019-01-12 23:19:53 +00:00
|
|
|
|
atlas: Arc<Mutex<TextureAtlas>>,
|
2019-01-04 13:14:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-11 22:39:32 +00:00
|
|
|
|
impl FontImpl {
|
2019-01-19 16:10:28 +00:00
|
|
|
|
pub fn new(
|
|
|
|
|
atlas: Arc<Mutex<TextureAtlas>>,
|
|
|
|
|
pixels_per_point: f32,
|
2022-02-22 08:47:31 +00:00
|
|
|
|
name: String,
|
2021-06-24 10:13:57 +00:00
|
|
|
|
ab_glyph_font: ab_glyph::FontArc,
|
2022-01-24 13:32:36 +00:00
|
|
|
|
scale_in_pixels: u32,
|
2022-02-12 17:22:42 +00:00
|
|
|
|
y_offset_points: f32,
|
2020-12-11 22:39:32 +00:00
|
|
|
|
) -> FontImpl {
|
2022-01-24 13:32:36 +00:00
|
|
|
|
assert!(scale_in_pixels > 0);
|
2020-09-09 10:14:53 +00:00
|
|
|
|
assert!(pixels_per_point > 0.0);
|
|
|
|
|
|
2022-01-24 13:32:36 +00:00
|
|
|
|
let height_in_points = scale_in_pixels as f32 / pixels_per_point;
|
2021-05-12 17:41:45 +00:00
|
|
|
|
|
2022-05-21 14:53:25 +00:00
|
|
|
|
// TODO(emilk): use these font metrics?
|
2022-02-12 17:22:42 +00:00
|
|
|
|
// use ab_glyph::ScaleFont as _;
|
|
|
|
|
// let scaled = ab_glyph_font.as_scaled(scale_in_pixels as f32);
|
|
|
|
|
// dbg!(scaled.ascent());
|
|
|
|
|
// dbg!(scaled.descent());
|
|
|
|
|
// dbg!(scaled.line_gap());
|
2021-01-10 09:20:50 +00:00
|
|
|
|
|
2021-05-28 17:51:10 +00:00
|
|
|
|
// Round to closest pixel:
|
2022-02-12 17:22:42 +00:00
|
|
|
|
let y_offset = (y_offset_points * pixels_per_point).round() / pixels_per_point;
|
2021-05-28 17:51:10 +00:00
|
|
|
|
|
2021-01-10 09:13:16 +00:00
|
|
|
|
Self {
|
2022-02-22 08:47:31 +00:00
|
|
|
|
name,
|
2021-06-24 10:13:57 +00:00
|
|
|
|
ab_glyph_font,
|
2019-01-19 16:10:28 +00:00
|
|
|
|
scale_in_pixels,
|
2021-01-10 09:20:50 +00:00
|
|
|
|
height_in_points,
|
|
|
|
|
y_offset,
|
2019-01-19 16:10:28 +00:00
|
|
|
|
pixels_per_point,
|
2020-12-11 23:53:07 +00:00
|
|
|
|
glyph_info_cache: Default::default(),
|
2019-01-04 13:14:32 +00:00
|
|
|
|
atlas,
|
|
|
|
|
}
|
2020-04-18 15:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 14:15:29 +00:00
|
|
|
|
/// Code points that will always be replaced by the replacement character.
|
|
|
|
|
///
|
|
|
|
|
/// See also [`invisible_char`].
|
2022-02-22 08:47:31 +00:00
|
|
|
|
fn ignore_character(&self, chr: char) -> bool {
|
|
|
|
|
if self.name == "emoji-icon-font" {
|
|
|
|
|
// HACK: https://github.com/emilk/egui/issues/1284 https://github.com/jslegers/emoji-icon-font/issues/18
|
|
|
|
|
// Don't show the wrong fullwidth capital letters:
|
|
|
|
|
if 'S' <= chr && chr <= 'Y' {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matches!(
|
|
|
|
|
chr,
|
|
|
|
|
// Strip out a religious symbol with secondary nefarious interpretation:
|
|
|
|
|
'\u{534d}' | '\u{5350}' |
|
|
|
|
|
|
|
|
|
|
// Ignore ubuntu-specific stuff in `Ubuntu-Light.ttf`:
|
|
|
|
|
'\u{E0FF}' | '\u{EFFD}' | '\u{F0FF}' | '\u{F200}'
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 15:35:56 +00:00
|
|
|
|
/// An un-ordered iterator over all supported characters.
|
|
|
|
|
fn characters(&self) -> impl Iterator<Item = char> + '_ {
|
|
|
|
|
use ab_glyph::Font as _;
|
|
|
|
|
self.ab_glyph_font
|
|
|
|
|
.codepoint_ids()
|
|
|
|
|
.map(|(_, chr)| chr)
|
2022-02-22 08:47:31 +00:00
|
|
|
|
.filter(|&chr| !self.ignore_character(chr))
|
2021-06-24 15:35:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-11 22:46:02 +00:00
|
|
|
|
/// `\n` will result in `None`
|
|
|
|
|
fn glyph_info(&self, c: char) -> Option<GlyphInfo> {
|
2020-09-09 12:24:13 +00:00
|
|
|
|
{
|
2020-12-11 23:53:07 +00:00
|
|
|
|
if let Some(glyph_info) = self.glyph_info_cache.read().get(&c) {
|
2020-12-11 22:46:02 +00:00
|
|
|
|
return Some(*glyph_info);
|
2020-09-09 12:24:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-18 15:09:01 +00:00
|
|
|
|
|
2022-02-22 08:47:31 +00:00
|
|
|
|
if self.ignore_character(c) {
|
2022-12-14 14:15:29 +00:00
|
|
|
|
return None; // these will result in the replacement character when rendering
|
2022-02-22 08:47:31 +00:00
|
|
|
|
}
|
2021-10-23 12:38:26 +00:00
|
|
|
|
|
|
|
|
|
if c == '\t' {
|
|
|
|
|
if let Some(space) = self.glyph_info(' ') {
|
|
|
|
|
let glyph_info = GlyphInfo {
|
|
|
|
|
advance_width: crate::text::TAB_SIZE as f32 * space.advance_width,
|
|
|
|
|
..GlyphInfo::default()
|
|
|
|
|
};
|
|
|
|
|
self.glyph_info_cache.write().insert(c, glyph_info);
|
|
|
|
|
return Some(glyph_info);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-21 16:33:23 +00:00
|
|
|
|
if c == '\u{2009}' {
|
|
|
|
|
// Thin space, often used as thousands deliminator: 1 234 567 890
|
|
|
|
|
// https://www.compart.com/en/unicode/U+2009
|
|
|
|
|
// https://en.wikipedia.org/wiki/Thin_space
|
|
|
|
|
|
|
|
|
|
if let Some(space) = self.glyph_info(' ') {
|
|
|
|
|
let em = self.height_in_points; // TODO(emilk): is this right?
|
|
|
|
|
let advance_width = f32::min(em / 6.0, space.advance_width * 0.5);
|
|
|
|
|
let glyph_info = GlyphInfo {
|
|
|
|
|
advance_width,
|
|
|
|
|
..GlyphInfo::default()
|
|
|
|
|
};
|
|
|
|
|
self.glyph_info_cache.write().insert(c, glyph_info);
|
|
|
|
|
return Some(glyph_info);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 14:15:29 +00:00
|
|
|
|
if invisible_char(c) {
|
|
|
|
|
let glyph_info = GlyphInfo::default();
|
|
|
|
|
self.glyph_info_cache.write().insert(c, glyph_info);
|
|
|
|
|
return Some(glyph_info);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-22 08:47:31 +00:00
|
|
|
|
// Add new character:
|
|
|
|
|
use ab_glyph::Font as _;
|
|
|
|
|
let glyph_id = self.ab_glyph_font.glyph_id(c);
|
|
|
|
|
|
2021-06-24 10:13:57 +00:00
|
|
|
|
if glyph_id.0 == 0 {
|
2022-12-14 14:15:29 +00:00
|
|
|
|
None // unsupported character
|
2020-12-12 18:30:01 +00:00
|
|
|
|
} else {
|
2021-10-23 12:38:26 +00:00
|
|
|
|
let glyph_info = allocate_glyph(
|
2020-12-12 18:30:01 +00:00
|
|
|
|
&mut self.atlas.lock(),
|
2021-06-24 10:13:57 +00:00
|
|
|
|
&self.ab_glyph_font,
|
|
|
|
|
glyph_id,
|
2022-01-24 13:32:36 +00:00
|
|
|
|
self.scale_in_pixels as f32,
|
2021-01-10 09:20:50 +00:00
|
|
|
|
self.y_offset,
|
2020-12-12 18:30:01 +00:00
|
|
|
|
self.pixels_per_point,
|
|
|
|
|
);
|
2021-03-23 19:06:52 +00:00
|
|
|
|
|
2020-12-12 18:30:01 +00:00
|
|
|
|
self.glyph_info_cache.write().insert(c, glyph_info);
|
|
|
|
|
Some(glyph_info)
|
|
|
|
|
}
|
2019-01-04 13:14:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
#[inline]
|
2020-12-11 22:39:32 +00:00
|
|
|
|
pub fn pair_kerning(
|
|
|
|
|
&self,
|
2021-06-24 10:13:57 +00:00
|
|
|
|
last_glyph_id: ab_glyph::GlyphId,
|
|
|
|
|
glyph_id: ab_glyph::GlyphId,
|
2020-12-11 22:39:32 +00:00
|
|
|
|
) -> f32 {
|
2021-06-24 10:13:57 +00:00
|
|
|
|
use ab_glyph::{Font as _, ScaleFont};
|
|
|
|
|
self.ab_glyph_font
|
2022-01-24 13:32:36 +00:00
|
|
|
|
.as_scaled(self.scale_in_pixels as f32)
|
2021-06-24 10:13:57 +00:00
|
|
|
|
.kern(last_glyph_id, glyph_id)
|
2020-12-11 22:39:32 +00:00
|
|
|
|
/ self.pixels_per_point
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Height of one row of text. In points
|
2021-03-28 21:16:19 +00:00
|
|
|
|
#[inline(always)]
|
2020-12-11 22:39:32 +00:00
|
|
|
|
pub fn row_height(&self) -> f32 {
|
2021-01-10 09:20:50 +00:00
|
|
|
|
self.height_in_points
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 21:16:19 +00:00
|
|
|
|
#[inline(always)]
|
2020-12-11 23:53:07 +00:00
|
|
|
|
pub fn pixels_per_point(&self) -> f32 {
|
|
|
|
|
self.pixels_per_point
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-11 23:53:07 +00:00
|
|
|
|
type FontIndex = usize;
|
|
|
|
|
|
2022-05-21 14:53:25 +00:00
|
|
|
|
// TODO(emilk): rename?
|
2022-04-03 16:18:35 +00:00
|
|
|
|
/// Wrapper over multiple [`FontImpl`] (e.g. a primary + fallbacks for emojis)
|
2020-12-11 22:39:32 +00:00
|
|
|
|
pub struct Font {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
fonts: Vec<Arc<FontImpl>>,
|
2021-08-16 20:17:31 +00:00
|
|
|
|
/// Lazily calculated.
|
2022-07-11 21:08:48 +00:00
|
|
|
|
characters: Option<BTreeSet<char>>,
|
2020-12-12 18:30:01 +00:00
|
|
|
|
replacement_glyph: (FontIndex, GlyphInfo),
|
2020-12-11 23:53:07 +00:00
|
|
|
|
pixels_per_point: f32,
|
|
|
|
|
row_height: f32,
|
2022-08-19 09:46:38 +00:00
|
|
|
|
glyph_info_cache: ahash::HashMap<char, (FontIndex, GlyphInfo)>,
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Font {
|
2022-01-24 13:32:36 +00:00
|
|
|
|
pub fn new(fonts: Vec<Arc<FontImpl>>) -> Self {
|
2020-12-26 20:20:55 +00:00
|
|
|
|
if fonts.is_empty() {
|
2021-03-29 19:24:09 +00:00
|
|
|
|
return Self {
|
|
|
|
|
fonts,
|
2022-01-24 13:32:36 +00:00
|
|
|
|
characters: None,
|
2021-03-29 19:24:09 +00:00
|
|
|
|
replacement_glyph: Default::default(),
|
2021-10-20 13:40:06 +00:00
|
|
|
|
pixels_per_point: 1.0,
|
2021-03-29 19:24:09 +00:00
|
|
|
|
row_height: 0.0,
|
|
|
|
|
glyph_info_cache: Default::default(),
|
|
|
|
|
};
|
2020-12-26 20:20:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-11 23:53:07 +00:00
|
|
|
|
let pixels_per_point = fonts[0].pixels_per_point();
|
|
|
|
|
let row_height = fonts[0].row_height();
|
|
|
|
|
|
2020-12-12 18:30:01 +00:00
|
|
|
|
let mut slf = Self {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
fonts,
|
2022-01-24 13:32:36 +00:00
|
|
|
|
characters: None,
|
2020-12-12 18:30:01 +00:00
|
|
|
|
replacement_glyph: Default::default(),
|
2020-12-11 23:53:07 +00:00
|
|
|
|
pixels_per_point,
|
|
|
|
|
row_height,
|
|
|
|
|
glyph_info_cache: Default::default(),
|
|
|
|
|
};
|
2020-12-18 12:06:50 +00:00
|
|
|
|
|
|
|
|
|
const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square
|
|
|
|
|
const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback
|
|
|
|
|
|
2020-12-12 18:30:01 +00:00
|
|
|
|
let replacement_glyph = slf
|
2020-12-18 12:06:50 +00:00
|
|
|
|
.glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR)
|
|
|
|
|
.or_else(|| slf.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR))
|
2020-12-12 18:30:01 +00:00
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
|
panic!(
|
2020-12-18 12:06:50 +00:00
|
|
|
|
"Failed to find replacement characters {:?} or {:?}",
|
|
|
|
|
PRIMARY_REPLACEMENT_CHAR, FALLBACK_REPLACEMENT_CHAR
|
2020-12-12 18:30:01 +00:00
|
|
|
|
)
|
|
|
|
|
});
|
|
|
|
|
slf.replacement_glyph = replacement_glyph;
|
2021-01-10 09:13:16 +00:00
|
|
|
|
|
2022-01-26 13:42:44 +00:00
|
|
|
|
slf
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:58:26 +00:00
|
|
|
|
pub fn preload_characters(&mut self, s: &str) {
|
|
|
|
|
for c in s.chars() {
|
|
|
|
|
self.glyph_info(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-26 13:42:44 +00:00
|
|
|
|
pub fn preload_common_characters(&mut self) {
|
2021-01-10 09:13:16 +00:00
|
|
|
|
// Preload the printable ASCII characters [32, 126] (which excludes control codes):
|
|
|
|
|
const FIRST_ASCII: usize = 32; // 32 == space
|
|
|
|
|
const LAST_ASCII: usize = 126;
|
|
|
|
|
for c in (FIRST_ASCII..=LAST_ASCII).map(|c| c as u8 as char) {
|
2022-01-26 13:42:44 +00:00
|
|
|
|
self.glyph_info(c);
|
2021-01-10 09:13:16 +00:00
|
|
|
|
}
|
2022-01-26 13:42:44 +00:00
|
|
|
|
self.glyph_info('°');
|
|
|
|
|
self.glyph_info(crate::text::PASSWORD_REPLACEMENT_CHAR);
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:58:26 +00:00
|
|
|
|
/// All supported characters.
|
2022-01-24 13:32:36 +00:00
|
|
|
|
pub fn characters(&mut self) -> &BTreeSet<char> {
|
|
|
|
|
self.characters.get_or_insert_with(|| {
|
2021-08-16 20:17:31 +00:00
|
|
|
|
let mut characters = BTreeSet::new();
|
|
|
|
|
for font in &self.fonts {
|
|
|
|
|
characters.extend(font.characters());
|
|
|
|
|
}
|
2022-01-24 13:32:36 +00:00
|
|
|
|
characters
|
|
|
|
|
})
|
2021-03-29 19:24:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 19:07:19 +00:00
|
|
|
|
#[inline(always)]
|
2020-12-11 22:39:32 +00:00
|
|
|
|
pub fn round_to_pixel(&self, point: f32) -> f32 {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
(point * self.pixels_per_point).round() / self.pixels_per_point
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Height of one row of text. In points
|
2021-03-28 21:16:19 +00:00
|
|
|
|
#[inline(always)]
|
2020-12-11 22:39:32 +00:00
|
|
|
|
pub fn row_height(&self) -> f32 {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
self.row_height
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
pub fn uv_rect(&self, c: char) -> UvRect {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
self.glyph_info_cache
|
|
|
|
|
.get(&c)
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
.map(|gi| gi.1.uv_rect)
|
|
|
|
|
.unwrap_or_default()
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 18:57:51 +00:00
|
|
|
|
/// Width of this character in points.
|
2022-01-24 13:32:36 +00:00
|
|
|
|
pub fn glyph_width(&mut self, c: char) -> f32 {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
self.glyph_info(c).1.advance_width
|
2020-12-11 22:39:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:58:26 +00:00
|
|
|
|
/// 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 🤦♂️
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Can we display all the glyphs in this text?
|
|
|
|
|
pub fn has_glyphs(&mut self, s: &str) -> bool {
|
|
|
|
|
s.chars().all(|c| self.has_glyph(c))
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-18 12:06:50 +00:00
|
|
|
|
/// `\n` will (intentionally) show up as the replacement character.
|
2022-01-24 13:32:36 +00:00
|
|
|
|
fn glyph_info(&mut self, c: char) -> (FontIndex, GlyphInfo) {
|
|
|
|
|
if let Some(font_index_glyph_info) = self.glyph_info_cache.get(&c) {
|
|
|
|
|
return *font_index_glyph_info;
|
2020-12-11 23:53:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-18 12:06:50 +00:00
|
|
|
|
let font_index_glyph_info = self.glyph_info_no_cache_or_fallback(c);
|
2020-12-12 18:30:01 +00:00
|
|
|
|
let font_index_glyph_info = font_index_glyph_info.unwrap_or(self.replacement_glyph);
|
2022-01-24 13:32:36 +00:00
|
|
|
|
self.glyph_info_cache.insert(c, font_index_glyph_info);
|
2020-12-11 23:53:07 +00:00
|
|
|
|
font_index_glyph_info
|
|
|
|
|
}
|
|
|
|
|
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
#[inline]
|
2022-01-24 13:32:36 +00:00
|
|
|
|
pub(crate) fn glyph_info_and_font_impl(&mut self, c: char) -> (Option<&FontImpl>, GlyphInfo) {
|
2021-10-20 13:40:06 +00:00
|
|
|
|
if self.fonts.is_empty() {
|
|
|
|
|
return (None, self.replacement_glyph.1);
|
|
|
|
|
}
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
let (font_index, glyph_info) = self.glyph_info(c);
|
|
|
|
|
let font_impl = &self.fonts[font_index];
|
2021-10-20 13:40:06 +00:00
|
|
|
|
(Some(font_impl), glyph_info)
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-24 13:32:36 +00:00
|
|
|
|
fn glyph_info_no_cache_or_fallback(&mut self, c: char) -> Option<(FontIndex, GlyphInfo)> {
|
2020-12-11 23:53:07 +00:00
|
|
|
|
for (font_index, font_impl) in self.fonts.iter().enumerate() {
|
|
|
|
|
if let Some(glyph_info) = font_impl.glyph_info(c) {
|
2022-01-24 13:32:36 +00:00
|
|
|
|
self.glyph_info_cache.insert(c, (font_index, glyph_info));
|
2020-12-11 23:53:07 +00:00
|
|
|
|
return Some((font_index, glyph_info));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
2021-02-06 14:19:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 14:15:29 +00:00
|
|
|
|
/// Code points that will always be invisible (zero width).
|
|
|
|
|
///
|
|
|
|
|
/// See also [`FontImpl::ignore_character`].
|
2021-05-02 17:50:06 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
fn invisible_char(c: char) -> bool {
|
2022-12-14 14:15:29 +00:00
|
|
|
|
if c == '\r' {
|
|
|
|
|
// A character most vile and pernicious. Don't display it.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-02 17:50:06 +00:00
|
|
|
|
// See https://github.com/emilk/egui/issues/336
|
|
|
|
|
|
|
|
|
|
// From https://www.fileformat.info/info/unicode/category/Cf/list.htm
|
2023-01-23 19:24:38 +00:00
|
|
|
|
|
|
|
|
|
// TODO(emilk): heed bidi characters
|
|
|
|
|
|
|
|
|
|
matches!(
|
|
|
|
|
c,
|
|
|
|
|
'\u{200B}' // ZERO WIDTH SPACE
|
|
|
|
|
| '\u{200C}' // ZERO WIDTH NON-JOINER
|
|
|
|
|
| '\u{200D}' // ZERO WIDTH JOINER
|
|
|
|
|
| '\u{200E}' // LEFT-TO-RIGHT MARK
|
|
|
|
|
| '\u{200F}' // RIGHT-TO-LEFT MARK
|
|
|
|
|
| '\u{202A}' // LEFT-TO-RIGHT EMBEDDING
|
|
|
|
|
| '\u{202B}' // RIGHT-TO-LEFT EMBEDDING
|
|
|
|
|
| '\u{202C}' // POP DIRECTIONAL FORMATTING
|
|
|
|
|
| '\u{202D}' // LEFT-TO-RIGHT OVERRIDE
|
|
|
|
|
| '\u{202E}' // RIGHT-TO-LEFT OVERRIDE
|
|
|
|
|
| '\u{2060}' // WORD JOINER
|
|
|
|
|
| '\u{2061}' // FUNCTION APPLICATION
|
|
|
|
|
| '\u{2062}' // INVISIBLE TIMES
|
|
|
|
|
| '\u{2063}' // INVISIBLE SEPARATOR
|
|
|
|
|
| '\u{2064}' // INVISIBLE PLUS
|
|
|
|
|
| '\u{2066}' // LEFT-TO-RIGHT ISOLATE
|
|
|
|
|
| '\u{2067}' // RIGHT-TO-LEFT ISOLATE
|
|
|
|
|
| '\u{2068}' // FIRST STRONG ISOLATE
|
|
|
|
|
| '\u{2069}' // POP DIRECTIONAL ISOLATE
|
|
|
|
|
| '\u{206A}' // INHIBIT SYMMETRIC SWAPPING
|
|
|
|
|
| '\u{206B}' // ACTIVATE SYMMETRIC SWAPPING
|
|
|
|
|
| '\u{206C}' // INHIBIT ARABIC FORM SHAPING
|
|
|
|
|
| '\u{206D}' // ACTIVATE ARABIC FORM SHAPING
|
|
|
|
|
| '\u{206E}' // NATIONAL DIGIT SHAPES
|
|
|
|
|
| '\u{206F}' // NOMINAL DIGIT SHAPES
|
|
|
|
|
| '\u{FEFF}' // ZERO WIDTH NO-BREAK SPACE
|
|
|
|
|
)
|
2021-05-02 17:50:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:24:13 +00:00
|
|
|
|
fn allocate_glyph(
|
|
|
|
|
atlas: &mut TextureAtlas,
|
2021-06-24 10:13:57 +00:00
|
|
|
|
font: &ab_glyph::FontArc,
|
|
|
|
|
glyph_id: ab_glyph::GlyphId,
|
2020-09-09 12:24:13 +00:00
|
|
|
|
scale_in_pixels: f32,
|
2021-01-10 09:20:50 +00:00
|
|
|
|
y_offset: f32,
|
2020-09-09 12:24:13 +00:00
|
|
|
|
pixels_per_point: f32,
|
2020-12-12 18:30:01 +00:00
|
|
|
|
) -> GlyphInfo {
|
2021-06-24 10:13:57 +00:00
|
|
|
|
assert!(glyph_id.0 != 0);
|
|
|
|
|
use ab_glyph::{Font as _, ScaleFont};
|
2020-09-09 12:24:13 +00:00
|
|
|
|
|
2021-06-24 10:13:57 +00:00
|
|
|
|
let glyph =
|
|
|
|
|
glyph_id.with_scale_and_position(scale_in_pixels, ab_glyph::Point { x: 0.0, y: 0.0 });
|
2020-09-09 12:24:13 +00:00
|
|
|
|
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
let uv_rect = font.outline_glyph(glyph).map(|glyph| {
|
2021-06-24 10:13:57 +00:00
|
|
|
|
let bb = glyph.px_bounds();
|
2020-09-09 12:24:13 +00:00
|
|
|
|
let glyph_width = bb.width() as usize;
|
|
|
|
|
let glyph_height = bb.height() as usize;
|
2020-12-12 14:03:12 +00:00
|
|
|
|
if glyph_width == 0 || glyph_height == 0 {
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
UvRect::default()
|
2020-12-12 14:03:12 +00:00
|
|
|
|
} else {
|
2022-01-22 10:23:12 +00:00
|
|
|
|
let (glyph_pos, image) = atlas.allocate((glyph_width, glyph_height));
|
2020-12-12 14:03:12 +00:00
|
|
|
|
glyph.draw(|x, y, v| {
|
|
|
|
|
if v > 0.0 {
|
|
|
|
|
let px = glyph_pos.0 + x as usize;
|
|
|
|
|
let py = glyph_pos.1 + y as usize;
|
2022-03-23 15:49:49 +00:00
|
|
|
|
image[(px, py)] = v;
|
2020-12-12 14:03:12 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2020-09-09 12:24:13 +00:00
|
|
|
|
|
2022-07-11 21:08:48 +00:00
|
|
|
|
let offset_in_pixels = vec2(bb.min.x, scale_in_pixels + bb.min.y);
|
2021-01-10 09:20:50 +00:00
|
|
|
|
let offset = offset_in_pixels / pixels_per_point + y_offset * Vec2::Y;
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
UvRect {
|
2021-01-10 09:20:50 +00:00
|
|
|
|
offset,
|
2020-12-12 14:03:12 +00:00
|
|
|
|
size: vec2(glyph_width as f32, glyph_height as f32) / pixels_per_point,
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
min: [glyph_pos.0 as u16, glyph_pos.1 as u16],
|
|
|
|
|
max: [
|
2020-12-12 14:03:12 +00:00
|
|
|
|
(glyph_pos.0 + glyph_width) as u16,
|
|
|
|
|
(glyph_pos.1 + glyph_height) as u16,
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
],
|
|
|
|
|
}
|
2020-12-12 14:03:12 +00:00
|
|
|
|
}
|
2021-06-24 10:13:57 +00:00
|
|
|
|
});
|
New text layout (#682)
This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`.
This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor.
One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley.
## Performance
Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!).
Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that.
All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
|
|
|
|
let uv_rect = uv_rect.unwrap_or_default();
|
2020-09-09 12:24:13 +00:00
|
|
|
|
|
2021-06-24 10:13:57 +00:00
|
|
|
|
let advance_width_in_points =
|
|
|
|
|
font.as_scaled(scale_in_pixels).h_advance(glyph_id) / pixels_per_point;
|
2020-09-09 12:24:13 +00:00
|
|
|
|
|
2020-12-12 18:30:01 +00:00
|
|
|
|
GlyphInfo {
|
2021-06-24 10:13:57 +00:00
|
|
|
|
id: glyph_id,
|
2020-09-09 12:24:13 +00:00
|
|
|
|
advance_width: advance_width_in_points,
|
|
|
|
|
uv_rect,
|
2020-12-12 18:30:01 +00:00
|
|
|
|
}
|
2020-09-09 12:24:13 +00:00
|
|
|
|
}
|