diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index 473c7302..41a9b3b7 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -564,19 +564,6 @@ impl Renderer { ); Cow::Borrowed(&image.pixels) } - epaint::ImageData::Font(image) => { - assert_eq!( - width as usize * height as usize, - image.pixels.len(), - "Mismatch between texture size and texel count" - ); - profiling::scope!("font -> sRGBA"); - Cow::Owned( - image - .srgba_pixels(Default::default()) - .collect::>(), - ) - } }; let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice()); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index abb03b1c..f3bc73ce 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -78,7 +78,7 @@ impl Default for WrappedTextureManager { // Will be filled in later let font_id = tex_mngr.alloc( "egui_font_texture".into(), - epaint::FontImage::new([0, 0]).into(), + epaint::ColorImage::filled([0, 0], Color32::TRANSPARENT).into(), Default::default(), ); assert_eq!( @@ -610,6 +610,8 @@ impl ContextImpl { log::trace!("Adding new fonts"); } + let text_alpha_from_coverage = self.memory.options.style().visuals.text_alpha_from_coverage; + let mut is_new = false; let fonts = self @@ -624,13 +626,14 @@ impl ContextImpl { Fonts::new( pixels_per_point, max_texture_side, + text_alpha_from_coverage, self.font_definitions.clone(), ) }); { profiling::scope!("Fonts::begin_pass"); - fonts.begin_pass(pixels_per_point, max_texture_side); + fonts.begin_pass(pixels_per_point, max_texture_side, text_alpha_from_coverage); } if is_new && self.memory.options.preload_font_glyphs { @@ -1921,16 +1924,6 @@ 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, @@ -2066,19 +2059,10 @@ 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>) { let style = style.into(); - 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; + self.options_mut(|opt| match theme { + Theme::Dark => opt.dark_style = style, + Theme::Light => opt.light_style = style, }); - if recreate_font_atlas { - self.reset_font_atlas(); - } } /// The [`crate::Visuals`] used by all subsequent windows, panels etc. @@ -2475,28 +2459,7 @@ impl ContextImpl { } // Inform the backend of all textures that have been updated (including font atlas). - 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 textures_delta = self.tex_manager.0.write().take_delta(); let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output); @@ -3094,17 +3057,9 @@ 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) { diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index b4daa932..7abc5ba4 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -467,7 +467,7 @@ pub use emath::{ remap_clamp, vec2, }; pub use epaint::{ - ClippedPrimitive, ColorImage, CornerRadius, FontImage, ImageData, Margin, Mesh, PaintCallback, + ClippedPrimitive, ColorImage, CornerRadius, ImageData, Margin, Mesh, PaintCallback, PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, mutex, text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak}, textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta}, diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index e0c86f0d..02f098e6 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -168,6 +168,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let fonts = egui::epaint::text::Fonts::new( pixels_per_point, max_texture_side, + egui::epaint::AlphaFromCoverage::default(), egui::FontDefinitions::default(), ); { @@ -210,7 +211,11 @@ pub fn criterion_benchmark(c: &mut Criterion) { let mut rng = rand::rng(); b.iter(|| { - fonts.begin_pass(pixels_per_point, max_texture_side); + fonts.begin_pass( + pixels_per_point, + max_texture_side, + egui::epaint::AlphaFromCoverage::default(), + ); // Delete a random character, simulating a user making an edit in a long file: let mut new_string = string.clone(); diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index a574cbb7..5833d73e 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -534,23 +534,6 @@ impl Painter { self.upload_texture_srgb(delta.pos, image.size, delta.options, data); } - egui::ImageData::Font(image) => { - assert_eq!( - image.width() * image.height(), - image.pixels.len(), - "Mismatch between texture size and texel count" - ); - - let data: Vec = { - profiling::scope!("font -> sRGBA"); - image - .srgba_pixels(Default::default()) - .flat_map(|a| a.to_array()) - .collect() - }; - - self.upload_texture_srgb(delta.pos, image.size, delta.options, &data); - } }; } diff --git a/crates/epaint/benches/benchmark.rs b/crates/epaint/benches/benchmark.rs index 676e1d0f..d4b10a21 100644 --- a/crates/epaint/benches/benchmark.rs +++ b/crates/epaint/benches/benchmark.rs @@ -1,8 +1,8 @@ use criterion::{Criterion, black_box, criterion_group, criterion_main}; use epaint::{ - ClippedShape, Color32, Mesh, PathStroke, Pos2, Rect, Shape, Stroke, TessellationOptions, - Tessellator, TextureAtlas, Vec2, pos2, tessellator::Path, + AlphaFromCoverage, ClippedShape, Color32, Mesh, PathStroke, Pos2, Rect, Shape, Stroke, + TessellationOptions, Tessellator, TextureAtlas, Vec2, pos2, tessellator::Path, }; #[global_allocator] @@ -66,7 +66,7 @@ fn tessellate_circles(c: &mut Criterion) { let pixels_per_point = 2.0; let options = TessellationOptions::default(); - let atlas = TextureAtlas::new([4096, 256]); + let atlas = TextureAtlas::new([4096, 256], AlphaFromCoverage::default()); let font_tex_size = atlas.size(); let prepared_discs = atlas.prepared_discs(); diff --git a/crates/epaint/src/image.rs b/crates/epaint/src/image.rs index 6b40714c..e14ea869 100644 --- a/crates/epaint/src/image.rs +++ b/crates/epaint/src/image.rs @@ -7,24 +7,20 @@ use std::sync::Arc; /// /// To load an image file, see [`ColorImage::from_rgba_unmultiplied`]. /// -/// In order to paint the image on screen, you first need to convert it to +/// This is currently an enum with only one variant, but more image types may be added in the future. /// -/// See also: [`ColorImage`], [`FontImage`]. -#[derive(Clone, PartialEq)] +/// See also: [`ColorImage`]. +#[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum ImageData { /// RGBA image. Color(Arc), - - /// Used for the font texture. - Font(FontImage), } impl ImageData { pub fn size(&self) -> [usize; 2] { match self { Self::Color(image) => image.size, - Self::Font(image) => image.size, } } @@ -38,7 +34,7 @@ impl ImageData { pub fn bytes_per_pixel(&self) -> usize { match self { - Self::Color(_) | Self::Font(_) => 4, + Self::Color(_) => 4, } } } @@ -271,6 +267,37 @@ impl ColorImage { } Self::new([width, height], output) } + + /// Clone a sub-region as a new image. + pub fn region_by_pixels(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self { + assert!( + x + w <= self.width(), + "x + w should be <= self.width(), but x: {}, w: {}, width: {}", + x, + w, + self.width() + ); + assert!( + y + h <= self.height(), + "y + h should be <= self.height(), but y: {}, h: {}, height: {}", + y, + h, + self.height() + ); + + let mut pixels = Vec::with_capacity(w * h); + for y in y..y + h { + let offset = y * self.width() + x; + pixels.extend(&self.pixels[offset..(offset + w)]); + } + assert_eq!( + pixels.len(), + w * h, + "pixels.len should be w * h, but got {}", + pixels.len() + ); + Self::new([w, h], pixels) + } } impl std::ops::Index<(usize, usize)> for ColorImage { @@ -371,127 +398,12 @@ impl AlphaFromCoverage { } } -/// A single-channel image designed for the font texture. -/// -/// Each value represents "coverage", i.e. how much a texel is covered by a character. -/// -/// This is roughly interpreted as the opacity of a white image. -#[derive(Clone, Default, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct FontImage { - /// width, height - pub size: [usize; 2], - - /// The coverage value. - /// - /// Often you want to use [`Self::srgba_pixels`] instead. - pub pixels: Vec, -} - -impl FontImage { - pub fn new(size: [usize; 2]) -> Self { - Self { - size, - pixels: vec![0.0; size[0] * size[1]], - } - } - - #[inline] - pub fn width(&self) -> usize { - self.size[0] - } - - #[inline] - pub fn height(&self) -> usize { - self.size[1] - } - - /// Returns the textures as `sRGBA` premultiplied pixels, row by row, top to bottom. - #[inline] - pub fn srgba_pixels( - &self, - alpha_from_coverage: AlphaFromCoverage, - ) -> impl ExactSizeIterator + '_ { - self.pixels - .iter() - .map(move |&coverage| alpha_from_coverage.color_from_coverage(coverage)) - } - - /// Convert this coverage image to a [`ColorImage`]. - pub fn to_color_image(&self, alpha_from_coverage: AlphaFromCoverage) -> ColorImage { - profiling::function_scope!(); - let pixels = self.srgba_pixels(alpha_from_coverage).collect(); - ColorImage::new(self.size, pixels) - } - - /// Clone a sub-region as a new image. - pub fn region(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self { - assert!( - x + w <= self.width(), - "x + w should be <= self.width(), but x: {}, w: {}, width: {}", - x, - w, - self.width() - ); - assert!( - y + h <= self.height(), - "y + h should be <= self.height(), but y: {}, h: {}, height: {}", - y, - h, - self.height() - ); - - let mut pixels = Vec::with_capacity(w * h); - for y in y..y + h { - let offset = y * self.width() + x; - pixels.extend(&self.pixels[offset..(offset + w)]); - } - assert_eq!( - pixels.len(), - w * h, - "pixels.len should be w * h, but got {}", - pixels.len() - ); - Self { - size: [w, h], - pixels, - } - } -} - -impl std::ops::Index<(usize, usize)> for FontImage { - type Output = f32; - - #[inline] - fn index(&self, (x, y): (usize, usize)) -> &f32 { - let [w, h] = self.size; - assert!(x < w && y < h, "x: {x}, y: {y}, w: {w}, h: {h}"); - &self.pixels[y * w + x] - } -} - -impl std::ops::IndexMut<(usize, usize)> for FontImage { - #[inline] - fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut f32 { - let [w, h] = self.size; - assert!(x < w && y < h, "x: {x}, y: {y}, w: {w}, h: {h}"); - &mut self.pixels[y * w + x] - } -} - -impl From for ImageData { - #[inline(always)] - fn from(image: FontImage) -> Self { - Self::Font(image) - } -} - // ---------------------------------------------------------------------------- /// A change to an image. /// /// Either a whole new image, or an update to a rectangular region of it. -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[must_use = "The painter must take care of this"] pub struct ImageDelta { diff --git a/crates/epaint/src/lib.rs b/crates/epaint/src/lib.rs index 7afc7b14..f02889d9 100644 --- a/crates/epaint/src/lib.rs +++ b/crates/epaint/src/lib.rs @@ -50,7 +50,7 @@ pub use self::{ color::ColorMode, corner_radius::CornerRadius, corner_radius_f32::CornerRadiusF32, - image::{AlphaFromCoverage, ColorImage, FontImage, ImageData, ImageDelta}, + image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta}, margin::Margin, margin_f32::*, mesh::{Mesh, Mesh16, Vertex}, diff --git a/crates/epaint/src/shapes/text_shape.rs b/crates/epaint/src/shapes/text_shape.rs index bf9db964..b366c86c 100644 --- a/crates/epaint/src/shapes/text_shape.rs +++ b/crates/epaint/src/shapes/text_shape.rs @@ -179,7 +179,12 @@ mod tests { #[test] fn text_bounding_box_under_rotation() { - let fonts = Fonts::new(1.0, 1024, FontDefinitions::default()); + let fonts = Fonts::new( + 1.0, + 1024, + AlphaFromCoverage::default(), + FontDefinitions::default(), + ); let font = FontId::monospace(12.0); let mut t = crate::Shape::text( diff --git a/crates/epaint/src/text/font.rs b/crates/epaint/src/text/font.rs index 72fffaad..dd095c44 100644 --- a/crates/epaint/src/text/font.rs +++ b/crates/epaint/src/text/font.rs @@ -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 diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 6e90b566..5f900691 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -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(), ); diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 3777d860..7915bbf6 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -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; diff --git a/crates/epaint/src/texture_atlas.rs b/crates/epaint/src/texture_atlas.rs index 8e174e54..36dd1b48 100644 --- a/crates/epaint/src/texture_atlas.rs +++ b/crates/epaint/src/texture_atlas.rs @@ -1,6 +1,7 @@ +use ecolor::Color32; use emath::{Rect, remap_clamp}; -use crate::{FontImage, ImageDelta}; +use crate::{AlphaFromCoverage, ColorImage, ImageDelta}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct Rectu { @@ -57,7 +58,7 @@ pub struct PreparedDisc { /// More characters can be added, possibly expanding the texture. #[derive(Clone)] pub struct TextureAtlas { - image: FontImage, + image: ColorImage, /// What part of the image that is dirty dirty: Rectu, @@ -72,18 +73,22 @@ pub struct TextureAtlas { /// pre-rasterized discs of radii `2^i`, where `i` is the index. discs: Vec, + + /// Controls how to convert glyph coverage to alpha. + pub(crate) text_alpha_from_coverage: AlphaFromCoverage, } impl TextureAtlas { - pub fn new(size: [usize; 2]) -> Self { + pub fn new(size: [usize; 2], text_alpha_from_coverage: AlphaFromCoverage) -> Self { assert!(size[0] >= 1024, "Tiny texture atlas"); let mut atlas = Self { - image: FontImage::new(size), + image: ColorImage::filled(size, Color32::TRANSPARENT), dirty: Rectu::EVERYTHING, cursor: (0, 0), row_height: 0, overflowed: false, discs: vec![], // will be filled in below + text_alpha_from_coverage, }; // Make the top left pixel fully white for `WHITE_UV`, i.e. painting something with solid color: @@ -93,7 +98,7 @@ impl TextureAtlas { (0, 0), "Expected the first allocation to be at (0, 0), but was at {pos:?}" ); - image[pos] = 1.0; + image[pos] = Color32::WHITE; // Allocate a series of anti-aliased discs used to render small filled circles: // TODO(emilk): these circles can be packed A LOT better. @@ -116,7 +121,7 @@ impl TextureAtlas { let coverage = remap_clamp(distance_to_center, (r - 0.5)..=(r + 0.5), 1.0..=0.0); image[((x as i32 + hw + dx) as usize, (y as i32 + hw + dy) as usize)] = - coverage; + text_alpha_from_coverage.color_from_coverage(coverage); } } atlas.discs.push(PrerasterizedDisc { @@ -184,7 +189,7 @@ impl TextureAtlas { /// The full font atlas image. #[inline] - pub fn image(&self) -> &FontImage { + pub fn image(&self) -> &ColorImage { &self.image } @@ -200,14 +205,14 @@ impl TextureAtlas { } else { let pos = [dirty.min_x, dirty.min_y]; let size = [dirty.max_x - dirty.min_x, dirty.max_y - dirty.min_y]; - let region = self.image.region(pos, size); + let region = self.image.region_by_pixels(pos, size); Some(ImageDelta::partial(pos, region, texture_options)) } } /// Returns the coordinates of where the rect ended up, /// and invalidates the region. - pub fn allocate(&mut self, (w, h): (usize, usize)) -> ((usize, usize), &mut FontImage) { + pub fn allocate(&mut self, (w, h): (usize, usize)) -> ((usize, usize), &mut ColorImage) { /// On some low-precision GPUs (my old iPad) characters get muddled up /// if we don't add some empty pixels between the characters. /// On modern high-precision GPUs this is not needed. @@ -254,13 +259,15 @@ impl TextureAtlas { } } -fn resize_to_min_height(image: &mut FontImage, required_height: usize) -> bool { +fn resize_to_min_height(image: &mut ColorImage, required_height: usize) -> bool { while required_height >= image.height() { image.size[1] *= 2; // double the height } if image.width() * image.height() > image.pixels.len() { - image.pixels.resize(image.width() * image.height(), 0.0); + image + .pixels + .resize(image.width() * image.height(), Color32::TRANSPARENT); true } else { false diff --git a/crates/epaint/src/textures.rs b/crates/epaint/src/textures.rs index cc191a75..0944a905 100644 --- a/crates/epaint/src/textures.rs +++ b/crates/epaint/src/textures.rs @@ -271,7 +271,7 @@ pub enum TextureWrapMode { /// What has been allocated and freed during the last period. /// /// These are commands given to the integration painter. -#[derive(Clone, Default, PartialEq)] +#[derive(Clone, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[must_use = "The painter must take care of this"] pub struct TexturesDelta {