1 module des.gl.simple.textout;
2 
3 import des.fonts.ftglyphrender;
4 
5 import des.gl.simple;
6 import des.gl.simple.shader.text;
7 
8 import std.traits;
9 import std.conv;
10 import std..string;
11 
12 import des.util.logsys;
13 import des.util.arch.emm;
14 
15 wstring wformat(S,Args...)( S fmt, Args args )
16     if( is( S == string ) || is( S == wstring ) )
17 { return to!wstring( format( to!string(fmt), args ) ); }
18 
19 class BaseLineTextBox : GLSimpleObject
20 {
21 private:
22 
23     GLBuffer vert, uv;
24 
25     GLTexture tex;
26 
27     wstring output;
28     vec2 output_size;
29 
30     vec2 pos;
31 
32     BitmapFont font;
33 
34     void repos()
35     {
36         if( output.length == 0 )
37             return;
38         vec2[] vert_data;
39         vec2[] uv_data;
40 
41         output_size = vec2(0);
42 
43         float offsetx = 0;
44         foreach( c; output )
45         {
46             if( c !in font.size )
47                 continue;
48             if( font.size[c].h > output_size.h )
49                 output_size.h = font.size[c].h;
50         }
51         
52         foreach( c; output )
53         {
54             if( c !in font.size )
55             {
56                 logger.error( "Character "w ~ c ~ "not in bitmap font."w );
57                 continue;
58             }
59             output_size.w += font.size[c].w;
60 
61             {
62                 auto v1 = pos + vec2( font.bearing[c].x + offsetx, output_size.h + font.bearing[c].y );
63                 auto v2 = v1 + font.size[c];
64 
65                 vert_data ~= vec2( v1.x, v2.y );
66                 vert_data ~= v1;
67                 vert_data ~= vec2( v2.x, v1.y );
68 
69                 vert_data ~= v2;
70                 vert_data ~= vec2( v1.x, v2.y );
71                 vert_data ~= vec2( v2.x, v1.y );
72 
73                 offsetx += font.size[c].x;
74             }
75 
76             {
77                 auto uvoffset = vec2( font.offset[c] ) / vec2( font.texture.size );
78                 auto uvsize = vec2( font.size[c] ) / vec2( font.texture.size );
79 
80                 auto uv1 = uvoffset;
81                 auto uv2 = uv1 + uvsize;
82 
83                 uv_data ~= vec2( uv1.x, uv2.y );
84                 uv_data ~= uv1;
85                 uv_data ~= vec2( uv2.x, uv1.y );
86 
87                 uv_data ~= uv2;
88                 uv_data ~= vec2( uv1.x, uv2.y );
89                 uv_data ~= vec2( uv2.x, uv1.y );
90             }
91         }
92 
93         vert.setData( vert_data );
94         uv.setData( uv_data );
95     }
96 
97 public:
98     this( string font_name, uint size=24u )
99     {
100         super( SS_WIN_TEXT );
101 
102         vert = createArrayBuffer();
103         setAttribPointer( vert, shader.getAttribLocation( "vert" ), 2, GLType.FLOAT );
104 
105         uv = createArrayBuffer();
106         setAttribPointer( uv, shader.getAttribLocation( "uv" ), 2, GLType.FLOAT );
107 
108         tex = newEMM!GLTexture( GLTexture.Target.T2D );
109 
110         tex.setMinFilter( GLTexture.Filter.NEAREST );
111         tex.setMagFilter( GLTexture.Filter.NEAREST );
112 
113         GlyphParam gparam;
114         gparam.height = size;
115 
116         auto grender = FTGlyphRender.get( font_name );
117 
118         grender.setParams( gparam );
119 
120         auto symbols = "!\"#$%&'()*+,-./0123456789:;<=>?@[\\]^_`{|}~^? "w;
121         auto english = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"w;
122         auto russian = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"w;
123 
124         font = grender.generateBitmapFont( symbols ~ english ~ russian );
125 
126 
127         tex.image( font.texture );
128 
129         text = "Default text";
130     }
131 
132     void draw( ivec2 win_size )
133     {
134         if( output.length == 0 )
135             return;
136         shader.setUniform!ivec2( "win_size", win_size );
137         tex.bind();
138             glDisable(GL_DEPTH_TEST);
139             drawArrays( DrawMode.TRIANGLES );
140         tex.unbind();
141     }
142 
143     @property
144     {
145         void text(T)( T t )
146             if( isSomeString!T )//TODO is convertable to wstring
147         {
148             import std.conv;
149             output = to!wstring( t );
150             repos();
151         }
152         wstring text(){ return output; }
153 
154         void position( vec2 pos )
155         { 
156             this.pos = pos; 
157             repos(); 
158         }
159 
160         void color( vec3 col ){ shader.setUniform!vec3( "color", col ); }
161 
162         vec2 size(){ return output_size; }
163     }
164 }
165 
166 class BaseMultiLineTextBox : ExternalMemoryManager
167 {
168     mixin EMM;
169 private:
170     BaseLineTextBox[] lines;
171 
172     wstring output;
173     vec2 output_size;
174     vec2 pos;
175 
176     string font_name;
177     uint font_size;
178 
179     void repos()
180     {
181         foreach( ref l; lines )
182             l.destroy();
183         lines.destroy();
184         auto ll = output.split( "\n" );
185         float ysize = 0;
186         float xsize = 0;
187         foreach( i, l; ll )
188         {
189             lines ~= newEMM!BaseLineTextBox( font_name, font_size );
190             lines[$-1].text = l;
191             lines[$-1].color = col;
192             lines[$-1].position = pos + vec2( 0, i * font_size );
193             if( lines[$-1].size.x > xsize )
194                 xsize = lines[$-1].size.x;
195             ysize += lines[$-1].size.y;
196         }
197 
198         output_size = vec2( xsize, ysize );
199     }
200 
201     vec3 col;
202 public:
203     this( string font_name, uint font_size = 24u )
204     {
205         this.font_name = font_name;
206         this.font_size = font_size;
207 
208         text = 
209 `Default
210 Multi
211 Line
212 Text`;
213     }
214 
215     void draw( ivec2 win_size )
216     {
217         foreach( l; lines )
218             l.draw( win_size );
219     }
220 
221     @property
222     {
223         void text(T)( T t )
224             if( isSomeString!T )
225         {
226             output = to!wstring( t );
227             repos();
228         }
229         wstring text(){ return output; }
230 
231         void position( vec2 pos )
232         { 
233             this.pos = pos; 
234             repos(); 
235         }
236 
237         void color( vec3 col ){ this.col = col; }
238 
239         vec2 size(){ return output_size; }
240     }
241 }