mod builder;
mod pipeline;
use pipeline::{Instance, Pipeline};
pub use builder::GlyphBrushBuilder;
pub use glyph_brush::{
rusttype::{self, Font, Point, PositionedGlyph, Rect, Scale, SharedBytes},
BuiltInLineBreaker, FontId, FontMap, GlyphCruncher, GlyphPositioner,
HorizontalAlign, Layout, LineBreak, LineBreaker, OwnedSectionText,
OwnedVariedSection, PositionedGlyphIter, Section, SectionGeometry,
SectionText, VariedSection, VerticalAlign,
};
use core::hash::BuildHasher;
use std::borrow::Cow;
use glyph_brush::{BrushAction, BrushError, Color, DefaultSectionHasher};
use log::{log_enabled, warn};
pub struct GlyphBrush<'font, H = DefaultSectionHasher> {
pipeline: Pipeline,
glyph_brush: glyph_brush::GlyphBrush<'font, Instance, H>,
}
impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
fn new(
device: &mut wgpu::Device,
filter_method: wgpu::FilterMode,
raw_builder: glyph_brush::GlyphBrushBuilder<'font, H>,
) -> Self {
let (cache_width, cache_height) = raw_builder.initial_cache_size;
let pipeline =
Pipeline::new(device, filter_method, cache_width, cache_height);
GlyphBrush {
pipeline: pipeline,
glyph_brush: raw_builder.build(),
}
}
#[inline]
pub fn queue<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush.queue(section)
}
#[inline]
pub fn queue_custom_layout<'a, S, G>(
&mut self,
section: S,
custom_layout: &G,
) where
G: GlyphPositioner,
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush.queue_custom_layout(section, custom_layout)
}
#[inline]
pub fn queue_pre_positioned(
&mut self,
glyphs: Vec<(PositionedGlyph<'font>, Color, FontId)>,
bounds: Rect<f32>,
z: f32,
) {
self.glyph_brush.queue_pre_positioned(glyphs, bounds, z)
}
#[inline]
pub fn keep_cached_custom_layout<'a, S, G>(
&mut self,
section: S,
custom_layout: &G,
) where
S: Into<Cow<'a, VariedSection<'a>>>,
G: GlyphPositioner,
{
self.glyph_brush
.keep_cached_custom_layout(section, custom_layout)
}
#[inline]
pub fn keep_cached<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush.keep_cached(section)
}
#[inline]
pub fn draw_queued(
&mut self,
device: &mut wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
target_width: u32,
target_height: u32,
) -> Result<(), String> {
self.draw_queued_with_transform(
Pipeline::IDENTITY_MATRIX,
device,
encoder,
target,
target_width,
target_height,
)
}
pub fn draw_queued_with_transform(
&mut self,
transform: [f32; 16],
device: &mut wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
target_width: u32,
target_height: u32,
) -> Result<(), String> {
let cache = self.pipeline.cache();
let mut brush_action;
loop {
brush_action = self.glyph_brush.process_queued(
(target_width, target_height),
|rect, tex_data| {
let offset = [rect.min.x as u16, rect.min.y as u16];
let size = [rect.width() as u16, rect.height() as u16];
cache.update(device, encoder, offset, size, tex_data);
},
Instance::from,
);
match brush_action {
Ok(_) => break,
Err(BrushError::TextureTooSmall { suggested }) => {
let max_image_dimension = 2048;
let (new_width, new_height) = if (suggested.0
> max_image_dimension
|| suggested.1 > max_image_dimension)
&& (self.glyph_brush.texture_dimensions().0
< max_image_dimension
|| self.glyph_brush.texture_dimensions().1
< max_image_dimension)
{
(max_image_dimension, max_image_dimension)
} else {
suggested
};
if log_enabled!(log::Level::Warn) {
warn!(
"Increasing glyph texture size {old:?} -> {new:?}. \
Consider building with `.initial_cache_size({new:?})` to avoid \
resizing",
old = self.glyph_brush.texture_dimensions(),
new = (new_width, new_height),
);
}
self.pipeline
.increase_cache_size(device, new_width, new_height);
self.glyph_brush.resize_texture(new_width, new_height);
}
}
}
match brush_action.unwrap() {
BrushAction::Draw(verts) => {
self.pipeline.upload(device, encoder, transform, &verts);
self.pipeline.draw(encoder, target);
}
BrushAction::ReDraw => {
self.pipeline.draw(encoder, target);
}
};
Ok(())
}
#[inline]
pub fn fonts(&self) -> &[Font<'_>] {
self.glyph_brush.fonts()
}
}
impl<'font, H: BuildHasher> GlyphCruncher<'font> for GlyphBrush<'font, H> {
#[inline]
fn pixel_bounds_custom_layout<'a, S, L>(
&mut self,
section: S,
custom_layout: &L,
) -> Option<Rect<i32>>
where
L: GlyphPositioner + std::hash::Hash,
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush
.pixel_bounds_custom_layout(section, custom_layout)
}
#[inline]
fn glyphs_custom_layout<'a, 'b, S, L>(
&'b mut self,
section: S,
custom_layout: &L,
) -> PositionedGlyphIter<'b, 'font>
where
L: GlyphPositioner + std::hash::Hash,
S: Into<Cow<'a, VariedSection<'a>>>,
{
self.glyph_brush
.glyphs_custom_layout(section, custom_layout)
}
#[inline]
fn fonts(&self) -> &[Font<'font>] {
self.glyph_brush.fonts()
}
}
impl<H> std::fmt::Debug for GlyphBrush<'_, H> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GlyphBrush")
}
}