pub mod glyph;
#[allow(dead_code)]
mod builder;
mod pipeline;
pub struct Region {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
use pipeline::{Instance, Pipeline};
pub use builder::GlyphBrushBuilder;
pub use glyph::ab_glyph;
pub use glyph::{
BuiltInLineBreaker, Extra, FontId, GlyphCruncher, GlyphPositioner, HorizontalAlign,
Layout, LineBreak, LineBreaker, OwnedSection, OwnedText, Section, SectionGeometry,
SectionGlyph, SectionGlyphIter, SectionText, Text, VerticalAlign,
};
use crate::components::core::orthographic_projection;
use ab_glyph::{Font, Rect};
use core::hash::BuildHasher;
use std::borrow::Cow;
use glyph::{BrushAction, BrushError, DefaultSectionHasher};
pub struct GlyphBrush<Depth, F = ab_glyph::FontArc, H = DefaultSectionHasher> {
pipeline: Pipeline<Depth>,
glyph_brush: glyph::GlyphBrush<Instance, Extra, F, H>,
}
impl<Depth, F: Font, H: BuildHasher> GlyphBrush<Depth, F, H> {
#[inline]
pub fn queue<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush.queue(section)
}
#[inline]
pub fn keep_cached<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush.keep_cached(section)
}
#[inline]
pub fn queue_custom_layout<'a, S, G>(&mut self, section: S, custom_layout: &G)
where
G: GlyphPositioner,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush.queue_custom_layout(section, custom_layout)
}
pub fn fonts(&self) -> &[F] {
self.glyph_brush.fonts()
}
pub fn add_font(&mut self, font: F) -> FontId {
self.glyph_brush.add_font(font)
}
}
impl<D, F, H> GlyphBrush<D, F, H>
where
F: Font + Sync,
H: BuildHasher,
{
fn process_queued(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
let pipeline = &mut self.pipeline;
let mut brush_action;
loop {
brush_action = self.glyph_brush.process_queued(
|rect, tex_data| {
let offset = [rect.min[0] as u16, rect.min[1] as u16];
let size = [rect.width() as u16, rect.height() as u16];
pipeline.update_cache(queue, offset, size, tex_data);
},
Instance::from_vertex,
);
match brush_action {
Ok(_) => break,
Err(BrushError::TextureTooSmall { suggested }) => {
let max_image_dimension = 64;
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
};
pipeline.increase_cache_size(device, new_width, new_height);
self.glyph_brush.resize_texture(new_width, new_height);
}
}
}
match brush_action.unwrap() {
BrushAction::Draw(mut verts) => {
self.pipeline.upload(device, queue, &mut verts);
}
BrushAction::ReDraw => {}
};
}
}
impl<F: Font + Sync, H: BuildHasher> GlyphBrush<(), F, H> {
fn new(
device: &wgpu::Device,
filter_mode: wgpu::FilterMode,
multisample: wgpu::MultisampleState,
render_format: wgpu::TextureFormat,
raw_builder: glyph::GlyphBrushBuilder<F, H>,
) -> Self {
let glyph_brush = raw_builder.build();
let (cache_width, cache_height) = glyph_brush.texture_dimensions();
GlyphBrush {
pipeline: Pipeline::<()>::new(
device,
filter_mode,
multisample,
render_format,
cache_width,
cache_height,
),
glyph_brush,
}
}
#[inline]
pub fn render<'pass>(
&'pass mut self,
context: &mut crate::context::Context,
rpass: &mut wgpu::RenderPass<'pass>,
) {
let device = &context.device;
let queue = &context.queue;
self.draw_queued_with_transform(
device,
queue,
rpass,
orthographic_projection(context.size.width, context.size.height),
);
}
#[inline]
pub fn draw_queued_with_transform<'pass>(
&'pass mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
rpass: &mut wgpu::RenderPass<'pass>,
transform: [f32; 16],
) {
self.process_queued(device, queue);
self.pipeline.draw(queue, rpass, transform, None);
}
#[inline]
pub fn _draw_queued_with_transform_and_scissoring<'pass>(
&'pass mut self,
device: &wgpu::Device,
queue: &mut wgpu::Queue,
rpass: &mut wgpu::RenderPass<'pass>,
transform: [f32; 16],
region: Region,
) -> Result<(), String> {
self.process_queued(device, queue);
self.pipeline.draw(queue, rpass, transform, Some(region));
Ok(())
}
}
impl<F: Font + Sync, H: BuildHasher> GlyphBrush<wgpu::DepthStencilState, F, H> {
fn new(
device: &wgpu::Device,
filter_mode: wgpu::FilterMode,
multisample: wgpu::MultisampleState,
render_format: wgpu::TextureFormat,
raw_builder: glyph::GlyphBrushBuilder<F, H>,
) -> Self {
let glyph_brush = raw_builder.build();
let (cache_width, cache_height) = glyph_brush.texture_dimensions();
GlyphBrush {
pipeline: Pipeline::<wgpu::DepthStencilState>::new(
device,
filter_mode,
multisample,
render_format,
cache_width,
cache_height,
),
glyph_brush,
}
}
#[inline]
pub fn _draw_queued<'pass>(
&'pass mut self,
device: &wgpu::Device,
queue: &mut wgpu::Queue,
rpass: &mut wgpu::RenderPass<'pass>,
w_h: (f32, f32),
) -> Result<(), String> {
self.draw_queued_with_transform(
device,
queue,
rpass,
orthographic_projection(w_h.0, w_h.1),
)
}
#[inline]
#[allow(dead_code)]
pub fn draw_queued_with_transform<'pass>(
&'pass mut self,
device: &wgpu::Device,
queue: &mut wgpu::Queue,
rpass: &mut wgpu::RenderPass<'pass>,
transform: [f32; 16],
) -> Result<(), String> {
self.process_queued(device, queue);
self.pipeline.draw((queue, rpass), transform, None);
Ok(())
}
#[inline]
pub fn _draw_queued_with_transform_and_scissoring<'pass>(
&'pass mut self,
config: (
&wgpu::Device,
&mut wgpu::Queue,
&mut wgpu::RenderPass<'pass>,
),
transform: [f32; 16],
region: Region,
) -> Result<(), String> {
let (device, queue, encoder) = config;
self.process_queued(device, queue);
self.pipeline
.draw((queue, encoder), transform, Some(region));
Ok(())
}
}
impl<D, F: Font, H: BuildHasher> GlyphCruncher<F> for GlyphBrush<D, F, H> {
#[inline]
fn glyphs_custom_layout<'a, 'b, S, L>(
&'b mut self,
section: S,
custom_layout: &L,
) -> SectionGlyphIter<'b>
where
L: GlyphPositioner + std::hash::Hash,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush
.glyphs_custom_layout(section, custom_layout)
}
#[inline]
fn fonts(&self) -> &[F] {
self.glyph_brush.fonts()
}
#[inline]
fn glyph_bounds_custom_layout<'a, S, L>(
&mut self,
section: S,
custom_layout: &L,
) -> Option<Rect>
where
L: GlyphPositioner + std::hash::Hash,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush
.glyph_bounds_custom_layout(section, custom_layout)
}
}