1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module g4d.element.text;
8 import g4d.element.base,
9        g4d.ft.font,
10        g4d.ft.texture,
11        g4d.gl.buffer,
12        g4d.shader.base,
13        g4d.exception;
14 import gl3n.linalg;
15 import std.algorithm,
16        std.math;
17 
18 /// A struct of character polygon.
19 private struct CharPoly
20 {
21     ArrayBuffer pos;
22     ArrayBuffer uv;
23 }
24 
25 /// An element that draws text horizontally.
26 class HTextElement : Element
27 {
28     protected struct Poly
29     {
30         dchar       c;
31         vec2        pos;
32         float       length;
33         ArrayBuffer posBuf;
34     }
35 
36     protected Poly[]      _polys;
37     protected TextTexture _texture;
38 
39     /// Polygons to be drawn.
40     const @property polys () { return _polys; }
41 
42     protected vec2 _size;
43     /// Size of area text will be drawn.
44     const @property size () { return _size; }
45 
46     ///
47     this ()
48     {
49         clear();
50     }
51 
52     ///
53     void clear ()
54     {
55         _polys   = [];
56         _texture = null;
57         _size    = vec2(0,0);
58     }
59 
60     /// Renders the text texture,
61     /// And calculates position of each characters.
62     void loadText ( FontFace face, dstring text )
63     {
64         clear();
65         _texture = new TextTexture( face, text );
66 
67         auto  curpos   = vec2(0,0);
68         const fontsize = face.size.y;
69 
70         float left, top, right, bottom;
71 
72         foreach ( c; text ) {
73             const metrics = _texture.chars[c];
74             auto  poly    = Poly(c);
75 
76             left   = curpos.x + metrics.horiBearing.x;
77             top    = curpos.y - fontsize + metrics.horiBearing.y;
78             right  = left + metrics.size.x;
79             bottom = top - metrics.size.y;
80 
81             _size.x = max( right, _size.x );
82             _size.y = max( bottom.abs, _size.y );
83 
84             poly.pos    = vec2( left, top );
85             poly.length = metrics.horiAdvance;
86             poly.posBuf = new ArrayBuffer([
87                 left ,top   ,0f,1f,
88                 right,top   ,0f,1f,
89                 right,bottom,0f,1f,
90                 left ,bottom,0f,1f,
91             ]);
92 
93             _polys   ~= poly;
94             curpos.x += metrics.horiAdvance;
95         }
96     }
97 
98     ///
99     void draw ( Shader s )
100     {
101         if ( !_polys.length ) return;
102         assert( s && _texture );
103 
104         const saver = ShaderStateSaver(s);
105         s.applyMatrix();
106         s.uploadTexture( _texture );
107 
108         foreach ( p; _polys ) {
109             auto uv  = _texture.chars[p.c].uv;
110             auto pos = p.posBuf;
111 
112             s.uploadUvBuffer( uv );
113             s.uploadPositionBuffer( pos );
114             s.drawFan( 4 );
115         }
116     }
117 }