1 /***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: texture OpenGL text rendering built from wxFont
5 * Author: Sean D'Epagnier
6 *
7 ***************************************************************************
8 * Copyright (C) 2014 Sean D'Epagnier *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26 #include <wx/wx.h>
27
28 #include "TexFont.h"
29
30 #ifdef USE_ANDROID_GLES2
31 #include <gl2.h>
32 #include "linmath.h"
33 #include "shaders.h"
34 #else
35 #include <GL/gl.h>
36 #include <GL/glu.h>
37 #endif
38
39 #if 0
40 #define TXF_CACHE 8
41 static TexFontCache s_txf[TXF_CACHE];
42
43 TexFont *GetTexFont(wxFont *pFont)
44 {
45 // rebuild font if needed
46 TexFont *f_cache;
47 unsigned int i;
48 for (i = 0; i < TXF_CACHE && s_txf[i].key != nullptr; i++)
49 {
50 if (s_txf[i].key == pFont) {
51 return &s_txf[i].cache;
52 }
53 }
54 if (i == TXF_CACHE) {
55 i = rand() & (TXF_CACHE -1);
56 }
57 s_txf[i].key = pFont;
58 f_cache = &s_txf[i].cache;
59 f_cache->Build(*pFont);
60 return f_cache;
61 }
62 #endif
63
TexFont()64 TexFont::TexFont( )
65 {
66 texobj = 0;
67 m_blur = false;
68 m_built = false;
69 m_color = wxColor(0,0,0);
70 }
71
~TexFont()72 TexFont::~TexFont( )
73 {
74 Delete( );
75 }
76
77
Build(wxFont & font,bool blur)78 void TexFont::Build( wxFont &font, bool blur )
79 {
80 /* avoid rebuilding if the parameters are the same */
81 if(m_built && (font == m_font) && (blur == m_blur))
82 return;
83
84 m_font = font;
85 m_blur = blur;
86
87 m_maxglyphw = 0;
88 m_maxglyphh = 0;
89
90 wxScreenDC sdc;
91
92 sdc.SetFont( font );
93
94 for( int i = MIN_GLYPH; i < MAX_GLYPH; i++ ) {
95 wxCoord gw, gh;
96 wxString text;
97 if(i == DEGREE_GLYPH)
98 text = wxString::Format(_T("%c"), 0x00B0); //_T("°");
99 else
100 text = wxString::Format(_T("%c"), i);
101 wxCoord descent, exlead;
102 sdc.GetTextExtent( text, &gw, &gh, &descent, &exlead, &font ); // measure the text
103
104 tgi[i].width = gw;
105 tgi[i].height = gh;
106
107 tgi[i].advance = gw;
108
109
110 m_maxglyphw = wxMax(tgi[i].width, m_maxglyphw);
111 m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh);
112 }
113
114 /* add extra pixel to give a border between rows of characters
115 without this, in some cases a faint line can be see on the edge
116 from the character above */
117 m_maxglyphh++;
118
119 int w = COLS_GLYPHS * m_maxglyphw;
120 int h = ROWS_GLYPHS * m_maxglyphh;
121
122 wxASSERT(w < 2048 && h < 2048);
123
124 /* make power of 2 */
125 for(tex_w = 1; tex_w < w; tex_w *= 2);
126 for(tex_h = 1; tex_h < h; tex_h *= 2);
127
128 wxBitmap tbmp(tex_w, tex_h);
129 wxMemoryDC dc;
130 dc.SelectObject(tbmp);
131 dc.SetFont( font );
132
133 /* fill bitmap with black */
134 dc.SetBackground( wxBrush( wxColour( 0, 0, 0 ) ) );
135 dc.Clear();
136
137 /* draw the text white */
138 dc.SetTextForeground( wxColour( 255, 255, 255 ) );
139
140 /* wxPen pen(wxColour( 255, 255, 255 ));
141 wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT);
142 dc.SetPen(pen);
143 dc.SetBrush(brush);
144 */
145 int row = 0, col = 0;
146 for( int i = MIN_GLYPH; i < MAX_GLYPH; i++ ) {
147 if(col == COLS_GLYPHS) {
148 col = 0;
149 row++;
150 }
151
152 tgi[i].x = col * m_maxglyphw;
153 tgi[i].y = row * m_maxglyphh;
154
155 wxString text;
156 if(i == DEGREE_GLYPH)
157 text = wxString::Format(_T("%c"), 0x00B0); //_T("°");
158 else
159 text = wxString::Format(_T("%c"), i);
160
161 dc.DrawText(text, tgi[i].x, tgi[i].y );
162
163 // dc.DrawRectangle(tgi[i].x, tgi[i].y, tgi[i].advance, tgi[i].height);
164 col++;
165 }
166
167 dc.SelectObject(wxNullBitmap);
168
169 wxImage image = tbmp.ConvertToImage();
170
171 GLuint format, internalformat;
172 int stride;
173
174 format = GL_ALPHA;
175 internalformat = format;
176 stride = 1;
177
178 if( m_blur )
179 image = image.Blur(1);
180
181 unsigned char *imgdata = image.GetData();
182
183 if(imgdata){
184 unsigned char *teximage = (unsigned char *) malloc( stride * tex_w * tex_h );
185
186 for( int j = 0; j < tex_w*tex_h; j++ )
187 for( int k = 0; k < stride; k++ )
188 teximage[j * stride + k] = imgdata[3*j];
189
190 Delete();
191
192 glGenTextures( 1, &texobj );
193 glBindTexture( GL_TEXTURE_2D, texobj );
194
195 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
196 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
197 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST/*GL_LINEAR*/ );
198 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
199
200 glTexImage2D( GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0,
201 format, GL_UNSIGNED_BYTE, teximage );
202
203 free(teximage);
204 }
205
206 m_built = true;
207 }
208
Delete()209 void TexFont::Delete( )
210 {
211 if (texobj) {
212 glDeleteTextures(1, &texobj);
213 texobj = 0;
214 }
215 m_built = false;
216 }
217
GetTextExtent(const char * string,int * width,int * height)218 void TexFont::GetTextExtent(const char *string, int *width, int *height)
219 {
220 int w=0, h=0;
221
222 for(int i = 0; string[i]; i++ ) {
223 unsigned char c = string[i];
224 if(c == '\n') {
225 h += tgi[(int)'A'].height;
226 continue;
227 }
228 if(c == 0xc2 && (unsigned char)string[i+1] == 0xb0) {
229 c = DEGREE_GLYPH;
230 i++;
231 }
232 if( c < MIN_GLYPH || c >= MAX_GLYPH)
233 continue;
234
235 TexGlyphInfo &tgisi = tgi[c];
236 w += tgisi.advance;
237 if(tgisi.height > h)
238 h = tgisi.height;
239 }
240 if(width) *width = w;
241 if(height) *height = h;
242 }
243
GetTextExtent(const wxString & string,int * width,int * height)244 void TexFont::GetTextExtent(const wxString &string, int *width, int *height)
245 {
246 GetTextExtent((const char*)string.ToUTF8(), width, height);
247 }
248
RenderGlyph(int c)249 void TexFont::RenderGlyph( int c )
250 {
251 if( c < MIN_GLYPH || c >= MAX_GLYPH)
252 return;
253
254 TexGlyphInfo &tgic = tgi[c];
255
256 int x = tgic.x, y = tgic.y;
257 float w = m_maxglyphw, h = m_maxglyphh;
258 float tx1 = (float)x / (float)tex_w;
259 float tx2 = (float)(x + w) / (float)tex_w;
260 float ty1 = (float)y / (float)tex_h;
261 float ty2 = (float)(y + h) / (float)tex_h;
262
263 #ifndef USE_ANDROID_GLES2
264
265 glBegin( GL_QUADS );
266
267 glTexCoord2f( tx1, ty1 ); glVertex2i( 0, 0 );
268 glTexCoord2f( tx2, ty1 ); glVertex2i( w, 0 );
269 glTexCoord2f( tx2, ty2 ); glVertex2i( w, h );
270 glTexCoord2f( tx1, ty2 ); glVertex2i( 0, h );
271
272 glEnd();
273 glTranslatef( tgic.advance, 0.0, 0.0 );
274 #else
275
276 float uv[8];
277 float coords[8];
278
279 //normal uv
280 uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1;
281 uv[4] = tx2; uv[5] = ty2; uv[6] = tx1; uv[7] = ty2;
282
283 // pixels
284 coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0;
285 coords[4] = w; coords[5] = h; coords[6] = 0; coords[7] = h;
286
287 glUseProgram( texture_2DA_shader_program );
288
289 // Get pointers to the attributes in the program.
290 GLint mPosAttrib = glGetAttribLocation( texture_2DA_shader_program, "aPos" );
291 GLint mUvAttrib = glGetAttribLocation( texture_2DA_shader_program, "aUV" );
292
293 // Set up the texture sampler to texture unit 0
294 GLint texUni = glGetUniformLocation( texture_2DA_shader_program, "uTex" );
295 glUniform1i( texUni, 0 );
296
297 // Disable VBO's (vertex buffer objects) for attributes.
298 glBindBuffer( GL_ARRAY_BUFFER, 0 );
299 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
300
301 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
302 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
303 // ... and enable it.
304 glEnableVertexAttribArray( mPosAttrib );
305
306 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
307 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
308 // ... and enable it.
309 glEnableVertexAttribArray( mUvAttrib );
310
311 float colorv[4];
312 colorv[0] = m_color.Red() / float(256);
313 colorv[1] = m_color.Green() / float(256);
314 colorv[2] = m_color.Blue() / float(256);
315 colorv[3] = 0;
316
317 GLint colloc = glGetUniformLocation(texture_2DA_shader_program,"color");
318 glUniform4fv(colloc, 1, colorv);
319
320 // Rotate
321 float angle = 0;
322 mat4x4 I, Q;
323 mat4x4_identity(I);
324 mat4x4_rotate_Z(Q, I, angle);
325
326 // Translate
327 Q[3][0] = m_dx;
328 Q[3][1] = m_dy;
329
330
331 //mat4x4 X;
332 //mat4x4_mul(X, (float (*)[4])vp->vp_transform, Q);
333
334 GLint matloc = glGetUniformLocation(texture_2DA_shader_program,"TransformMatrix");
335 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
336
337 // Select the active texture unit.
338 glActiveTexture( GL_TEXTURE0 );
339
340 // For some reason, glDrawElements is busted on Android
341 // So we do this a hard ugly way, drawing two triangles...
342 #if 0
343 GLushort indices1[] = {0,1,3,2};
344 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
345 #else
346
347 float co1[8];
348 co1[0] = coords[0];
349 co1[1] = coords[1];
350 co1[2] = coords[2];
351 co1[3] = coords[3];
352 co1[4] = coords[6];
353 co1[5] = coords[7];
354 co1[6] = coords[4];
355 co1[7] = coords[5];
356
357 float tco1[8];
358 tco1[0] = uv[0];
359 tco1[1] = uv[1];
360 tco1[2] = uv[2];
361 tco1[3] = uv[3];
362 tco1[4] = uv[6];
363 tco1[5] = uv[7];
364 tco1[6] = uv[4];
365 tco1[7] = uv[5];
366
367 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 );
368 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 );
369
370 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
371
372 #endif
373
374
375
376
377 m_dx += tgic.advance;
378
379 #endif
380 }
381
RenderString(const char * string,int x,int y)382 void TexFont::RenderString( const char *string, int x, int y )
383 {
384
385 #ifndef USE_ANDROID_GLES2
386
387 glPushMatrix();
388 glTranslatef(x, y, 0);
389
390 glPushMatrix();
391 glBindTexture( GL_TEXTURE_2D, texobj);
392
393 for( int i = 0; string[i]; i++ ) {
394 if(string[i] == '\n') {
395 glPopMatrix();
396 glTranslatef(0, tgi[(int)'A'].height, 0);
397 glPushMatrix();
398 continue;
399 }
400 /* degree symbol */
401 if((unsigned char)string[i] == 0xc2 &&
402 (unsigned char)string[i+1] == 0xb0) {
403 RenderGlyph( DEGREE_GLYPH );
404 i++;
405 continue;
406 }
407 RenderGlyph( string[i] );
408 }
409
410 glPopMatrix();
411 glPopMatrix();
412 #else
413 m_dx = x;
414 m_dy = y;
415
416 glBindTexture( GL_TEXTURE_2D, texobj);
417
418 for( int i = 0; string[i]; i++ ) {
419 if(string[i] == '\n') {
420 m_dy += tgi[(int)'A'].height;
421 continue;
422 }
423 /* degree symbol */
424 if((unsigned char)string[i] == 0xc2 &&
425 (unsigned char)string[i+1] == 0xb0) {
426 RenderGlyph( DEGREE_GLYPH );
427 i++;
428 continue;
429 }
430 RenderGlyph( string[i] );
431 }
432
433 #endif
434 }
435
RenderString(const wxString & string,int x,int y)436 void TexFont::RenderString( const wxString &string, int x, int y )
437 {
438 RenderString((const char*)string.ToUTF8(), x, y);
439 }
440
441 //#endif //#ifdef ocpnUSE_GL
442