From 2e3994884330ed0d0d4f5f91dccc115d6f993f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 27 May 2020 22:43:21 +0200 Subject: [PATCH] Update `glyph_brush` to `0.7` :tada: --- Cargo.toml | 4 +- examples/clipping.rs | 28 +++--- examples/hello.rs | 28 +++--- src/builder.rs | 70 ++++++-------- src/lib.rs | 226 +++++++++++++++++++++---------------------- src/pipeline.rs | 20 ++-- 6 files changed, 182 insertions(+), 194 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4987656..9c15f7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glow_glyph" -version = "0.1.0" +version = "0.2.0" authors = ["Héctor Ramón Jiménez "] 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" diff --git a/examples/clipping.rs b/examples/clipping.rs index f3e51e6..0fbe042 100644 --- a/examples/clipping.rs +++ b/examples/clipping.rs @@ -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> { 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() }); diff --git a/examples/hello.rs b/examples/hello.rs index 68f8840..3be4979 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -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> { 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() }); diff --git a/src/builder.rs b/src/builder.rs index 76d4c25..5e0505d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -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 { + inner: glyph_brush::GlyphBrushBuilder, } -impl<'a, H> From> - for GlyphBrushBuilder<'a, H> +impl From> + for GlyphBrushBuilder { - fn from(inner: glyph_brush::GlyphBrushBuilder<'a, H>) -> Self { + fn from(inner: glyph_brush::GlyphBrushBuilder) -> 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>>( - font_0_data: B, - ) -> Result { - let font = Font::from_bytes(font_0_data)?; - - Ok(Self::using_font(font)) - } - - #[inline] - pub fn using_fonts_bytes(font_data: V) -> Result - where - B: Into>, - V: Into>, - { - let fonts = font_data - .into() - .into_iter() - .map(Font::from_bytes) - .collect::, 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(font: F) -> GlyphBrushBuilder { + Self::using_fonts(vec![font]) } - pub fn using_fonts>>>(fonts: V) -> Self { + pub fn using_fonts(fonts: Vec) -> GlyphBrushBuilder { GlyphBrushBuilder { inner: glyph_brush::GlyphBrushBuilder::using_fonts(fonts), } } } -impl<'a, H: BuildHasher> GlyphBrushBuilder<'a, H> { +impl GlyphBrushBuilder { 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( self, section_hasher: T, - ) -> GlyphBrushBuilder<'a, T> { + ) -> GlyphBrushBuilder { 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::::new(gl, self.inner) + pub fn build(self, gl: &glow::Context) -> GlyphBrush { + GlyphBrush::::new(gl, self.inner) } } diff --git a/src/lib.rs b/src/lib.rs index 92302ff..7b5080a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { pipeline: Pipeline, - glyph_brush: glyph_brush::GlyphBrush<'font, Instance, H>, + glyph_brush: glyph_brush::GlyphBrush, } -impl<'font, H: BuildHasher> GlyphBrush<'font, H> { +impl GlyphBrush { /// 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>>, + S: Into>>, { self.glyph_brush.queue(section) } @@ -65,7 +66,7 @@ impl<'font, H: BuildHasher> GlyphBrush<'font, H> { custom_layout: &G, ) where G: GlyphPositioner, - S: Into>>, + S: Into>>, { 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, - z: f32, + glyphs: Vec, + extra: Vec, + 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>>, + S: Into>>, 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>>, + S: Into>>, { 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>>( - &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 GlyphBrush { /// 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 GlyphBrush { + fn new( + gl: &glow::Context, + raw_builder: glyph_brush::GlyphBrushBuilder, + ) -> 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> { +impl GlyphCruncher for GlyphBrush { #[inline] - fn pixel_bounds_custom_layout<'a, S, L>( - &mut self, + fn glyphs_custom_layout<'a, 'b, S, L>( + &'b mut self, section: S, custom_layout: &L, - ) -> Option> + ) -> SectionGlyphIter<'b> where L: GlyphPositioner + std::hash::Hash, - S: Into>>, + S: Into>>, { self.glyph_brush - .pixel_bounds_custom_layout(section, custom_layout) + .glyphs_custom_layout(section, custom_layout) } #[inline] - fn glyphs_custom_layout<'a, 'b, S, L>( - &'b mut self, + fn glyph_bounds_custom_layout<'a, S, L>( + &mut self, section: S, custom_layout: &L, - ) -> PositionedGlyphIter<'b, 'font> + ) -> Option where L: GlyphPositioner + std::hash::Hash, - S: Into>>, + S: Into>>, { self.glyph_brush - .glyphs_custom_layout(section, custom_layout) + .glyph_bounds_custom_layout(section, custom_layout) } #[inline] - fn fonts(&self) -> &[Font<'font>] { + fn fonts(&self) -> &[F] { self.glyph_brush.fonts() } } -impl std::fmt::Debug for GlyphBrush<'_, H> { +impl std::fmt::Debug for GlyphBrush { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "GlyphBrush") diff --git a/src/pipeline.rs b/src/pipeline.rs index 2c73261..8e8e7b4 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -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: ::Program, @@ -212,19 +212,15 @@ unsafe impl bytemuck::Pod for Instance {} impl Instance { const INITIAL_AMOUNT: usize = 50_000; -} -impl From 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 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, } } }