mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
Improve text rendering in light mode (#7290)
This changes how we convert glyph coverage to alpha (and ultimately a color), but only in light mode. This is a bit of a hack, because it doesn't fix dark-on-light text in _dark mode_ (if you have any), but for the common case this PR is a huge improvement. You can also tweak this yourself now using `Visuals::text_alpha_from_coverage` or from the UI (bottom of the image):  ## Before / After   ## Black text Before/after If you think the text above looks too weak, it's only because of the default text color. Here's how it looks like with perfectly `#000000` black text:  
This commit is contained in:
@@ -1876,6 +1876,16 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reset_font_atlas(&self) {
|
||||
let pixels_per_point = self.pixels_per_point();
|
||||
let fonts = self.read(|ctx| {
|
||||
ctx.fonts
|
||||
.get(&pixels_per_point.into())
|
||||
.map(|current_fonts| current_fonts.lock().fonts.definitions().clone())
|
||||
});
|
||||
self.memory_mut(|mem| mem.new_font_definitions = fonts);
|
||||
}
|
||||
|
||||
/// Tell `egui` which fonts to use.
|
||||
///
|
||||
/// The default `egui` fonts only support latin and cyrillic alphabets,
|
||||
@@ -2011,10 +2021,19 @@ impl Context {
|
||||
/// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
|
||||
pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
|
||||
let style = style.into();
|
||||
self.options_mut(|opt| match theme {
|
||||
Theme::Dark => opt.dark_style = style,
|
||||
Theme::Light => opt.light_style = style,
|
||||
let mut recreate_font_atlas = false;
|
||||
self.options_mut(|opt| {
|
||||
let dest = match theme {
|
||||
Theme::Dark => &mut opt.dark_style,
|
||||
Theme::Light => &mut opt.light_style,
|
||||
};
|
||||
recreate_font_atlas =
|
||||
dest.visuals.text_alpha_from_coverage != style.visuals.text_alpha_from_coverage;
|
||||
*dest = style;
|
||||
});
|
||||
if recreate_font_atlas {
|
||||
self.reset_font_atlas();
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`crate::Visuals`] used by all subsequent windows, panels etc.
|
||||
@@ -2411,7 +2430,28 @@ impl ContextImpl {
|
||||
}
|
||||
|
||||
// Inform the backend of all textures that have been updated (including font atlas).
|
||||
let textures_delta = self.tex_manager.0.write().take_delta();
|
||||
let textures_delta = {
|
||||
// HACK to get much nicer looking text in light mode.
|
||||
// This assumes all text is black-on-white in light mode,
|
||||
// and white-on-black in dark mode, which is not necessarily true,
|
||||
// but often close enough.
|
||||
// Of course this fails for cases when there is black-on-white text in dark mode,
|
||||
// and white-on-black text in light mode.
|
||||
|
||||
let text_alpha_from_coverage =
|
||||
self.memory.options.style().visuals.text_alpha_from_coverage;
|
||||
|
||||
let mut textures_delta = self.tex_manager.0.write().take_delta();
|
||||
|
||||
for (_, delta) in &mut textures_delta.set {
|
||||
if let ImageData::Font(font) = &mut delta.image {
|
||||
delta.image =
|
||||
ImageData::Color(font.to_color_image(text_alpha_from_coverage).into());
|
||||
}
|
||||
}
|
||||
|
||||
textures_delta
|
||||
};
|
||||
|
||||
let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
|
||||
|
||||
@@ -3009,9 +3049,17 @@ impl Context {
|
||||
|
||||
options.ui(ui);
|
||||
|
||||
let text_alpha_from_coverage_changed =
|
||||
prev_options.style().visuals.text_alpha_from_coverage
|
||||
!= options.style().visuals.text_alpha_from_coverage;
|
||||
|
||||
if options != prev_options {
|
||||
self.options_mut(move |o| *o = options);
|
||||
}
|
||||
|
||||
if text_alpha_from_coverage_changed {
|
||||
ui.ctx().reset_font_atlas();
|
||||
}
|
||||
}
|
||||
|
||||
fn fonts_tweak_ui(&self, ui: &mut Ui) {
|
||||
|
||||
@@ -408,11 +408,11 @@ impl Options {
|
||||
.show(ui, |ui| {
|
||||
theme_preference.radio_buttons(ui);
|
||||
|
||||
std::sync::Arc::make_mut(match theme {
|
||||
let style = std::sync::Arc::make_mut(match theme {
|
||||
Theme::Dark => dark_style,
|
||||
Theme::Light => light_style,
|
||||
})
|
||||
.ui(ui);
|
||||
});
|
||||
style.ui(ui);
|
||||
});
|
||||
|
||||
CollapsingHeader::new("✒ Painting")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#![allow(clippy::if_same_then_else)]
|
||||
|
||||
use emath::Align;
|
||||
use epaint::{CornerRadius, Shadow, Stroke, text::FontTweak};
|
||||
use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, text::FontTweak};
|
||||
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
@@ -921,6 +921,9 @@ pub struct Visuals {
|
||||
/// this is more to provide a convenient summary of the rest of the settings.
|
||||
pub dark_mode: bool,
|
||||
|
||||
/// ADVANCED: Controls how we render text.
|
||||
pub text_alpha_from_coverage: AlphaFromCoverage,
|
||||
|
||||
/// Override default text color for all text.
|
||||
///
|
||||
/// This is great for setting the color of text for any widget.
|
||||
@@ -1374,6 +1377,7 @@ impl Visuals {
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
dark_mode: true,
|
||||
text_alpha_from_coverage: AlphaFromCoverage::DARK_MODE_DEFAULT,
|
||||
override_text_color: None,
|
||||
weak_text_alpha: 0.6,
|
||||
weak_text_color: None,
|
||||
@@ -1436,6 +1440,7 @@ impl Visuals {
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
dark_mode: false,
|
||||
text_alpha_from_coverage: AlphaFromCoverage::LIGHT_MODE_DEFAULT,
|
||||
widgets: Widgets::light(),
|
||||
selection: Selection::light(),
|
||||
hyperlink_color: Color32::from_rgb(0, 155, 255),
|
||||
@@ -2068,6 +2073,7 @@ impl Visuals {
|
||||
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
||||
let Self {
|
||||
dark_mode,
|
||||
text_alpha_from_coverage,
|
||||
override_text_color: _,
|
||||
weak_text_alpha,
|
||||
weak_text_color,
|
||||
@@ -2216,6 +2222,10 @@ impl Visuals {
|
||||
"Weak text color",
|
||||
);
|
||||
});
|
||||
|
||||
ui.add_space(4.0);
|
||||
|
||||
text_alpha_from_coverage_ui(ui, text_alpha_from_coverage);
|
||||
});
|
||||
|
||||
ui.collapsing("Text cursor", |ui| {
|
||||
@@ -2326,6 +2336,40 @@ impl Visuals {
|
||||
}
|
||||
}
|
||||
|
||||
fn text_alpha_from_coverage_ui(ui: &mut Ui, text_alpha_from_coverage: &mut AlphaFromCoverage) {
|
||||
let mut dark_mode_special =
|
||||
*text_alpha_from_coverage == AlphaFromCoverage::TwoCoverageMinusCoverageSq;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Text rendering:");
|
||||
|
||||
ui.checkbox(&mut dark_mode_special, "Dark-mode special");
|
||||
|
||||
if dark_mode_special {
|
||||
*text_alpha_from_coverage = AlphaFromCoverage::TwoCoverageMinusCoverageSq;
|
||||
} else {
|
||||
let mut gamma = match text_alpha_from_coverage {
|
||||
AlphaFromCoverage::Linear => 1.0,
|
||||
AlphaFromCoverage::Gamma(gamma) => *gamma,
|
||||
AlphaFromCoverage::TwoCoverageMinusCoverageSq => 0.5, // approximately the same
|
||||
};
|
||||
|
||||
ui.add(
|
||||
DragValue::new(&mut gamma)
|
||||
.speed(0.01)
|
||||
.range(0.1..=4.0)
|
||||
.prefix("Gamma: "),
|
||||
);
|
||||
|
||||
if gamma == 1.0 {
|
||||
*text_alpha_from_coverage = AlphaFromCoverage::Linear;
|
||||
} else {
|
||||
*text_alpha_from_coverage = AlphaFromCoverage::Gamma(gamma);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl TextCursorStyle {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
let Self {
|
||||
|
||||
Reference in New Issue
Block a user