klyff 0.1.3

Text rendering library for games with MSDF support
Documentation
//! # Klyff text renderer
//!
//! A text renderer intended for games, with high customisability. It uses:
//! - [`wgpu`] for rendering.
//! - [`cosmic_text`] for shaping and layout.
//! - [`skrifa`] & [`swash`] for font reading.
//! - [`etagere`] for atlas packing.
//! - [`klyff_msdf`] for MSDF generation (done entirely on the GPU with wgpu).
//!
//! Because glyphs are rendered with MSDF, glyphs can be scaled up infinitely and still preserves
//! sharp corners. This is a pure rust reimplementation of this technique, originally invented by
//! Chlumsky: [msdfgen](https://github.com/Chlumsky/msdfgen).
//!
//! This crate features two layers: a convenient high level API, and a low level API for more
//! customisability (such as custom shaders).
//!
//! ### High level API
//!
//! The high level API mainly involves [`TextRenderer`] and [`StyledText`].
//!
//! First, create a [`TextRenderer`] with [`TextRenderer::new()`] for a basic renderer drawing
//! only the glyph's body, or [`TextRenderer::with_styling()`] to draw texts with additional
//! styling such as shadow, outline or glow. Each styling type is drawn with a separate draw call,
//! so disable what you don't need in the [`Features`] parameter.
//!
//! ```rust, no_run
//! use klyff::{
//!     EncoderContext, Rect, StyledTextBuilder, TextRenderer, TextStyle, TextureAtlas,
//!     TextureAtlasDescriptor,
//!     cosmic_text::{Attrs, Family, FontSystem, Metrics},
//! };
//!
//! // Dependencies
//! let device: wgpu::Device = todo!("Obtain a device from wgpu");
//! let queue: wgpu::Queue = todo!("Obtain a device from wgpu");
//! let surface_format: wgpu::TextureFormat = todo!("Define your surface format");
//! let view: wgpu::TextureView = todo!("Obtain your render target");
//! let mut font_system: FontSystem = todo!("Obtain a font system from cosmic_text");
//! let (width, height) = (800u32, 600u32);
//!
//! // One-time setup: a renderer for the swapchain format, and an atlas to cache glyphs.
//! let mut renderer = TextRenderer::new(&device, surface_format);
//! let mut atlas = TextureAtlas::new(&device, TextureAtlasDescriptor::default());
//!
//! // Build a `StyledText` for the given bounding box. Each call to `push_text` adds a run.
//! let attrs = Attrs::new().family(Family::Name("Open Sans"));
//! let mut text_builder = StyledTextBuilder::new(
//!     Rect::from_xywh(0.0, 0.0, width as f32, height as f32),
//!     &mut font_system,
//!     Metrics::new(48.0, 60.0));
//! text_builder.push_text("Hello, world!", &attrs, TextStyle::default())
//! let text = text_builder.finish(&mut font_system, &attrs);
//!
//! // Per frame: prepare uploads glyphs into the atlas and writes vertex buffers.
//! let mut cmd_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
//! renderer.prepare(
//!     EncoderContext {
//!         atlas: &mut atlas,
//!         device: &device,
//!         queue: &queue,
//!         cmd_encoder: &mut cmd_encoder,
//!         font_system: &mut font_system,
//!     },
//!     (width, height),
//!     std::slice::from_ref(&text),
//! );
//!
//! // Then issue the draw inside any render pass that targets `surface_format`.
//! let mut pass = cmd_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
//!     label: None,
//!     color_attachments: &[Some(wgpu::RenderPassColorAttachment {
//!         view: &view,
//!         resolve_target: None,
//!         depth_slice: None,
//!         ops: wgpu::Operations {
//!             load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
//!             store: wgpu::StoreOp::Store,
//!         },
//!     })],
//!     depth_stencil_attachment: None,
//!     occlusion_query_set: None,
//!     timestamp_writes: None,
//! });
//! renderer.render(&mut pass, &atlas);
//! ```
//!
//! See the full setup at the `hello_world` and `all_features` examples.
//!
//! #### Features
//!
//! 1. Styling
//!
//! Styling is done through [`StyledText`]. Each text can hold multiple chunks of text, each with its
//! own styling. Build styled text with [`StyledTextBuilder`].
//!
//! When styling is enabled, each effect is drawn as a separate draw call in back-to-front order
//! (shadow - outer glow - outer stroke - fill - inner glow - inner stroke) to avoid overlap between
//! different glyphs and proper alpha blending.
//!
//! Beyond this, [`TextRenderer`] allows you to further modify styling per glyph, and position of
//! each emitted vertex with [`TextRenderer::prepare_with_glyph_transform`], which is useful for
//! text animations.
//!
//! 2. Rasterized glyphs
//!
//! Glyphs that are incompatible with MSDF rendering (such as colored glyphs, bitmap glyphs) are
//! rasterized as is into the atlas, and rendered with a separate pipeline. Currently, styling is
//! not supported for these glyphs (though it is feasible to implement in the future).
//!
//! 3. Custom glyphs
//!
//! [`StyledText`] supports injecting custom glyphs into text. They are represented with an empty
//! glyph in the Private Use Area (PUA) defined by a custom font. You can set it up by calling
//! [`setup_custom_glyph_font`].
//!
//! Klyff does not handle rendering custom glyphs, instead it stores the processed glyphs and
//! their position in a [`Vec`] which can be read back and rendered later by application code. Both
//! [`TextRenderer`] and [`MeshEncoder`] supports this operation.
//!
//! 4. Custom pipeline
//!
//! TextRenderer supports passing in custom pipeline for MSDF and rasterized glyphs through
//! [`TextRenderer::with_custom_pipeline`]. This is intended for use cases such as specifying
//! depth-stencil state, custom blend ops, or otherwise modifying the pipeline while still using
//! the built-in shader and pipeline layout.
//!
//! Modifying the pipeline layout or the shader module may create an invalid pipeline and panic at
//! runtime, so do be careful. If you need to use custom shaders then the low level API is
//! recommended for more flexibility.
//!
//! ### Low level API
//!
//! For renderers that need full shader control, klyff splits the work into composable encoders.
//!
//! - [`MeshEncoder`] decodes [`Text`] into the vertex / index buffers, which is then shared across
//!   all draw calls. It records a list of [`DecodedGlyph`] entry, allowing you to attach per-glyph
//!   attributes without re-running layout.
//!
//! - [`MaterialEncoder`] walks the decoded glyphs and produces a parallel vertex buffer of
//!   [`GlyphMaterial`] data - drawing instruction consumed by the built-in MSDF shader. One
//!   [`MaterialEncoder`] corresponds to one draw call / one layer (fill, stroke, glow, shadow, …);
//!   the layered effects of [`TextRenderer`] are themselves implemented as multiple
//!   [`MaterialEncoder`]s sharing one [`MeshEncoder`].
//!
//! If the built-in [`GlyphMaterial`] does not cover your effect, the same pattern still applies:
//! iterate the [`MeshEncoder`]'s [`DecodedGlyph`]s, emit your own parallel vertex buffer and bind
//! it alongside the mesh buffer in your own pipeline.
//!
//! You can see an example of these patterns in the `custom_pipeline` example crate.
//!
//! ### Glyph atlas
//!
//! The generated distance fields / rasterized glyphs are cached on a texture atlas. By default
//! this atlas is generated at runtime as new glyphs are drawn. With the `serde` feature you can
//! also generate the atlas offline and save it with [`TextureAtlas::bake()`], then reload it on
//! startup with [`TextureAtlas::from_baked()`].
//!
//! If you do not want new glyphs to be generated into the atlas during runtime, call
//! [`TextureAtlas::freeze()`]; [`TextureAtlas::unfreeze()`] re-enables generation.
//!
//! See the `prebaked_atlas` example for demonstration of this feature.
//!
//! ### Versioning
//!
//! This crate follows semver. In addition to standard rust's API backward compatibility guarantee,
//! any change to vertex buffer layout or bind group layout will be considered a breaking change.

pub mod atlas;
mod data;
mod features;
mod pipelines;
mod renderer;

pub use atlas::{
    AtlasRegion, AtlasSize, CachedGlyph, FontData, GlyphKey, TextureAtlas, TextureAtlasDescriptor,
    WriteGlyphError,
};
pub use cosmic_text;
pub use data::{
    CUSTOM_GLYPH_CHAR, Color, CustomGlyph, CustomGlyphFont, DecodedGlyph, Glow, GlyphColoring,
    GlyphStyleMap, Outline, Rect, Shadow, StyledText, StyledTextBuilder, Text, TextStyle,
    setup_custom_glyph_font,
};
pub use features::Features;
pub use fontdb;
pub use pipelines::{
    EncoderContext, GlyphMaterial, MaterialEncoder, MeshEncoder, MsdfTextPipeline,
    RasterizedTextPipeline, ScreenSizeUniform,
};
pub use renderer::TextRenderer;

#[cfg(feature = "serde")]
pub use atlas::{AtlasState, BakeError, BakedAtlas};