Cleanup before release
This commit is contained in:
parent
2daf883cad
commit
692e23ca33
8 changed files with 96 additions and 84 deletions
|
@ -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<dyn Fn(RasterizationRequest) -> Option<RasterizedCustomGlyph>>,
|
||||
|
||||
rasterize_svg: Box<dyn Fn(RasterizeCustomGlyphRequest) -> Option<RasterizedCustomGlyph>>,
|
||||
// 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<RasterizedCustomGlyph> {
|
||||
// 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<RasterizedCustomGlyph> {
|
||||
// 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<u8> = 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<u8> = 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,
|
||||
|
|
|
@ -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
|
||||
|
|
17
src/cache.rs
17
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<Inner>);
|
||||
|
||||
|
@ -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"),
|
||||
|
|
|
@ -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<Color>,
|
||||
/// 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<ContentType>) {
|
||||
pub(crate) fn validate(
|
||||
&self,
|
||||
input: &RasterizeCustomGlyphRequest,
|
||||
expected_type: Option<ContentType>,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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],
|
||||
}
|
||||
|
|
|
@ -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<RasterizedCustomGlyph>,
|
||||
mut rasterize_custom_glyph: impl FnMut(
|
||||
RasterizeCustomGlyphRequest,
|
||||
) -> Option<RasterizedCustomGlyph>,
|
||||
) -> 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<RasterizedCustomGlyph>,
|
||||
rasterize_custom_glyph: impl FnMut(
|
||||
RasterizeCustomGlyphRequest,
|
||||
) -> Option<RasterizedCustomGlyph>,
|
||||
) -> bool {
|
||||
let did_grow = match content_type {
|
||||
ContentType::Mask => self.mask_atlas.grow(
|
||||
|
|
|
@ -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<Item = TextArea<'a>>,
|
||||
cache: &mut SwashCache,
|
||||
rasterize_custom_glyph: impl FnMut(RasterizationRequest) -> Option<RasterizedCustomGlyph>,
|
||||
rasterize_custom_glyph: impl FnMut(RasterizeCustomGlyphRequest) -> Option<RasterizedCustomGlyph>,
|
||||
) -> Result<(), PrepareError> {
|
||||
self.prepare_with_depth_and_custom(
|
||||
device,
|
||||
|
@ -130,7 +130,9 @@ impl TextRenderer {
|
|||
text_areas: impl IntoIterator<Item = TextArea<'a>>,
|
||||
cache: &mut SwashCache,
|
||||
mut metadata_to_depth: impl FnMut(usize) -> f32,
|
||||
mut rasterize_custom_glyph: impl FnMut(RasterizationRequest) -> Option<RasterizedCustomGlyph>,
|
||||
mut rasterize_custom_glyph: impl FnMut(
|
||||
RasterizeCustomGlyphRequest,
|
||||
) -> Option<RasterizedCustomGlyph>,
|
||||
) -> 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<GetGlyphImageResult> {
|
||||
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<R>(
|
|||
mut rasterize_custom_glyph: R,
|
||||
) -> Result<Option<GlyphToRender>, PrepareError>
|
||||
where
|
||||
R: FnMut(RasterizationRequest) -> Option<RasterizedCustomGlyph>,
|
||||
R: FnMut(RasterizeCustomGlyphRequest) -> Option<RasterizedCustomGlyph>,
|
||||
{
|
||||
if atlas.mask_atlas.glyph_cache.contains(&cache_key) {
|
||||
atlas.mask_atlas.promote(cache_key);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue