diff --git a/README.md b/README.md
index 7fe62213..5af7f5b7 100644
--- a/README.md
+++ b/README.md
@@ -209,5 +209,6 @@ Egui is under MIT OR Apache-2.0 license.
Fonts:
+* NotoEmoji-Regular.ttf, [google.com/get/noto](https://google.com/get/noto), [SIL Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL)
* ProggyClean.ttf, Copyright (c) 2004, 2005 Tristan Grimmer. MIT License.
* Ubuntu-Light.ttf by [Dalton Maag](http://www.daltonmaag.com/): [Ubuntu font licence](https://ubuntu.com/legal/font-licence)
diff --git a/egui/fonts/NotoEmoji-Regular.ttf b/egui/fonts/NotoEmoji-Regular.ttf
new file mode 100644
index 00000000..19b7badf
Binary files /dev/null and b/egui/fonts/NotoEmoji-Regular.ttf differ
diff --git a/OFL.txt b/egui/fonts/OFL.txt
old mode 100755
new mode 100644
similarity index 69%
rename from OFL.txt
rename to egui/fonts/OFL.txt
index daa25d00..d952d62c
--- a/OFL.txt
+++ b/egui/fonts/OFL.txt
@@ -1,93 +1,92 @@
-Copyright 2011 The Comfortaa Project Authors (https://github.com/alexeiva/comfortaa), with Reserved Font Name "Comfortaa".
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/egui/src/paint/font.rs b/egui/src/paint/font.rs
index 5db5940d..1065ee5d 100644
--- a/egui/src/paint/font.rs
+++ b/egui/src/paint/font.rs
@@ -50,7 +50,7 @@ pub struct FontImpl {
/// Maximum character height
scale_in_pixels: f32,
pixels_per_point: f32,
- glyph_infos: RwLock>,
+ glyph_info_cache: RwLock>,
atlas: Arc>,
}
@@ -71,7 +71,7 @@ impl FontImpl {
font,
scale_in_pixels,
pixels_per_point,
- glyph_infos: Default::default(),
+ glyph_info_cache: Default::default(),
atlas,
};
@@ -89,7 +89,7 @@ impl FontImpl {
/// `\n` will result in `None`
fn glyph_info(&self, c: char) -> Option {
{
- if let Some(glyph_info) = self.glyph_infos.read().get(&c) {
+ if let Some(glyph_info) = self.glyph_info_cache.read().get(&c) {
return Some(*glyph_info);
}
}
@@ -103,7 +103,7 @@ impl FontImpl {
self.pixels_per_point,
);
let glyph_info = glyph_info?;
- self.glyph_infos.write().insert(c, glyph_info);
+ self.glyph_info_cache.write().insert(c, glyph_info);
Some(glyph_info)
}
@@ -118,63 +118,135 @@ impl FontImpl {
/ self.pixels_per_point
}
+ /// Height of one row of text. In points
+ pub fn row_height(&self) -> f32 {
+ self.scale_in_pixels / self.pixels_per_point
+ }
+
+ pub fn pixels_per_point(&self) -> f32 {
+ self.pixels_per_point
+ }
+}
+
+type FontIndex = usize;
+
+// TODO: rename Layouter ?
+/// Wrapper over multiple `FontImpl` (commonly two: primary + emoji fallback)
+pub struct Font {
+ fonts: Vec>,
+ replacement_font_index_glyph_info: (FontIndex, GlyphInfo),
+ pixels_per_point: f32,
+ row_height: f32,
+ glyph_info_cache: RwLock>,
+}
+
+impl Font {
+ pub fn new(fonts: Vec>) -> Self {
+ assert!(!fonts.is_empty());
+ let replacement_glyph_font_index = 0;
+
+ let replacement_glyph_info = fonts[replacement_glyph_font_index]
+ .glyph_info(REPLACEMENT_CHAR)
+ .unwrap_or_else(|| {
+ panic!(
+ "Failed to find replacement character {:?}",
+ REPLACEMENT_CHAR
+ )
+ });
+
+ let replacement_font_index_glyph_info =
+ (replacement_glyph_font_index, replacement_glyph_info);
+
+ let pixels_per_point = fonts[0].pixels_per_point();
+ let row_height = fonts[0].row_height();
+
+ let slf = Self {
+ fonts,
+ replacement_font_index_glyph_info,
+ pixels_per_point,
+ row_height,
+ glyph_info_cache: Default::default(),
+ };
+ slf.glyph_info_cache
+ .write()
+ .insert(REPLACEMENT_CHAR, replacement_font_index_glyph_info);
+ slf
+ }
+
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
pub fn row_height(&self) -> f32 {
- self.scale_in_pixels / self.pixels_per_point
+ self.row_height
}
pub fn uv_rect(&self, c: char) -> Option {
- self.glyph_infos.read().get(&c).and_then(|gi| gi.uv_rect)
- }
-}
-
-// TODO: rename Layouter ?
-/// Wrapper over multiple `FontImpl` (commonly two: primary + emoji fallback)
-pub struct Font {
- font_impl: Arc,
- replacement_glyph_info: GlyphInfo,
-}
-
-impl Font {
- pub fn new(font_impl: Arc) -> Self {
- let replacement_glyph_info = font_impl.glyph_info(REPLACEMENT_CHAR).unwrap_or_else(|| {
- panic!(
- "Failed to find replacement character {:?}",
- REPLACEMENT_CHAR
- )
- });
- Self {
- font_impl,
- replacement_glyph_info,
- }
- }
-
- pub fn round_to_pixel(&self, point: f32) -> f32 {
- self.font_impl.round_to_pixel(point)
- }
-
- /// Height of one row of text. In points
- pub fn row_height(&self) -> f32 {
- self.font_impl.row_height()
- }
-
- pub fn uv_rect(&self, c: char) -> Option {
- self.font_impl.uv_rect(c)
+ self.glyph_info_cache
+ .read()
+ .get(&c)
+ .and_then(|gi| gi.1.uv_rect)
}
pub fn glyph_width(&self, c: char) -> f32 {
- self.glyph_info(c).advance_width
+ self.glyph_info(c).1.advance_width
}
/// `\n` will (intentionally) show up as '?' (`REPLACEMENT_CHAR`)
- fn glyph_info(&self, c: char) -> GlyphInfo {
- self.font_impl
- .glyph_info(c)
- .unwrap_or_else(|| self.replacement_glyph_info)
+ fn glyph_info(&self, c: char) -> (FontIndex, GlyphInfo) {
+ {
+ if let Some(glyph_info) = self.glyph_info_cache.read().get(&c) {
+ return *glyph_info;
+ }
+ }
+
+ let font_index_glyph_info = self.glyph_info_no_cache(c);
+ let font_index_glyph_info =
+ font_index_glyph_info.unwrap_or_else(|| self.replacement_font_index_glyph_info);
+ self.glyph_info_cache
+ .write()
+ .insert(c, font_index_glyph_info);
+ font_index_glyph_info
+ }
+
+ fn glyph_info_no_cache(&self, c: char) -> Option<(FontIndex, GlyphInfo)> {
+ for (font_index, font_impl) in self.fonts.iter().enumerate() {
+ if let Some(glyph_info) = font_impl.glyph_info(c) {
+ self.glyph_info_cache
+ .write()
+ .insert(c, (font_index, glyph_info));
+ return Some((font_index, glyph_info));
+ }
+ }
+ None
+ }
+
+ /// Typeset the given text onto one row.
+ /// Assumes there are no `\n` in the text.
+ /// Return `x_offsets`, one longer than the number of characters in the text.
+ fn layout_single_row_fragment(&self, text: &str) -> Vec {
+ let mut x_offsets = Vec::with_capacity(text.chars().count() + 1);
+ x_offsets.push(0.0);
+
+ let mut cursor_x_in_points = 0.0f32;
+ let mut last_glyph_id = None;
+
+ for c in text.chars() {
+ let (font_index, glyph_info) = self.glyph_info(c);
+ let font_impl = &self.fonts[font_index];
+
+ if let Some(last_glyph_id) = last_glyph_id {
+ cursor_x_in_points += font_impl.pair_kerning(last_glyph_id, glyph_info.id)
+ }
+ cursor_x_in_points += glyph_info.advance_width;
+ cursor_x_in_points = self.round_to_pixel(cursor_x_in_points);
+ last_glyph_id = Some(glyph_info.id);
+
+ x_offsets.push(cursor_x_in_points);
+ }
+
+ x_offsets
}
/// Typeset the given text onto one row.
@@ -272,32 +344,6 @@ impl Font {
galley
}
- /// Typeset the given text onto one row.
- /// Assumes there are no `\n` in the text.
- /// Return `x_offsets`, one longer than the number of characters in the text.
- fn layout_single_row_fragment(&self, text: &str) -> Vec {
- let mut x_offsets = Vec::with_capacity(text.chars().count() + 1);
- x_offsets.push(0.0);
-
- let mut cursor_x_in_points = 0.0f32;
- let mut last_glyph_id = None;
-
- for c in text.chars() {
- let glyph = self.glyph_info(c);
-
- if let Some(last_glyph_id) = last_glyph_id {
- cursor_x_in_points += self.font_impl.pair_kerning(last_glyph_id, glyph.id)
- }
- cursor_x_in_points += glyph.advance_width;
- cursor_x_in_points = self.round_to_pixel(cursor_x_in_points);
- last_glyph_id = Some(glyph.id);
-
- x_offsets.push(cursor_x_in_points);
- }
-
- x_offsets
- }
-
/// A paragraph is text with no line break character in it.
/// The text will be wrapped by the given `max_width_in_points`.
/// Always returns at least one row.
diff --git a/egui/src/paint/fonts.rs b/egui/src/paint/fonts.rs
index dbe725d3..67101368 100644
--- a/egui/src/paint/fonts.rs
+++ b/egui/src/paint/fonts.rs
@@ -7,7 +7,7 @@ use std::{
use crate::mutex::Mutex;
use super::{
- font::Font,
+ font::{Font, FontImpl},
texture_atlas::{Texture, TextureAtlas},
};
@@ -41,6 +41,9 @@ pub struct FontDefinitions {
/// Egui has built-in-default for these,
/// but you can override them if you like.
pub ttf_data: BTreeMap,
+
+ /// ttf data for emoji font, if any
+ pub emoji_ttf_data: Option<&'static [u8]>,
}
impl Default for FontDefinitions {
@@ -66,10 +69,13 @@ impl FontDefinitions {
ttf_data.insert(FontFamily::Monospace, monospace_typeface_data);
ttf_data.insert(FontFamily::VariableWidth, variable_typeface_data);
+ let emoji_ttf_data = include_bytes!("../../fonts/NotoEmoji-Regular.ttf");
+
Self {
pixels_per_point,
fonts,
ttf_data,
+ emoji_ttf_data: Some(emoji_ttf_data),
}
}
}
@@ -101,7 +107,7 @@ impl Fonts {
return;
}
- let mut atlas = TextureAtlas::new(512, 16); // TODO: better default?
+ let mut atlas = TextureAtlas::new(1024, 16); // TODO: better default?
{
// Make the top left pixel fully white:
@@ -117,22 +123,36 @@ impl Fonts {
pixels_per_point,
fonts,
ttf_data,
+ emoji_ttf_data,
} = definitions;
+
self.fonts = fonts
.into_iter()
.map(|(text_style, (family, size))| {
let typeface_data = ttf_data
.get(&family)
.unwrap_or_else(|| panic!("Missing TTF data for {:?}", family));
- let font_impl = super::font::FontImpl::new(
+
+ let font_impl = Arc::new(FontImpl::new(
atlas.clone(),
typeface_data,
size,
pixels_per_point,
- );
- let font_impl = Arc::new(font_impl);
+ ));
- (text_style, Font::new(font_impl))
+ let mut fonts = vec![font_impl];
+
+ if let Some(emoji_ttf_data) = emoji_ttf_data {
+ let emoji_font_impl = Arc::new(FontImpl::new(
+ atlas.clone(),
+ emoji_ttf_data,
+ size,
+ pixels_per_point,
+ ));
+ fonts.push(emoji_font_impl);
+ }
+
+ (text_style, Font::new(fonts))
})
.collect();