1#![allow(clippy::nonminimal_bool)]
2
3#[macro_use]
4extern crate glium;
5extern crate unicode_normalization;
6extern crate rusttype;
7
8use std::borrow::Cow;
9use std::ops::Range;
10
11use glium::texture::Texture2d;
12use glium::backend::Facade;
13use glium::backend::glutin::Display;
14
15pub use rusttype::{Scale, point, Point, vector, Vector, Rect, SharedBytes};
16use rusttype::{Font, FontCollection, PositionedGlyph, Glyph, GlyphId};
17use rusttype::gpu_cache::*;
18
19use self::unicode_normalization::UnicodeNormalization;
20
21
22
23pub struct Render<'a> {
24 fonts: Vec<Font<'a>>,
25 pub texture: Texture2d,
26 cache: Cache<'a>,
27 hidpi_factor: f64,
28 tolerance: (f32, f32),
29}
30impl<'a> Render<'a> {
31 pub fn new(gl: &Display, texture_size: u32, tolerance: (f32, f32)) -> Self {
33 let dpi = gl.gl_window().window().scale_factor();
34 let initial_size = (texture_size as f64 * dpi) as u32;
36
37 let mut ret = Render {
38 fonts: Vec::new(),
39 texture: Texture2d::empty_with_format(
40 gl,
41 glium::texture::UncompressedFloatFormat::U8,
42 glium::texture::MipmapsOption::NoMipmap,
43 initial_size,
44 initial_size,
45 ).unwrap(),
46 cache: Cache::builder().build(),
47 hidpi_factor: dpi, tolerance,
49 };
50 ret.set_cache(gl, initial_size);
51 ret
52 }
53
54 pub fn add_fonts<B>(&mut self, bytes: B) -> Result<(), rusttype::Error>
55 where
56 B: Into<SharedBytes<'a>>,
57 {
58 let fonts = FontCollection::from_bytes(bytes)?;
59 for font in fonts.into_fonts() {
60 self.fonts.push(font?);
61 }
62 Ok(())
63 }
64
65 fn set_cache<G: Facade>(&mut self, gl: &G, size: u32) {
66 let dim = (size, size);
67 if self.cache.dimensions() != dim {
68 self.cache = Cache::builder()
69 .dimensions(size, size)
70 .scale_tolerance(self.tolerance.0)
71 .position_tolerance(self.tolerance.1)
72 .build();
73 }
74 if self.texture.dimensions() != dim {
75 self.texture = Texture2d::empty(gl, size, size).unwrap();
76 }
77 }
78}
79
80pub struct Text<'a> {
82 pub style: Style,
83 pub text: Cow<'a, str>,
84}
85impl<'a, I> From<(Style, I)> for Text<'a>
86where
87 I: Into<Cow<'a, str>>,
88{
89 fn from((s, t): (Style, I)) -> Self {
90 Text {
91 style: s,
92 text: t.into(),
93 }
94 }
95}
96impl<'a> From<String> for Text<'a> {
98 fn from(t: String) -> Self {
99 Text {
100 style: Style::default(),
101 text: t.into(),
102 }
103 }
104}
105impl<'a> From<&'a str> for Text<'a> {
106 fn from(t: &'a str) -> Self {
107 Text {
108 style: Style::default(),
109 text: t.into(),
110 }
111 }
112}
113
114#[derive(Debug, Clone, Copy)]
115pub struct Style {
116 pub scale: f32,
118 pub color: [u8; 4],
120 pub fontid: usize,
122
123 pub shadow: Option<[u8; 4]>,
125 pub bold: bool,
128 pub italic: bool,
130 pub underline: bool,
132 pub strike: bool,
134}
135impl Default for Style {
136 fn default() -> Self {
137 Style {
138 scale: 24.0,
139 color: [0, 0, 0, 0xFF],
140 fontid: 0,
141
142 shadow: None,
143 bold: false,
144 italic: false,
145 underline: false,
146 strike: false,
147 }
148 }
149}
150impl Style {
151 pub fn new() -> Self {
152 Self::default()
153 }
154
155 fn get_glyph<'context, 'fonts>(
156 &self,
157 c: char,
158 fonts: &'context [Font<'fonts>],
159 ) -> (usize, Glyph<'fonts>)
160 where
161 'fonts: 'context,
162 {
163 let font = &fonts[self.fontid];
166 let g = font.glyph(c);
167 (self.fontid, g)
168 }
169}
170
171pub enum FlowControl {
172 NextGlyph,
173 SkipBlock,
174 StopBuild,
175}
176
177#[derive(Debug, Clone)]
179pub struct TextBlock<P> {
180 pub origin: P,
182 pub width: u32,
183}
184impl<P> TextBlock<P> {
185 fn to_block(&self) -> LayoutBlock {
186 LayoutBlock {
187 width: self.width,
188 caret: point(0.0, 0.0),
189 wrap_unit_start: 0,
190 any_break: false,
191 max_area: point(0.0, 0.0),
192 last_glyph_id: None,
193 query_area: Rect {
194 min: point(0.0, 0.0),
195 max: point(0.0, 0.0),
196 },
197 query_hit: false,
198 just_wrapped: false,
199 }
200 }
201}
202
203#[derive(Debug)]
204struct LayoutBlock {
205 width: u32,
206 caret: Point<f32>,
207 wrap_unit_start: usize,
208 any_break: bool,
209 max_area: Point<f32>,
210 last_glyph_id: Option<GlyphId>,
211 query_area: Rect<f32>,
212 query_hit: bool,
213 just_wrapped: bool,
214}
215impl LayoutBlock {
216 fn bump_line(&mut self, advance_height: f32) {
217 if self.caret.x > self.max_area.x { self.max_area.x = self.caret.x; }
218 self.caret = point(0.0, self.caret.y + advance_height);
219 self.max_area.y = self.caret.y;
220 }
221}
222
223#[derive(Default)]
224pub struct RunBuffer<'text, P> {
225 parts: Vec<Text<'text>>,
226 blocks: Vec<(Range<usize>, TextBlock<P>)>,
227 char_count: usize,
228}
229impl<'text, P> RunBuffer<'text, P>
230where
231 P: 'text,
232{
233 pub fn new() -> Self {
234 RunBuffer {
235 parts: Vec::new(),
236 blocks: Vec::new(),
237 char_count: 0,
238 }
239 }
240
241 pub fn push_blocks<I>(&mut self, layout: TextBlock<P>, it: I)
242 where
243 I: Iterator<Item=Text<'text>>,
244 {
245 let start = self.parts.len();
246 for text in it {
247 self.char_count += text.text.len();
248 self.parts.push(text);
249 }
250 let end = self.parts.len();
251 self.blocks.push((start..end, layout));
252 }
253
254 pub fn push<T>(&mut self, layout: TextBlock<P>, t: T)
255 where
256 T: Into<Text<'text>>,
257 {
258 self.push_blocks(layout, Some(t.into()).into_iter())
259 }
260
261 pub fn write<S>(&mut self, pos: P, width: u32, text: S)
262 where
263 S: Into<Cow<'text, str>>,
264 {
265 self.push(
266 TextBlock {
267 origin: pos,
268 width,
269 },
270 Text {
271 text: text.into(),
272 style: Style::default(),
273 },
274 );
275 }
276
277 pub fn reset(&mut self) {
278 self.parts.clear();
279 self.blocks.clear();
280 self.char_count = 0;
281 }
282
283 pub fn glyph_estimate(&self) -> usize {
286 self.char_count
287 }
288
289 pub fn parts(&self) -> &[Text<'text>] {
290 &self.parts[..]
291 }
292
293 pub fn measure_area(
296 &mut self,
297 render: &mut Render,
298 query: Point<f32>,
299 ) -> (
300 Point<f32>,
301 &mut TextBlock<P>,
302 Option<QueryResult>,
303 ) {
304 let &mut(ref range, ref mut layout) = self.blocks.last_mut().expect("measure_area called but there were no blocks");
305 let mut layout_block = layout.to_block();
306 let mut glyphs = Vec::new();
307 let mut max_area = point(0.0, 0.0);
308 let mut qr = None;
309 for (part_index, text) in self.parts[range.clone()].iter().enumerate() {
310 let (nm, q) = layout_block_glyphs(
311 &mut layout_block,
312 &mut glyphs,
313 render.hidpi_factor,
314 &render.fonts,
315 text,
316 query,
317 );
318 if let (None, Some(mut q)) = (&qr, q) {
319 q.part_index = part_index;
320 qr = Some(q);
321 }
322 if nm.x > max_area.x { max_area.x = nm.x; }
323 if nm.y > max_area.y { max_area.y = nm.y; }
324 }
325 layout_block.bump_line(0.0);
326 (max_area, layout, qr)
327 }
328
329 pub fn build<F>(
340 &mut self,
341 render: &mut Render,
342 mut write_glyph: F
343 )
344 where
345 F: for<'x, 'y> FnMut(&'x Text<'y>, &P, Rect<i32>, Rect<f32>) -> FlowControl,
346 {
347 let mut glyphs: Vec<(usize, PositionedGlyph)> = Vec::new();
348 let mut glyph_block: Vec<(&TextBlock<P>, &Text, Range<usize>)> = Vec::new();
349 for (range, layout) in &self.blocks {
350 let mut layout_block = layout.to_block();
351 for text in &self.parts[range.clone()] {
352 let start = glyphs.len();
353 layout_block_glyphs(
354 &mut layout_block,
355 &mut glyphs,
356 render.hidpi_factor,
357 &render.fonts,
358 text,
359 point(-9e9, -9e9),
360 );
361 let end = glyphs.len();
362 glyph_block.push((layout, text, start..end));
363 }
364 }
365 for &(fontid, ref glyph) in &glyphs {
366 render.cache.queue_glyph(fontid, glyph.clone());
367 }
368 let texture = &mut render.texture;
369 render.cache.cache_queued(|rect, data| {
373 texture.main_level().write(glium::Rect {
374 left: rect.min.x,
375 bottom: rect.min.y,
376 width: rect.width(),
377 height: rect.height(),
378 }, glium::texture::RawImage2d {
379 data: Cow::Borrowed(data),
380 width: rect.width(),
381 height: rect.height(),
382 format: glium::texture::ClientFormat::U8,
383 });
384 }).unwrap();
385 for (layout, text, range) in glyph_block {
386 for &(fontid, ref glyph) in &glyphs[range] {
387 if let Ok(Some((uv, pos))) = render.cache.rect_for(fontid, glyph) {
388 match write_glyph(text, &layout.origin, pos, uv) {
389 FlowControl::NextGlyph => (),
390 FlowControl::SkipBlock => break,
391 FlowControl::StopBuild => return,
392 }
393 }
394 }
395 }
396 }
397}
398fn layout_block_glyphs<'layout, 'context, 'fonts, 'result, 'text>(
399 layout: &'layout mut LayoutBlock,
400 result: &'result mut Vec<(usize, PositionedGlyph<'fonts>)>,
401 dpi: f64,
402 fonts: &'context [Font<'fonts>],
403 text: &'text Text<'text>,
404 query: Point<f32>,
405) -> (Point<f32>, Option<QueryResult>)
406where
407 'fonts: 'context,
408{
409 let mut query_result = None;
410 let style = &text.style;
411 let scale = Scale::uniform((text.style.scale as f64 * dpi) as f32);
412 let v_metrics = fonts[0].v_metrics(scale); let advance_height: f32 = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap; let is_separator = |c: char| c == ' ';
415 let caret2rect = |caret: Point<f32>| -> Rect<f32> {
416 let min = caret - vector(0.0, v_metrics.ascent);
417 let max = caret; Rect { min, max }
419 };
420 layout.query_area = caret2rect(layout.caret);
421 for (nfc_char_index, c) in text.text.nfc().enumerate() {
422 if c.is_control() {
423 if c == '\n' {
424 layout.bump_line(advance_height);
425 layout.query_area = caret2rect(layout.caret);
426 layout.wrap_unit_start = result.len();
427 layout.any_break = false;
428 layout.query_hit = false;
429 }
430 continue;
431 }
432 if is_separator(c) {
433 layout.wrap_unit_start = result.len() + 1; layout.any_break = true;
435 layout.query_hit = false;
436 layout.query_area = caret2rect(layout.caret);
437 }
438 let (fontid, base_glyph) = style.get_glyph(c, fonts);
439 let font = &fonts[fontid];
440 if !layout.just_wrapped {
441 if let Some(id) = layout.last_glyph_id.replace(base_glyph.id()) {
442 layout.caret.x += font.pair_kerning(scale, id, base_glyph.id()); }
444 }
445 let mut glyph: PositionedGlyph = base_glyph.scaled(scale).positioned(layout.caret);
446 let bb = glyph.pixel_bounding_box();
447 if let Some(bb) = bb {
448 if bb.max.x as i64 > layout.width as i64 {
449 layout.bump_line(advance_height);
450 if result.len() > layout.wrap_unit_start && layout.any_break {
451 let delta = layout.caret - result[layout.wrap_unit_start].1.position();
454 for &mut (_, ref mut g) in &mut result[layout.wrap_unit_start..] {
455 *g = g.clone().into_unpositioned().positioned(g.position() + delta);
456 }
457 let last = &result.last().expect("any glyphs").1;
458 layout.caret = last.position();
459 layout.caret.x += last.unpositioned().h_metrics().advance_width;
460 layout.query_area.min = layout.query_area.min + delta;
463 layout.query_area.max = layout.query_area.max + delta;
464 } else {
465 layout.query_area = caret2rect(layout.caret);
468 }
469 if layout.query_hit {
470 layout.query_hit = false;
471 query_result = None;
472 }
473 layout.any_break = false;
474 glyph = glyph.into_unpositioned().positioned(layout.caret);
475 layout.last_glyph_id = None;
476 layout.just_wrapped = true;
477 }
478 }
479 let metrics = glyph.unpositioned().h_metrics();
480 layout.caret.x += metrics.advance_width;
481 layout.query_area.max.x = layout.caret.x;
482 if true
483 && query_result.is_none()
484 && (layout.query_area.min.x <= query.x && query.x <= layout.query_area.max.x)
485 && (layout.query_area.min.y <= query.y && query.y <= layout.query_area.max.y)
486 && layout.query_area.min.x != layout.query_area.max.x
487 {
488 layout.query_hit = true;
489 query_result = Some(QueryResult {
490 part_index: !0, nfc_char_index,
492 area: layout.query_area,
493 });
494 } else if let (Some(qr), true) = (&mut query_result, layout.query_hit) {
495 qr.area.max.x = qr.area.max.x.max(layout.caret.x);
496 }
497 result.push((fontid, glyph));
498 layout.just_wrapped = false;
500 }
501 let mut ret = layout.max_area;
502 ret.x = ret.x.max(layout.caret.x);
503 (ret, query_result)
504}
505
506#[derive(Debug, Clone)]
507pub struct QueryResult {
508 pub part_index: usize,
510 pub nfc_char_index: usize,
514 pub area: Rect<f32>,
517}
518
519pub mod simple2d {
520 use super::*;
521
522 use glium;
523 use glium::backend::Facade;
524 use glium::{Program, Surface};
525 use glium::program::ProgramChooserCreationError;
526
527 pub struct Simple2d {
529 pub program: Program,
530 }
531 impl Simple2d {
532 pub fn create<G: Facade>(gl: &G) -> Result<Simple2d, ProgramChooserCreationError> {
533 let program = program!(
534 gl,
535 140 => {
537 vertex: "
538 #version 140
539
540 in vec2 position;
541 in vec2 tex_coords;
542 in vec4 color;
543
544 out vec2 v_tex_coords;
545 out vec4 v_color;
546
547 void main() {
548 gl_Position = vec4(position, 0.0, 1.0);
549 v_tex_coords = tex_coords;
550 v_color = color;
551 }
552 ",
553
554 fragment: "
555 #version 140
556 uniform sampler2D tex;
557 in vec2 v_tex_coords;
558 in vec4 v_color;
559 out vec4 f_color;
560
561 void main() {
562 f_color = v_color * vec4(1.0, 1.0, 1.0, texture(tex, v_tex_coords).r);
563 }
564 "
565 }
566 );
567 program.map(|p| Simple2d {
568 program: p,
569 })
570 }
571
572 pub fn draw<G: Facade>(
573 &self,
574 gl: &G,
575 target: &mut glium::Frame,
576 font: &mut Render,
577 buffer: &mut RunBuffer<(i32, i32)>
578 ) -> Result<(), glium::DrawError>
579 {
580 let (screen_width, screen_height) = target.get_dimensions();
581 let (screen_width, screen_height) = (screen_width as f32, screen_height as f32);
582 let mut vertices = Vec::new();
583 buffer.build(font, |text, origin, pos_rect, uv_rect| {
584 let color = {
585 let c = text.style.color;
586 let f = |c| c as f32 / 255.0;
587 [f(c[0]), f(c[1]), f(c[2]), f(c[3])]
588 };
589 let origin = vector(origin.0, origin.1);
590 let (gl_rect_min, gl_rect_max) = (
591 2.0 * vector(
592 (pos_rect.min.x + origin.x) as f32 / screen_width - 0.5,
593 1.0 - (pos_rect.min.y + origin.y) as f32 / screen_height - 0.5,
594 ),
595 2.0 * vector(
596 (pos_rect.max.x + origin.x) as f32 / screen_width - 0.5,
597 1.0 - (pos_rect.max.y + origin.y) as f32 / screen_height - 0.5,
598 ),
599 );
600 let verts = [
601 Vertex {
602 position: [gl_rect_min.x, gl_rect_max.y],
603 tex_coords: [uv_rect.min.x, uv_rect.max.y],
604 color,
605 },
606 Vertex {
607 position: [gl_rect_min.x, gl_rect_min.y],
608 tex_coords: [uv_rect.min.x, uv_rect.min.y],
609 color,
610 },
611 Vertex {
612 position: [gl_rect_max.x, gl_rect_min.y],
613 tex_coords: [uv_rect.max.x, uv_rect.min.y],
614 color,
615 },
616 Vertex {
617 position: [gl_rect_max.x, gl_rect_min.y],
618 tex_coords: [uv_rect.max.x, uv_rect.min.y],
619 color,
620 },
621 Vertex {
622 position: [gl_rect_max.x, gl_rect_max.y],
623 tex_coords: [uv_rect.max.x, uv_rect.max.y],
624 color,
625 },
626 Vertex {
627 position: [gl_rect_min.x, gl_rect_max.y],
628 tex_coords: [uv_rect.min.x, uv_rect.max.y],
629 color,
630 },
631 ];
632 vertices.extend_from_slice(&verts[..]);
633 FlowControl::NextGlyph
634 });
635
636 let vbo = glium::VertexBuffer::new(gl, &vertices).unwrap();
637 let uniforms = uniform! {
638 tex: font.texture.sampled().magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest)
639 };
640 target.draw(
641 &vbo,
642 glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
643 &self.program,
644 &uniforms,
645 &glium::DrawParameters {
646 blend: glium::Blend::alpha_blending(),
647 .. Default::default()
648 },
649 )
650 }
651 }
652
653
654 #[derive(Copy, Clone)]
655 struct Vertex {
656 position: [f32; 2],
657 tex_coords: [f32; 2],
658 color: [f32; 4]
659 }
660
661 implement_vertex!(Vertex, position, tex_coords, color);
662}