From 692e23ca33017741966f55adfa3fd6bcf56158ea Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 16 Sep 2024 23:25:27 -0600 Subject: [PATCH] Cleanup before release --- examples/custom-glyphs.rs | 88 +++++++++++++++++++-------------------- examples/text-sizes.rs | 1 + src/cache.rs | 17 ++++---- src/custom_glyph.rs | 10 +++-- src/lib.rs | 7 ++-- src/text_atlas.rs | 16 ++++--- src/text_render.rs | 27 ++++++------ src/viewport.rs | 14 +++++-- 8 files changed, 96 insertions(+), 84 deletions(-) diff --git a/examples/custom-glyphs.rs b/examples/custom-glyphs.rs index 6b1ecc5..98f6616 100644 --- a/examples/custom-glyphs.rs +++ b/examples/custom-glyphs.rs @@ -1,7 +1,7 @@ use glyphon::{ - Attrs, Buffer, Cache, Color, ContentType, CustomGlyph, RasterizationRequest, RasterizedCustomGlyph, - Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea, TextAtlas, TextBounds, - TextRenderer, Viewport, + Attrs, Buffer, Cache, Color, ContentType, CustomGlyph, Family, FontSystem, Metrics, + RasterizeCustomGlyphRequest, RasterizedCustomGlyph, Resolution, Shaping, SwashCache, TextArea, + TextAtlas, TextBounds, TextRenderer, Viewport, }; use std::sync::Arc; use wgpu::{ @@ -28,16 +28,13 @@ struct WindowState { queue: wgpu::Queue, surface: wgpu::Surface<'static>, surface_config: SurfaceConfiguration, - font_system: FontSystem, swash_cache: SwashCache, viewport: glyphon::Viewport, atlas: glyphon::TextAtlas, text_renderer: glyphon::TextRenderer, text_buffer: glyphon::Buffer, - - rasterize_svg: Box Option>, - + rasterize_svg: Box Option>, // Make sure that the winit window is last in the struct so that // it is dropped after the wgpu surface is dropped, otherwise the // program may crash when closed. This is probably a bug in wgpu. @@ -106,46 +103,47 @@ impl WindowState { let svg_0 = resvg::usvg::Tree::from_data(LION_SVG, &Default::default()).unwrap(); let svg_1 = resvg::usvg::Tree::from_data(EAGLE_SVG, &Default::default()).unwrap(); - let rasterize_svg = move |input: RasterizationRequest| -> Option { - // Select the svg data based on the custom glyph ID. - let (svg, content_type) = match input.id { - 0 => (&svg_0, ContentType::Mask), - 1 => (&svg_1, ContentType::Color), - _ => return None, + let rasterize_svg = + move |input: RasterizeCustomGlyphRequest| -> Option { + // Select the svg data based on the custom glyph ID. + let (svg, content_type) = match input.id { + 0 => (&svg_0, ContentType::Mask), + 1 => (&svg_1, ContentType::Color), + _ => return None, + }; + + // Calculate the scale based on the "glyph size". + let svg_size = svg.size(); + let scale_x = input.width as f32 / svg_size.width(); + let scale_y = input.height as f32 / svg_size.height(); + + let Some(mut pixmap) = + resvg::tiny_skia::Pixmap::new(input.width as u32, input.height as u32) + else { + return None; + }; + + let mut transform = resvg::usvg::Transform::from_scale(scale_x, scale_y); + + // Offset the glyph by the subpixel amount. + let offset_x = input.x_bin.as_float(); + let offset_y = input.y_bin.as_float(); + if offset_x != 0.0 || offset_y != 0.0 { + transform = transform.post_translate(offset_x, offset_y); + } + + resvg::render(svg, transform, &mut pixmap.as_mut()); + + let data: Vec = if let ContentType::Mask = content_type { + // Only use the alpha channel for symbolic icons. + pixmap.data().iter().skip(3).step_by(4).copied().collect() + } else { + pixmap.data().to_vec() + }; + + Some(RasterizedCustomGlyph { data, content_type }) }; - // Calculate the scale based on the "glyph size". - let svg_size = svg.size(); - let scale_x = input.width as f32 / svg_size.width(); - let scale_y = input.height as f32 / svg_size.height(); - - let Some(mut pixmap) = - resvg::tiny_skia::Pixmap::new(input.width as u32, input.height as u32) - else { - return None; - }; - - let mut transform = resvg::usvg::Transform::from_scale(scale_x, scale_y); - - // Offset the glyph by the subpixel amount. - let offset_x = input.x_bin.as_float(); - let offset_y = input.y_bin.as_float(); - if offset_x != 0.0 || offset_y != 0.0 { - transform = transform.post_translate(offset_x, offset_y); - } - - resvg::render(svg, transform, &mut pixmap.as_mut()); - - let data: Vec = if let ContentType::Mask = content_type { - // Only use the alpha channel for symbolic icons. - pixmap.data().iter().skip(3).step_by(4).copied().collect() - } else { - pixmap.data().to_vec() - }; - - Some(RasterizedCustomGlyph { data, content_type }) - }; - Self { device, queue, diff --git a/examples/text-sizes.rs b/examples/text-sizes.rs index 43b9f69..6296a75 100644 --- a/examples/text-sizes.rs +++ b/examples/text-sizes.rs @@ -244,6 +244,7 @@ impl winit::application::ApplicationHandler for Application { bottom: top.floor() as i32 + physical_size.height, }, default_color: FONT_COLOR, + custom_glyphs: &[], }; let total_lines = b diff --git a/src/cache.rs b/src/cache.rs index 46dbd25..a4a3449 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,5 +1,11 @@ use crate::{GlyphToRender, Params}; - +use std::{ + borrow::Cow, + mem, + num::NonZeroU64, + ops::Deref, + sync::{Arc, RwLock}, +}; use wgpu::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry, BindingResource, BindingType, BlendState, Buffer, BufferBindingType, ColorTargetState, @@ -10,12 +16,8 @@ use wgpu::{ TextureFormat, TextureSampleType, TextureView, TextureViewDimension, VertexFormat, VertexState, }; -use std::borrow::Cow; -use std::mem; -use std::num::NonZeroU64; -use std::ops::Deref; -use std::sync::{Arc, RwLock}; - +/// A cache to share common resources (e.g., pipelines, layouts, shaders) between multiple text +/// renderers. #[derive(Debug, Clone)] pub struct Cache(Arc); @@ -38,6 +40,7 @@ struct Inner { } impl Cache { + /// Creates a new `Cache` with the given `device`. pub fn new(device: &Device) -> Self { let sampler = device.create_sampler(&SamplerDescriptor { label: Some("glyphon sampler"), diff --git a/src/custom_glyph.rs b/src/custom_glyph.rs index a0df98c..08f0007 100644 --- a/src/custom_glyph.rs +++ b/src/custom_glyph.rs @@ -19,7 +19,7 @@ pub struct CustomGlyph { /// The color of this glyph (only relevant if the glyph is rendered with the /// type [`ContentType::Mask`]) /// - /// Set to `None` to use [`TextArea::default_color`]. + /// Set to `None` to use [`crate::TextArea::default_color`]. pub color: Option, /// If `true`, then this glyph will be snapped to the nearest whole physical /// pixel and the resulting `SubpixelBin`'s in `RasterizationRequest` will always @@ -31,7 +31,7 @@ pub struct CustomGlyph { /// A request to rasterize a custom glyph #[derive(Debug, Clone, Copy, PartialEq)] -pub struct RasterizationRequest { +pub struct RasterizeCustomGlyphRequest { /// The unique identifier of the glyph pub id: CustomGlyphId, /// The width of the glyph in physical pixels @@ -63,7 +63,11 @@ pub struct RasterizedCustomGlyph { } impl RasterizedCustomGlyph { - pub(crate) fn validate(&self, input: &RasterizationRequest, expected_type: Option) { + pub(crate) fn validate( + &self, + input: &RasterizeCustomGlyphRequest, + expected_type: Option, + ) { if let Some(expected_type) = expected_type { assert_eq!(self.content_type, expected_type, "Custom glyph rasterizer must always produce the same content type for a given input. Expected {:?}, got {:?}. Input: {:?}", expected_type, self.content_type, input); } diff --git a/src/lib.rs b/src/lib.rs index 9be0cdc..fafe9f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ mod viewport; pub use cache::Cache; pub use custom_glyph::{ - ContentType, CustomGlyph, CustomGlyphId, RasterizationRequest, RasterizedCustomGlyph, + ContentType, CustomGlyph, CustomGlyphId, RasterizeCustomGlyphRequest, RasterizedCustomGlyph, }; pub use error::{PrepareError, RenderError}; pub use text_atlas::{ColorMode, TextAtlas}; @@ -117,9 +117,8 @@ pub struct TextArea<'a> { /// The visible bounds of the text area. This is used to clip the text and doesn't have to /// match the `left` and `top` values. pub bounds: TextBounds, - // The default color of the text area. + /// The default color of the text area. pub default_color: Color, - - /// Additional custom glyphs to render + /// Additional custom glyphs to render. pub custom_glyphs: &'a [CustomGlyph], } diff --git a/src/text_atlas.rs b/src/text_atlas.rs index cf86464..41deb9a 100644 --- a/src/text_atlas.rs +++ b/src/text_atlas.rs @@ -1,6 +1,6 @@ use crate::{ - text_render::GlyphonCacheKey, Cache, ContentType, RasterizationRequest, RasterizedCustomGlyph, - FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, + text_render::GlyphonCacheKey, Cache, ContentType, RasterizeCustomGlyphRequest, FontSystem, + GlyphDetails, GpuCacheStatus, RasterizedCustomGlyph, SwashCache, }; use etagere::{size2, Allocation, BucketedAtlasAllocator}; use lru::LruCache; @@ -124,7 +124,9 @@ impl InnerAtlas { font_system: &mut FontSystem, cache: &mut SwashCache, scale_factor: f32, - mut rasterize_custom_glyph: impl FnMut(RasterizationRequest) -> Option, + mut rasterize_custom_glyph: impl FnMut( + RasterizeCustomGlyphRequest, + ) -> Option, ) -> bool { if self.size >= self.max_texture_dimension_2d { return false; @@ -169,7 +171,7 @@ impl InnerAtlas { (image.data, width, height) } GlyphonCacheKey::Custom(cache_key) => { - let input = RasterizationRequest { + let input = RasterizeCustomGlyphRequest { id: cache_key.glyph_id, width: cache_key.width, height: cache_key.height, @@ -264,7 +266,7 @@ impl Kind { } } -/// The color mode of an [`Atlas`]. +/// The color mode of a [`TextAtlas`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ColorMode { /// Accurate color management. @@ -352,7 +354,9 @@ impl TextAtlas { cache: &mut SwashCache, content_type: ContentType, scale_factor: f32, - rasterize_custom_glyph: impl FnMut(RasterizationRequest) -> Option, + rasterize_custom_glyph: impl FnMut( + RasterizeCustomGlyphRequest, + ) -> Option, ) -> bool { let did_grow = match content_type { ContentType::Mask => self.mask_atlas.grow( diff --git a/src/text_render.rs b/src/text_render.rs index 21592b8..3d57b86 100644 --- a/src/text_render.rs +++ b/src/text_render.rs @@ -1,7 +1,7 @@ use crate::{ - custom_glyph::CustomGlyphCacheKey, ColorMode, ContentType, RasterizationRequest, RasterizedCustomGlyph, - FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, PrepareError, RenderError, SwashCache, - SwashContent, TextArea, TextAtlas, Viewport, + custom_glyph::CustomGlyphCacheKey, ColorMode, ContentType, FontSystem, GlyphDetails, + GlyphToRender, GpuCacheStatus, PrepareError, RasterizeCustomGlyphRequest, + RasterizedCustomGlyph, RenderError, SwashCache, SwashContent, TextArea, TextAtlas, Viewport, }; use cosmic_text::{Color, SubpixelBin}; use std::{slice, sync::Arc}; @@ -104,7 +104,7 @@ impl TextRenderer { viewport: &Viewport, text_areas: impl IntoIterator>, cache: &mut SwashCache, - rasterize_custom_glyph: impl FnMut(RasterizationRequest) -> Option, + rasterize_custom_glyph: impl FnMut(RasterizeCustomGlyphRequest) -> Option, ) -> Result<(), PrepareError> { self.prepare_with_depth_and_custom( device, @@ -130,7 +130,9 @@ impl TextRenderer { text_areas: impl IntoIterator>, cache: &mut SwashCache, mut metadata_to_depth: impl FnMut(usize) -> f32, - mut rasterize_custom_glyph: impl FnMut(RasterizationRequest) -> Option, + mut rasterize_custom_glyph: impl FnMut( + RasterizeCustomGlyphRequest, + ) -> Option, ) -> Result<(), PrepareError> { self.glyph_vertices.clear(); @@ -193,7 +195,7 @@ impl TextRenderer { return None; } - let input = RasterizationRequest { + let input = RasterizeCustomGlyphRequest { id: glyph.id, width, height, @@ -202,9 +204,7 @@ impl TextRenderer { scale: text_area.scale, }; - let Some(output) = (rasterize_custom_glyph)(input) else { - return None; - }; + let output = (rasterize_custom_glyph)(input)?; output.validate(&input, None); @@ -268,11 +268,8 @@ impl TextRenderer { font_system, _rasterize_custom_glyph| -> Option { - let Some(image) = - cache.get_image_uncached(font_system, physical_glyph.cache_key) - else { - return None; - }; + let image = + cache.get_image_uncached(font_system, physical_glyph.cache_key)?; let content_type = match image.content { SwashContent::Color => ContentType::Color, @@ -429,7 +426,7 @@ fn prepare_glyph( mut rasterize_custom_glyph: R, ) -> Result, PrepareError> where - R: FnMut(RasterizationRequest) -> Option, + R: FnMut(RasterizeCustomGlyphRequest) -> Option, { if atlas.mask_atlas.glyph_cache.contains(&cache_key) { atlas.mask_atlas.promote(cache_key); diff --git a/src/viewport.rs b/src/viewport.rs index 14d3412..6a99c72 100644 --- a/src/viewport.rs +++ b/src/viewport.rs @@ -1,10 +1,13 @@ use crate::{Cache, Params, Resolution}; - +use std::{mem, slice}; use wgpu::{BindGroup, Buffer, BufferDescriptor, BufferUsages, Device, Queue}; -use std::mem; -use std::slice; - +/// Controls the visible area of all text for a given renderer. Any text outside of the visible +/// area will be clipped. +/// +/// Many projects will only ever need a single `Viewport`, but it is possible to create multiple +/// `Viewport`s if you want to render text to specific areas within a window (without having to) +/// bound each `TextArea`). #[derive(Debug)] pub struct Viewport { params: Params, @@ -13,6 +16,7 @@ pub struct Viewport { } impl Viewport { + /// Creates a new `Viewport` with the given `device` and `cache`. pub fn new(device: &Device, cache: &Cache) -> Self { let params = Params { screen_resolution: Resolution { @@ -38,6 +42,7 @@ impl Viewport { } } + /// Updates the `Viewport` with the given `resolution`. pub fn update(&mut self, queue: &Queue, resolution: Resolution) { if self.params.screen_resolution != resolution { self.params.screen_resolution = resolution; @@ -51,6 +56,7 @@ impl Viewport { } } + /// Returns the current resolution of the `Viewport`. pub fn resolution(&self) -> Resolution { self.params.screen_resolution }