glium_brush/
lib.rs

1#[macro_use]
2extern crate glium;
3pub extern crate glyph_brush;
4
5use std::borrow::Cow;
6use std::ops::Deref;
7use std::hash::BuildHasher;
8
9use glium::backend::{Facade, Context};
10use glium::{Surface, Program, Frame};
11use glium::index::PrimitiveType;
12use glium::texture::{RawImage2d, ClientFormat};
13use glium::texture::texture2d::Texture2d;
14
15use glyph_brush::rusttype::{Rect, SharedBytes};
16use glyph_brush::{
17	rusttype::{point, Font}, BrushAction, BrushError, DefaultSectionHasher, FontId, VariedSection, GlyphPositioner,
18};
19
20const IDENTITY_MATRIX4: [[f32; 4]; 4] = [
21	[1.0, 0.0, 0.0, 0.0],
22	[0.0, 1.0, 0.0, 0.0],
23	[0.0, 0.0, 1.0, 0.0],
24	[0.0, 0.0, 0.0, 1.0],
25];
26
27#[derive(Copy, Clone, Debug)]
28struct GlyphVertex {
29	/// screen position
30	left_top: [f32; 3],
31	right_bottom: [f32; 2],
32	/// texture position
33	tex_left_top: [f32; 2],
34	tex_right_bottom: [f32; 2],
35	/// text color
36	color: [f32; 4],
37}
38
39implement_vertex!(GlyphVertex, left_top, right_bottom, tex_left_top,
40	tex_right_bottom, color);
41
42#[derive(Copy, Clone, Debug)]
43struct InstanceVertex {
44	v: f32,
45}
46
47implement_vertex!(InstanceVertex, v);
48
49fn rect_to_rect(rect: Rect<u32>) -> glium::Rect {
50	glium::Rect {
51		left: rect.min.x,
52		bottom: rect.min.y,
53		width: rect.width(),
54		height: rect.height(),
55	}
56}
57
58fn update_texture(tex: &Texture2d, rect: Rect<u32>, tex_data: &[u8]) {
59	let image = RawImage2d {
60		data: std::borrow::Cow::Borrowed(tex_data),
61		format: ClientFormat::U8,
62		height: rect.height(),
63		width: rect.width(),
64	};
65	tex.write(rect_to_rect(rect), image);
66}
67
68#[inline]
69fn to_vertex(
70	glyph_brush::GlyphVertex {
71		mut tex_coords,
72		pixel_coords,
73		bounds,
74		screen_dimensions: (screen_w, screen_h),
75		color,
76		z,
77	}: glyph_brush::GlyphVertex,
78) -> GlyphVertex {
79	let gl_bounds = Rect {
80		min: point(
81			2.0 * (bounds.min.x / screen_w - 0.5),
82			2.0 * (0.5 - bounds.min.y / screen_h),
83		),
84		max: point(
85			2.0 * (bounds.max.x / screen_w - 0.5),
86			2.0 * (0.5 - bounds.max.y / screen_h),
87		),
88	};
89
90	let mut gl_rect = Rect {
91		min: point(
92			2.0 * (pixel_coords.min.x as f32 / screen_w - 0.5),
93			2.0 * (0.5 - pixel_coords.min.y as f32 / screen_h),
94		),
95		max: point(
96			2.0 * (pixel_coords.max.x as f32 / screen_w - 0.5),
97			2.0 * (0.5 - pixel_coords.max.y as f32 / screen_h),
98		),
99	};
100
101	// handle overlapping bounds, modify uv_rect to preserve texture aspect
102	if gl_rect.max.x > gl_bounds.max.x {
103		let old_width = gl_rect.width();
104		gl_rect.max.x = gl_bounds.max.x;
105		tex_coords.max.x = tex_coords.min.x + tex_coords.width() * gl_rect.width() / old_width;
106	}
107	if gl_rect.min.x < gl_bounds.min.x {
108		let old_width = gl_rect.width();
109		gl_rect.min.x = gl_bounds.min.x;
110		tex_coords.min.x = tex_coords.max.x - tex_coords.width() * gl_rect.width() / old_width;
111	}
112	// note: y access is flipped gl compared with screen,
113	// texture is not flipped (ie is a headache)
114	if gl_rect.max.y < gl_bounds.max.y {
115		let old_height = gl_rect.height();
116		gl_rect.max.y = gl_bounds.max.y;
117		tex_coords.max.y = tex_coords.min.y + tex_coords.height() * gl_rect.height() / old_height;
118	}
119	if gl_rect.min.y > gl_bounds.min.y {
120		let old_height = gl_rect.height();
121		gl_rect.min.y = gl_bounds.min.y;
122		tex_coords.min.y = tex_coords.max.y - tex_coords.height() * gl_rect.height() / old_height;
123	}
124
125	GlyphVertex {
126		left_top: [gl_rect.min.x, gl_rect.max.y, z],
127		right_bottom: [gl_rect.max.x, gl_rect.min.y],
128		tex_left_top: [tex_coords.min.x, tex_coords.max.y],
129		tex_right_bottom: [tex_coords.max.x, tex_coords.min.y],
130		color,
131	}
132}
133
134/*
135/// Object allowing glyph drawing, containing cache state. Manages glyph positioning cacheing,
136/// glyph draw caching & efficient GPU texture cache updating and re-sizing on demand.
137///
138/// Build using a [`GlyphBrushBuilder`](struct.GlyphBrushBuilder.html).
139///
140/// # Example
141///
142/// ```no_run
143/// # extern crate gfx;
144/// # extern crate gfx_window_glutin;
145/// # extern crate glutin;
146/// extern crate gfx_glyph;
147/// # use gfx_glyph::{GlyphBrushBuilder};
148/// use gfx_glyph::Section;
149/// # fn main() -> Result<(), String> {
150/// # let events_loop = glutin::EventsLoop::new();
151/// # let (_window, _device, mut gfx_factory, gfx_color, gfx_depth) =
152/// #     gfx_window_glutin::init::<gfx::format::Srgba8, gfx::format::Depth>(
153/// #         glutin::WindowBuilder::new(),
154/// #         glutin::ContextBuilder::new(),
155/// #         &events_loop);
156/// # let mut gfx_encoder: gfx::Encoder<_, _> = gfx_factory.create_command_buffer().into();
157/// # let dejavu: &[u8] = include_bytes!("../../fonts/DejaVuSans.ttf");
158/// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu)
159/// #     .build(gfx_factory.clone());
160/// # let some_other_section = Section { text: "another", ..Section::default() };
161///
162/// let section = Section {
163///     text: "Hello gfx_glyph",
164///     ..Section::default()
165/// };
166///
167/// glyph_brush.queue(section);
168/// glyph_brush.queue(some_other_section);
169///
170/// glyph_brush.draw_queued(&mut gfx_encoder, &gfx_color, &gfx_depth)?;
171/// # Ok(())
172/// # }
173/// ```
174///
175/// # Caching behaviour
176///
177/// Calls to [`GlyphBrush::queue`](#method.queue),
178/// [`GlyphBrush::pixel_bounds`](#method.pixel_bounds), [`GlyphBrush::glyphs`](#method.glyphs)
179/// calculate the positioned glyphs for a section.
180/// This is cached so future calls to any of the methods for the same section are much
181/// cheaper. In the case of [`GlyphBrush::queue`](#method.queue) the calculations will also be
182/// used for actual drawing.
183///
184/// The cache for a section will be **cleared** after a
185/// [`GlyphBrush::draw_queued`](#method.draw_queued) call when that section has not been used since
186/// the previous draw call.
187*/
188
189pub struct GlyphBrush<'font, 'a, H :BuildHasher = DefaultSectionHasher> {
190	glyph_brush :glyph_brush::GlyphBrush<'font, H>,
191	params :glium::DrawParameters<'a>,
192	program :Program,
193	texture :Texture2d,
194	index_buffer :glium::index::NoIndices,
195	vertex_buffer :glium::VertexBuffer<GlyphVertex>,
196	instances :glium::VertexBuffer<InstanceVertex>,
197}
198
199static VERTEX_SHADER :&str = include_str!("shader/vert.glsl");
200static FRAGMENT_SHADER :&str = include_str!("shader/frag.glsl");
201
202impl<'font, 'p> GlyphBrush<'font, 'p> {
203	pub fn new<'a :'font, F :Facade, V :Into<Vec<Font<'a>>>>(facade :&F, fonts :V) -> Self {
204		let glyph_brush = glyph_brush::GlyphBrushBuilder::using_fonts(fonts).build();
205		let program = Program::from_source(facade, VERTEX_SHADER,
206			FRAGMENT_SHADER, None).unwrap();
207		let params = glium::DrawParameters {
208			blend :glium::Blend::alpha_blending(),
209			.. Default::default()
210		};
211		let (twidth, theight) = glyph_brush.texture_dimensions();
212		let texture = Texture2d::empty(facade, twidth, theight).unwrap();
213		let index_buffer = glium::index::NoIndices(PrimitiveType::TriangleStrip);
214
215		// We only need this so that we have groups of four
216		// instances each which is what the shader expects.
217		// Dunno if there is a nicer way to do this than this
218		// hack.
219		let instances = glium::VertexBuffer::new(facade, &[InstanceVertex{ v : 0.0}; 4]).unwrap();
220		let vertex_buffer = glium::VertexBuffer::empty(facade, 0).unwrap();
221
222		GlyphBrush {
223			glyph_brush,
224			params,
225			program,
226			texture,
227			index_buffer,
228			vertex_buffer,
229			instances,
230		}
231	}
232}
233
234impl<'font, 'p, H :BuildHasher> GlyphBrush<'font, 'p, H> {
235	/// Queues a section/layout to be drawn by the next call of
236	/// [`draw_queued`](struct.GlyphBrush.html#method.draw_queued). Can be called multiple times
237	/// to queue multiple sections for drawing.
238	///
239	/// Used to provide custom `GlyphPositioner` logic, if using built-in
240	/// [`Layout`](enum.Layout.html) simply use [`queue`](struct.GlyphBrush.html#method.queue)
241	///
242	/// Benefits from caching, see [caching behaviour](#caching-behaviour).
243	#[inline]
244	pub fn queue_custom_layout<'a, S, G>(&mut self, section: S, custom_layout: &G)
245	where
246		G: GlyphPositioner,
247		S: Into<Cow<'a, VariedSection<'a>>>,
248	{
249		self.glyph_brush.queue_custom_layout(section, custom_layout)
250	}
251
252	/// Queues a section/layout to be drawn by the next call of
253	/// [`draw_queued`](struct.GlyphBrush.html#method.draw_queued). Can be called multiple times
254	/// to queue multiple sections for drawing.
255	///
256	/// Benefits from caching, see [caching behaviour](#caching-behaviour).
257	#[inline]
258	pub fn queue<'a, S>(&mut self, section: S)
259	where
260		S: Into<Cow<'a, VariedSection<'a>>>,
261	{
262		self.glyph_brush.queue(section)
263	}
264
265
266	/*
267	/// Draws all queued sections onto a render target.
268	/// See [`queue`](struct.GlyphBrush.html#method.queue).
269	///
270	/// Trims the cache, see [caching behaviour](#caching-behaviour).
271	///
272	/// # Raw usage
273	/// Can also be used with gfx raw render & depth views if necessary. The `Format` must also
274	/// be provided. [See example.](struct.GlyphBrush.html#raw-usage-1)
275	*/
276
277	#[inline]
278	pub fn draw_queued<F :Facade + Deref<Target = Context>>(&mut self, facade :&F, frame :&mut Frame) {
279		self.draw_queued_with_transform(IDENTITY_MATRIX4, facade, frame)
280	}
281
282	/*
283	/// Draws all queued sections onto a render target, applying a position transform (e.g.
284	/// a projection).
285	/// See [`queue`](struct.GlyphBrush.html#method.queue).
286	///
287	/// Trims the cache, see [caching behaviour](#caching-behaviour).
288	///
289	/// # Raw usage
290	/// Can also be used with gfx raw render & depth views if necessary. The `Format` must also
291	/// be provided.
292	///
293	/// ```no_run
294	/// # extern crate gfx;
295	/// # extern crate gfx_window_glutin;
296	/// # extern crate glutin;
297	/// # extern crate gfx_glyph;
298	/// # use gfx_glyph::{GlyphBrushBuilder};
299	/// # use gfx_glyph::Section;
300	/// # use gfx::format;
301	/// # use gfx::format::Formatted;
302	/// # use gfx::memory::Typed;
303	/// # fn main() -> Result<(), String> {
304	/// # let events_loop = glutin::EventsLoop::new();
305	/// # let (_window, _device, mut gfx_factory, gfx_color, gfx_depth) =
306	/// #     gfx_window_glutin::init::<gfx::format::Srgba8, gfx::format::Depth>(
307	/// #         glutin::WindowBuilder::new(),
308	/// #         glutin::ContextBuilder::new(),
309	/// #         &events_loop);
310	/// # let mut gfx_encoder: gfx::Encoder<_, _> = gfx_factory.create_command_buffer().into();
311	/// # let dejavu: &[u8] = include_bytes!("../../fonts/DejaVuSans.ttf");
312	/// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu)
313	/// #     .build(gfx_factory.clone());
314	/// # let raw_render_view = gfx_color.raw();
315	/// # let raw_depth_view = gfx_depth.raw();
316	/// # let transform = [[0.0; 4]; 4];
317	/// glyph_brush.draw_queued_with_transform(
318	///     transform,
319	///     &mut gfx_encoder,
320	///     &(raw_render_view, format::Srgba8::get_format()),
321	///     &(raw_depth_view, format::Depth::get_format()),
322	/// )?
323	/// # ;
324	/// # Ok(())
325	/// # }
326	/// ```
327	*/
328
329	pub fn draw_queued_with_transform<F :Facade + Deref<Target = Context>>(&mut self, transform :[[f32; 4]; 4],  facade :&F, frame :&mut Frame) {
330		let screen_dims = facade.get_framebuffer_dimensions();
331		let mut brush_action;
332		loop {
333
334			// We need this scope because of lifetimes.
335			// Ultimately, we'd like to put the &self.texture
336			// into the closure, but that'd inevitably
337			// borrow the entirety of self inside the closure.
338			// This is a problem with the language and is
339			// discussed here:
340			// http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
341			{
342				let tex = &self.texture;
343				brush_action = self.glyph_brush.process_queued(
344					screen_dims,
345					|rect, tex_data| {
346						update_texture(tex, rect, tex_data);
347					},
348					to_vertex,
349				);
350			}
351			match brush_action {
352				Ok(_) => break,
353				Err(BrushError::TextureTooSmall { suggested }) => {
354					let (nwidth, nheight) = suggested;
355					self.texture = Texture2d::empty(facade, nwidth, nheight).unwrap();
356					self.glyph_brush.resize_texture(nwidth, nheight);
357				},
358			}
359		}
360
361		let sampler = glium::uniforms::Sampler::new(&self.texture)
362			.wrap_function(glium::uniforms::SamplerWrapFunction::Clamp)
363			.minify_filter(glium::uniforms::MinifySamplerFilter::Linear)
364			.magnify_filter(glium::uniforms::MagnifySamplerFilter::Linear);
365
366		match brush_action.unwrap() {
367			BrushAction::Draw(verts) => {
368				self.vertex_buffer = glium::VertexBuffer::new(facade, &verts).unwrap();
369			},
370			BrushAction::ReDraw => {},
371		};
372
373		let uniforms = uniform! {
374			font_tex: sampler,
375			transform: transform,
376		};
377
378		// drawing a frame
379		frame.draw((&self.instances, self.vertex_buffer.per_instance().unwrap()),
380			&self.index_buffer, &self.program, &uniforms, &self.params).unwrap();
381	}
382
383	/// Returns the available fonts.
384	///
385	/// The `FontId` corresponds to the index of the font data.
386	#[inline]
387	pub fn fonts(&self) -> &[Font<'font>] {
388		self.glyph_brush.fonts()
389	}
390
391	/*
392	/// Adds an additional font to the one(s) initially added on build.
393	///
394	/// Returns a new [`FontId`](struct.FontId.html) to reference this font.
395	///
396	/// # Example
397	///
398	/// ```no_run
399	/// # extern crate gfx;
400	/// # extern crate gfx_window_glutin;
401	/// # extern crate glutin;
402	/// extern crate gfx_glyph;
403	/// use gfx_glyph::{GlyphBrushBuilder, Section};
404	/// # fn main() {
405	/// # let events_loop = glutin::EventsLoop::new();
406	/// # let (_window, _device, mut gfx_factory, gfx_color, gfx_depth) =
407	/// #     gfx_window_glutin::init::<gfx::format::Srgba8, gfx::format::Depth>(
408	/// #         glutin::WindowBuilder::new(),
409	/// #         glutin::ContextBuilder::new(),
410	/// #         &events_loop);
411	/// # let mut gfx_encoder: gfx::Encoder<_, _> = gfx_factory.create_command_buffer().into();
412	///
413	/// // dejavu is built as default `FontId(0)`
414	/// let dejavu: &[u8] = include_bytes!("../../fonts/DejaVuSans.ttf");
415	/// let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build(gfx_factory.clone());
416	///
417	/// // some time later, add another font referenced by a new `FontId`
418	/// let open_sans_italic: &[u8] = include_bytes!("../../fonts/OpenSans-Italic.ttf");
419	/// let open_sans_italic_id = glyph_brush.add_font_bytes(open_sans_italic);
420	/// # glyph_brush.draw_queued(&mut gfx_encoder, &gfx_color, &gfx_depth).unwrap();
421	/// # let _ = open_sans_italic_id;
422	/// # }
423	/// ```
424	*/
425
426	pub fn add_font_bytes<'a: 'font, B: Into<SharedBytes<'a>>>(&mut self, font_data: B) -> FontId {
427		self.glyph_brush.add_font_bytes(font_data)
428	}
429
430	/// Adds an additional font to the one(s) initially added on build.
431	///
432	/// Returns a new [`FontId`](struct.FontId.html) to reference this font.
433	pub fn add_font<'a: 'font>(&mut self, font_data: Font<'a>) -> FontId {
434		self.glyph_brush.add_font(font_data)
435	}
436}