Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup / release 0.6.0 #115

Merged
merged 2 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "glyphon"
description = "Fast, simple 2D text rendering for wgpu"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
homepage = "https://github.com/grovesNL/glyphon.git"
repository = "https://github.com/grovesNL/glyphon"
Expand Down
82 changes: 40 additions & 42 deletions examples/custom-glyphs.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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.
Expand Down Expand Up @@ -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,
};

// 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);
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);
}

// 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());

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()
};

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 })
};

Some(RasterizedCustomGlyph { data, content_type })
};

Self {
device,
queue,
Expand Down
1 change: 1 addition & 0 deletions examples/text-sizes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 10 additions & 7 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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>);

Expand All @@ -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"),
Expand Down
10 changes: 7 additions & 3 deletions src/custom_glyph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
7 changes: 3 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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],
}
16 changes: 10 additions & 6 deletions src/text_atlas.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
27 changes: 12 additions & 15 deletions src/text_render.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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,
Expand All @@ -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();

Expand Down Expand Up @@ -193,7 +195,7 @@ impl TextRenderer {
return None;
}

let input = RasterizationRequest {
let input = RasterizeCustomGlyphRequest {
id: glyph.id,
width,
height,
Expand All @@ -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);

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 10 additions & 4 deletions src/viewport.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -51,6 +56,7 @@ impl Viewport {
}
}

/// Returns the current resolution of the `Viewport`.
pub fn resolution(&self) -> Resolution {
self.params.screen_resolution
}
Expand Down