mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Make the font atlas use a color image (#7298)
* [x] I have followed the instructions in the PR template Splitting this out from the Parley work as requested. This removes `FontImage` and makes the font atlas use a `ColorImage`. It converts alpha to coverage at glyph-drawing time, not at delta-upload time. This doesn't do much now, but will allow for color emoji rendering once we start using Parley. I've changed things around so that we pass in `text_alpha_to_coverage` to the `Fonts` the same way we do with `pixels_per_point` and `max_texture_side`, reusing the existing code to check if the setting differs and recreating the font atlas if so. I'm not quite sure why this wasn't done in the first place. I've left `ImageData` as an enum for now, in case we want to add support for more texture pixel formats in the future (which I personally think would be worthwhile). If you'd like, I can just remove that enum entirely.
This commit is contained in:
@@ -279,12 +279,13 @@ impl FontImpl {
|
||||
} else {
|
||||
let glyph_pos = {
|
||||
let atlas = &mut self.atlas.lock();
|
||||
let text_alpha_from_coverage = atlas.text_alpha_from_coverage;
|
||||
let (glyph_pos, image) = atlas.allocate((glyph_width, glyph_height));
|
||||
glyph.draw(|x, y, v| {
|
||||
if 0.0 < v {
|
||||
let px = glyph_pos.0 + x as usize;
|
||||
let py = glyph_pos.1 + y as usize;
|
||||
image[(px, py)] = v;
|
||||
image[(px, py)] = text_alpha_from_coverage.color_from_coverage(v);
|
||||
}
|
||||
});
|
||||
glyph_pos
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
TextureAtlas,
|
||||
AlphaFromCoverage, TextureAtlas,
|
||||
mutex::{Mutex, MutexGuard},
|
||||
text::{
|
||||
Galley, LayoutJob, LayoutSection,
|
||||
@@ -430,36 +430,56 @@ impl Fonts {
|
||||
pub fn new(
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
text_alpha_from_coverage: AlphaFromCoverage,
|
||||
definitions: FontDefinitions,
|
||||
) -> Self {
|
||||
let fonts_and_cache = FontsAndCache {
|
||||
fonts: FontsImpl::new(pixels_per_point, max_texture_side, definitions),
|
||||
fonts: FontsImpl::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
text_alpha_from_coverage,
|
||||
definitions,
|
||||
),
|
||||
galley_cache: Default::default(),
|
||||
};
|
||||
Self(Arc::new(Mutex::new(fonts_and_cache)))
|
||||
}
|
||||
|
||||
/// Call at the start of each frame with the latest known
|
||||
/// `pixels_per_point` and `max_texture_side`.
|
||||
/// `pixels_per_point`, `max_texture_side`, and `text_alpha_from_coverage`.
|
||||
///
|
||||
/// Call after painting the previous frame, but before using [`Fonts`] for the new frame.
|
||||
///
|
||||
/// This function will react to changes in `pixels_per_point` and `max_texture_side`,
|
||||
/// This function will react to changes in `pixels_per_point`, `max_texture_side`, and `text_alpha_from_coverage`,
|
||||
/// as well as notice when the font atlas is getting full, and handle that.
|
||||
pub fn begin_pass(&self, pixels_per_point: f32, max_texture_side: usize) {
|
||||
pub fn begin_pass(
|
||||
&self,
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
text_alpha_from_coverage: AlphaFromCoverage,
|
||||
) {
|
||||
let mut fonts_and_cache = self.0.lock();
|
||||
|
||||
let pixels_per_point_changed = fonts_and_cache.fonts.pixels_per_point != pixels_per_point;
|
||||
let max_texture_side_changed = fonts_and_cache.fonts.max_texture_side != max_texture_side;
|
||||
let text_alpha_from_coverage_changed =
|
||||
fonts_and_cache.fonts.atlas.lock().text_alpha_from_coverage != text_alpha_from_coverage;
|
||||
let font_atlas_almost_full = fonts_and_cache.fonts.atlas.lock().fill_ratio() > 0.8;
|
||||
let needs_recreate =
|
||||
pixels_per_point_changed || max_texture_side_changed || font_atlas_almost_full;
|
||||
let needs_recreate = pixels_per_point_changed
|
||||
|| max_texture_side_changed
|
||||
|| text_alpha_from_coverage_changed
|
||||
|| font_atlas_almost_full;
|
||||
|
||||
if needs_recreate {
|
||||
let definitions = fonts_and_cache.fonts.definitions.clone();
|
||||
|
||||
*fonts_and_cache = FontsAndCache {
|
||||
fonts: FontsImpl::new(pixels_per_point, max_texture_side, definitions),
|
||||
fonts: FontsImpl::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
text_alpha_from_coverage,
|
||||
definitions,
|
||||
),
|
||||
galley_cache: Default::default(),
|
||||
};
|
||||
}
|
||||
@@ -497,7 +517,7 @@ impl Fonts {
|
||||
|
||||
/// The full font atlas image.
|
||||
#[inline]
|
||||
pub fn image(&self) -> crate::FontImage {
|
||||
pub fn image(&self) -> crate::ColorImage {
|
||||
self.lock().fonts.atlas.lock().image().clone()
|
||||
}
|
||||
|
||||
@@ -642,6 +662,7 @@ impl FontsImpl {
|
||||
pub fn new(
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
text_alpha_from_coverage: AlphaFromCoverage,
|
||||
definitions: FontDefinitions,
|
||||
) -> Self {
|
||||
assert!(
|
||||
@@ -651,7 +672,7 @@ impl FontsImpl {
|
||||
|
||||
let texture_width = max_texture_side.at_most(16 * 1024);
|
||||
let initial_height = 32; // Keep initial font atlas small, so it is fast to upload to GPU. This will expand as needed anyways.
|
||||
let atlas = TextureAtlas::new([texture_width, initial_height]);
|
||||
let atlas = TextureAtlas::new([texture_width, initial_height], text_alpha_from_coverage);
|
||||
|
||||
let atlas = Arc::new(Mutex::new(atlas));
|
||||
|
||||
@@ -1120,6 +1141,7 @@ mod tests {
|
||||
let mut fonts = FontsImpl::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
|
||||
|
||||
@@ -1034,11 +1034,18 @@ fn is_cjk_break_allowed(c: char) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::AlphaFromCoverage;
|
||||
|
||||
use super::{super::*, *};
|
||||
|
||||
#[test]
|
||||
fn test_zero_max_width() {
|
||||
let mut fonts = FontsImpl::new(1.0, 1024, FontDefinitions::default());
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
let mut layout_job = LayoutJob::single_section("W".into(), TextFormat::default());
|
||||
layout_job.wrap.max_width = 0.0;
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
@@ -1049,7 +1056,12 @@ mod tests {
|
||||
fn test_truncate_with_newline() {
|
||||
// No matter where we wrap, we should be appending the newline character.
|
||||
|
||||
let mut fonts = FontsImpl::new(1.0, 1024, FontDefinitions::default());
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
let text_format = TextFormat {
|
||||
font_id: FontId::monospace(12.0),
|
||||
..Default::default()
|
||||
@@ -1094,7 +1106,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_cjk() {
|
||||
let mut fonts = FontsImpl::new(1.0, 1024, FontDefinitions::default());
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
let mut layout_job = LayoutJob::single_section(
|
||||
"日本語とEnglishの混在した文章".into(),
|
||||
TextFormat::default(),
|
||||
@@ -1109,7 +1126,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_pre_cjk() {
|
||||
let mut fonts = FontsImpl::new(1.0, 1024, FontDefinitions::default());
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
let mut layout_job = LayoutJob::single_section(
|
||||
"日本語とEnglishの混在した文章".into(),
|
||||
TextFormat::default(),
|
||||
@@ -1124,7 +1146,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_truncate_width() {
|
||||
let mut fonts = FontsImpl::new(1.0, 1024, FontDefinitions::default());
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
let mut layout_job =
|
||||
LayoutJob::single_section("# DNA\nMore text".into(), TextFormat::default());
|
||||
layout_job.wrap.max_width = f32::INFINITY;
|
||||
|
||||
Reference in New Issue
Block a user