Merge pull request #1 from hecrj/feature/update-glyph_brush

Update `glyph_brush` to `0.7`
This commit is contained in:
Héctor Ramón 2020-05-27 23:00:07 +02:00 committed by GitHub
commit 783175e56b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 188 additions and 200 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "glow_glyph"
version = "0.1.0"
version = "0.2.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "A fast text renderer for glow, powered by glyph_brush"
@ -12,7 +12,7 @@ readme = "README.md"
[dependencies]
glow = "0.4"
glyph_brush = "0.6"
glyph_brush = "0.7"
log = "0.4"
bytemuck = "1.2"

View File

@ -1,7 +1,8 @@
use glow::HasContext;
use glow_glyph::{GlyphBrushBuilder, Region, Scale, Section};
use glow_glyph::{ab_glyph, GlyphBrushBuilder, Region, Section, Text};
use std::error::Error;
fn main() -> Result<(), String> {
fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
// Open window and create a surface
@ -26,10 +27,11 @@ fn main() -> Result<(), String> {
});
// Prepare glyph_brush
let inconsolata: &[u8] = include_bytes!("Inconsolata-Regular.ttf");
let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(inconsolata)
.expect("Load fonts")
.build(&gl);
let inconsolata = ab_glyph::FontArc::try_from_slice(include_bytes!(
"Inconsolata-Regular.ttf"
))?;
let mut glyph_brush = GlyphBrushBuilder::using_font(inconsolata).build(&gl);
// Render loop
context.window().request_redraw();
@ -66,11 +68,12 @@ fn main() -> Result<(), String> {
unsafe { gl.clear(glow::COLOR_BUFFER_BIT) }
glyph_brush.queue(Section {
text: "Hello glow_glyph!",
screen_position: (30.0, 30.0),
color: [0.0, 0.0, 0.0, 1.0],
scale: Scale { x: 40.0, y: 40.0 },
bounds: (size.width as f32, size.height as f32),
text: vec![Text::default()
.with_text("Hello wgpu_glyph!")
.with_color([0.0, 0.0, 0.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
@ -79,11 +82,12 @@ fn main() -> Result<(), String> {
.expect("Draw queued");
glyph_brush.queue(Section {
text: "Hello glow_glyph!",
screen_position: (30.0, 90.0),
color: [1.0, 1.0, 1.0, 1.0],
scale: Scale { x: 40.0, y: 40.0 },
bounds: (size.width as f32, size.height as f32),
text: vec![Text::default()
.with_text("Hello wgpu_glyph!")
.with_color([1.0, 1.0, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});

View File

@ -1,7 +1,8 @@
use glow::HasContext;
use glow_glyph::{GlyphBrushBuilder, Scale, Section};
use glow_glyph::{ab_glyph, GlyphBrushBuilder, Section, Text};
use std::error::Error;
fn main() -> Result<(), String> {
fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
// Open window and create a surface
@ -26,10 +27,11 @@ fn main() -> Result<(), String> {
});
// Prepare glyph_brush
let inconsolata: &[u8] = include_bytes!("Inconsolata-Regular.ttf");
let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(inconsolata)
.expect("Load fonts")
.build(&gl);
let inconsolata = ab_glyph::FontArc::try_from_slice(include_bytes!(
"Inconsolata-Regular.ttf"
))?;
let mut glyph_brush = GlyphBrushBuilder::using_font(inconsolata).build(&gl);
// Render loop
context.window().request_redraw();
@ -72,20 +74,22 @@ fn main() -> Result<(), String> {
unsafe { gl.clear(glow::COLOR_BUFFER_BIT) }
glyph_brush.queue(Section {
text: "Hello glow_glyph!",
screen_position: (30.0, 30.0),
color: [0.0, 0.0, 0.0, 1.0],
scale: Scale { x: 40.0, y: 40.0 },
bounds: (size.width as f32, size.height as f32),
text: vec![Text::default()
.with_text("Hello wgpu_glyph!")
.with_color([0.0, 0.0, 0.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
glyph_brush.queue(Section {
text: "Hello glow_glyph!",
screen_position: (30.0, 90.0),
color: [1.0, 1.0, 1.0, 1.0],
scale: Scale { x: 40.0, y: 40.0 },
bounds: (size.width as f32, size.height as f32),
text: vec![Text::default()
.with_text("Hello wgpu_glyph!")
.with_color([1.0, 1.0, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});

View File

@ -1,67 +1,57 @@
use core::hash::BuildHasher;
use glyph_brush::ab_glyph::Font;
use glyph_brush::delegate_glyph_brush_builder_fns;
use glyph_brush::{rusttype, DefaultSectionHasher};
use rusttype::{Error, Font, SharedBytes};
use glyph_brush::DefaultSectionHasher;
use super::GlyphBrush;
/// Builder for a [`GlyphBrush`](struct.GlyphBrush.html).
pub struct GlyphBrushBuilder<'a, H = DefaultSectionHasher> {
inner: glyph_brush::GlyphBrushBuilder<'a, H>,
pub struct GlyphBrushBuilder<F, H = DefaultSectionHasher> {
inner: glyph_brush::GlyphBrushBuilder<F, H>,
}
impl<'a, H> From<glyph_brush::GlyphBrushBuilder<'a, H>>
for GlyphBrushBuilder<'a, H>
impl<F, H> From<glyph_brush::GlyphBrushBuilder<F, H>>
for GlyphBrushBuilder<F, H>
{
fn from(inner: glyph_brush::GlyphBrushBuilder<'a, H>) -> Self {
fn from(inner: glyph_brush::GlyphBrushBuilder<F, H>) -> Self {
GlyphBrushBuilder { inner }
}
}
impl<'a> GlyphBrushBuilder<'a> {
/// Specifies the default font data used to render glyphs.
/// Referenced with `FontId(0)`, which is default.
#[inline]
pub fn using_font_bytes<B: Into<SharedBytes<'a>>>(
font_0_data: B,
) -> Result<Self, Error> {
let font = Font::from_bytes(font_0_data)?;
Ok(Self::using_font(font))
}
#[inline]
pub fn using_fonts_bytes<B, V>(font_data: V) -> Result<Self, Error>
where
B: Into<SharedBytes<'a>>,
V: Into<Vec<B>>,
{
let fonts = font_data
.into()
.into_iter()
.map(Font::from_bytes)
.collect::<Result<Vec<Font>, Error>>()?;
Ok(Self::using_fonts(fonts))
}
impl GlyphBrushBuilder<()> {
/// Specifies the default font used to render glyphs.
/// Referenced with `FontId(0)`, which is default.
#[inline]
pub fn using_font(font_0: Font<'a>) -> Self {
Self::using_fonts(vec![font_0])
pub fn using_font<F: Font>(font: F) -> GlyphBrushBuilder<F> {
Self::using_fonts(vec![font])
}
pub fn using_fonts<V: Into<Vec<Font<'a>>>>(fonts: V) -> Self {
pub fn using_fonts<F: Font>(fonts: Vec<F>) -> GlyphBrushBuilder<F> {
GlyphBrushBuilder {
inner: glyph_brush::GlyphBrushBuilder::using_fonts(fonts),
}
}
}
impl<'a, H: BuildHasher> GlyphBrushBuilder<'a, H> {
impl<F: Font, H: BuildHasher> GlyphBrushBuilder<F, H> {
delegate_glyph_brush_builder_fns!(inner);
/// When multiple CPU cores are available spread rasterization work across
/// all cores.
///
/// Significantly reduces worst case latency in multicore environments.
///
/// # Platform-specific behaviour
///
/// This option has no effect on wasm32.
pub fn draw_cache_multithread(mut self, multithread: bool) -> Self {
self.inner.draw_cache_builder =
self.inner.draw_cache_builder.multithread(multithread);
self
}
/// Sets the section hasher. `GlyphBrush` cannot handle absolute section
/// hash collisions so use a good hash algorithm.
///
@ -72,14 +62,14 @@ impl<'a, H: BuildHasher> GlyphBrushBuilder<'a, H> {
pub fn section_hasher<T: BuildHasher>(
self,
section_hasher: T,
) -> GlyphBrushBuilder<'a, T> {
) -> GlyphBrushBuilder<F, T> {
GlyphBrushBuilder {
inner: self.inner.section_hasher(section_hasher),
}
}
/// Builds a `GlyphBrush` in the given `glow::Context`.
pub fn build(self, gl: &glow::Context) -> GlyphBrush<'a, H> {
GlyphBrush::<H>::new(gl, self.inner)
pub fn build(self, gl: &glow::Context) -> GlyphBrush<F, H> {
GlyphBrush::<F, H>::new(gl, self.inner)
}
}

View File

@ -12,30 +12,31 @@ pub use region::Region;
use pipeline::{Instance, Pipeline};
pub use builder::GlyphBrushBuilder;
pub use glyph_brush::ab_glyph;
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,
BuiltInLineBreaker, Extra, FontId, GlyphCruncher, GlyphPositioner,
HorizontalAlign, Layout, LineBreak, LineBreaker, Section, SectionGeometry,
SectionGlyph, SectionGlyphIter, SectionText, Text, VerticalAlign,
};
use ab_glyph::{Font, FontArc, Rect};
use core::hash::BuildHasher;
use std::borrow::Cow;
use glyph_brush::{BrushAction, BrushError, Color, DefaultSectionHasher};
use glyph_brush::{BrushAction, BrushError, DefaultSectionHasher};
use log::{log_enabled, warn};
/// Object allowing glyph drawing, containing cache state. Manages glyph positioning cacheing,
/// glyph draw caching & efficient GPU texture cache updating and re-sizing on demand.
///
/// Build using a [`GlyphBrushBuilder`](struct.GlyphBrushBuilder.html).
pub struct GlyphBrush<'font, H = DefaultSectionHasher> {
pub struct GlyphBrush<F = FontArc, H = DefaultSectionHasher> {
pipeline: Pipeline,
glyph_brush: glyph_brush::GlyphBrush<'font, Instance, H>,
glyph_brush: glyph_brush::GlyphBrush<Instance, Extra, F, H>,
}
impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
impl<F: Font, H: BuildHasher> GlyphBrush<F, H> {
/// Queues a section/layout to be drawn by the next call of
/// [`draw_queued`](struct.GlyphBrush.html#method.draw_queued). Can be
/// called multiple times to queue multiple sections for drawing.
@ -44,7 +45,7 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
#[inline]
pub fn queue<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, VariedSection<'a>>>,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush.queue(section)
}
@ -65,7 +66,7 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
custom_layout: &G,
) where
G: GlyphPositioner,
S: Into<Cow<'a, VariedSection<'a>>>,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush.queue_custom_layout(section, custom_layout)
}
@ -76,11 +77,11 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
#[inline]
pub fn queue_pre_positioned(
&mut self,
glyphs: Vec<(PositionedGlyph<'font>, Color, FontId)>,
bounds: Rect<f32>,
z: f32,
glyphs: Vec<SectionGlyph>,
extra: Vec<Extra>,
bounds: Rect,
) {
self.glyph_brush.queue_pre_positioned(glyphs, bounds, z)
self.glyph_brush.queue_pre_positioned(glyphs, extra, bounds)
}
/// Retains the section in the cache as if it had been used in the last
@ -94,7 +95,7 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
section: S,
custom_layout: &G,
) where
S: Into<Cow<'a, VariedSection<'a>>>,
S: Into<Cow<'a, Section<'a>>>,
G: GlyphPositioner,
{
self.glyph_brush
@ -109,110 +110,28 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
#[inline]
pub fn keep_cached<'a, S>(&mut self, section: S)
where
S: Into<Cow<'a, VariedSection<'a>>>,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush.keep_cached(section)
}
fn process_queued(&mut self, context: &glow::Context) {
let pipeline = &mut self.pipeline;
let mut brush_action;
loop {
brush_action = self.glyph_brush.process_queued(
|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];
pipeline.update_cache(context, offset, size, tex_data);
},
Instance::from,
);
match brush_action {
Ok(_) => break,
Err(BrushError::TextureTooSmall { suggested }) => {
// TODO: Obtain max texture dimensions
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),
);
}
pipeline
.increase_cache_size(context, new_width, new_height);
self.glyph_brush.resize_texture(new_width, new_height);
}
}
}
match brush_action.unwrap() {
BrushAction::Draw(verts) => {
self.pipeline.upload(context, &verts);
}
BrushAction::ReDraw => {}
};
}
/// Returns the available fonts.
///
/// The `FontId` corresponds to the index of the font data.
#[inline]
pub fn fonts(&self) -> &[Font<'_>] {
pub fn fonts(&self) -> &[F] {
self.glyph_brush.fonts()
}
/// Adds an additional font to the one(s) initially added on build.
///
/// Returns a new [`FontId`](struct.FontId.html) to reference this font.
pub fn add_font_bytes<'a: 'font, B: Into<SharedBytes<'a>>>(
&mut self,
font_data: B,
) -> FontId {
self.glyph_brush.add_font_bytes(font_data)
}
/// Adds an additional font to the one(s) initially added on build.
///
/// Returns a new [`FontId`](struct.FontId.html) to reference this font.
pub fn add_font<'a: 'font>(&mut self, font_data: Font<'a>) -> FontId {
self.glyph_brush.add_font(font_data)
pub fn add_font(&mut self, font: F) -> FontId {
self.glyph_brush.add_font(font)
}
}
impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
fn new(
gl: &glow::Context,
raw_builder: glyph_brush::GlyphBrushBuilder<'font, H>,
) -> Self {
let glyph_brush = raw_builder.build();
let (cache_width, cache_height) = glyph_brush.texture_dimensions();
GlyphBrush {
pipeline: Pipeline::new(gl, cache_width, cache_height),
glyph_brush,
}
}
impl<F: Font + Sync, H: BuildHasher> GlyphBrush<F, H> {
/// Draws all queued sections onto a render target.
/// See [`queue`](struct.GlyphBrush.html#method.queue).
///
@ -276,6 +195,81 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
Ok(())
}
fn process_queued(&mut self, context: &glow::Context) {
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(context, offset, size, tex_data);
},
Instance::from_vertex,
);
match brush_action {
Ok(_) => break,
Err(BrushError::TextureTooSmall { suggested }) => {
// TODO: Obtain max texture dimensions
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),
);
}
pipeline
.increase_cache_size(context, new_width, new_height);
self.glyph_brush.resize_texture(new_width, new_height);
}
}
}
match brush_action.unwrap() {
BrushAction::Draw(verts) => {
self.pipeline.upload(context, &verts);
}
BrushAction::ReDraw => {}
};
}
}
impl<F: Font, H: BuildHasher> GlyphBrush<F, H> {
fn new(
gl: &glow::Context,
raw_builder: glyph_brush::GlyphBrushBuilder<F, H>,
) -> Self {
let glyph_brush = raw_builder.build();
let (cache_width, cache_height) = glyph_brush.texture_dimensions();
GlyphBrush {
pipeline: Pipeline::new(gl, cache_width, cache_height),
glyph_brush,
}
}
}
/// Helper function to generate a generate a transform matrix.
@ -289,42 +283,42 @@ pub fn orthographic_projection(width: u32, height: u32) -> [f32; 16] {
]
}
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)
}
impl<F: Font, H: BuildHasher> GlyphCruncher<F> for GlyphBrush<F, H> {
#[inline]
fn glyphs_custom_layout<'a, 'b, S, L>(
&'b mut self,
section: S,
custom_layout: &L,
) -> PositionedGlyphIter<'b, 'font>
) -> SectionGlyphIter<'b>
where
L: GlyphPositioner + std::hash::Hash,
S: Into<Cow<'a, VariedSection<'a>>>,
S: Into<Cow<'a, Section<'a>>>,
{
self.glyph_brush
.glyphs_custom_layout(section, custom_layout)
}
#[inline]
fn fonts(&self) -> &[Font<'font>] {
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)
}
#[inline]
fn fonts(&self) -> &[F] {
self.glyph_brush.fonts()
}
}
impl<H> std::fmt::Debug for GlyphBrush<'_, H> {
impl<F, H> std::fmt::Debug for GlyphBrush<F, H> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GlyphBrush")

View File

@ -1,10 +1,10 @@
mod cache;
use crate::ab_glyph::{point, Rect};
use crate::Region;
use cache::Cache;
use glow::HasContext;
use glyph_brush::rusttype::{point, Rect};
pub struct Pipeline {
program: <glow::Context as HasContext>::Program,
@ -212,19 +212,15 @@ unsafe impl bytemuck::Pod for Instance {}
impl Instance {
const INITIAL_AMOUNT: usize = 50_000;
}
impl From<glyph_brush::GlyphVertex> for Instance {
#[inline]
fn from(vertex: glyph_brush::GlyphVertex) -> Instance {
let glyph_brush::GlyphVertex {
pub fn from_vertex(
glyph_brush::GlyphVertex {
mut tex_coords,
pixel_coords,
bounds,
color,
z,
} = vertex;
extra,
}: glyph_brush::GlyphVertex,
) -> Instance {
let gl_bounds = bounds;
let mut gl_rect = Rect {
@ -262,11 +258,11 @@ impl From<glyph_brush::GlyphVertex> for Instance {
}
Instance {
left_top: [gl_rect.min.x, gl_rect.max.y, z],
left_top: [gl_rect.min.x, gl_rect.max.y, extra.z],
right_bottom: [gl_rect.max.x, gl_rect.min.y],
tex_left_top: [tex_coords.min.x, tex_coords.max.y],
tex_right_bottom: [tex_coords.max.x, tex_coords.min.y],
color,
color: extra.color,
}
}
}