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 // #include <GL/gl.h>
28 // #include <GL/glu.h>
29
30 #ifdef __OCPN__ANDROID__
31 #include "qdebug.h"
32 #else
33 #include <GL/gl.h>
34 #endif
35
36 #include "pi_TexFont.h"
37
38 #ifdef USE_ANDROID_GLES2
39 #include <gl2.h>
40 #include "linmath.h"
41 #include "pi_shaders.h"
42 #endif
43
TexFont()44 TexFont::TexFont( )
45 {
46 texobj = 0;
47 m_blur = false;
48 m_built = false;
49 }
50
~TexFont()51 TexFont::~TexFont( )
52 {
53 Delete( );
54 }
55
56
Build(wxFont & font,bool blur)57 void TexFont::Build( wxFont &font, bool blur )
58 {
59 // qDebug() << "pi_Texfont build";
60 /* avoid rebuilding if the parameters are the same */
61 if(font == m_font && blur == m_blur)
62 return;
63
64 m_font = font;
65 m_blur = blur;
66
67 m_maxglyphw = 0;
68 m_maxglyphh = 0;
69
70 wxScreenDC sdc;
71
72 sdc.SetFont( font );
73
74 for( int i = MIN_GLYPH; i < MAX_GLYPH; i++ ) {
75 wxCoord gw, gh;
76 wxString text;
77 if(i == DEGREE_GLYPH)
78 text = wxString::Format(_T("%c"), 0x00B0); //_T("°");
79 else
80 text = wxString::Format(_T("%c"), i);
81 wxCoord descent, exlead;
82 sdc.GetTextExtent( text, &gw, &gh, &descent, &exlead, &font ); // measure the text
83
84 tgi[i].width = gw;
85 tgi[i].height = gh;
86
87 tgi[i].advance = gw;
88
89
90 m_maxglyphw = wxMax(tgi[i].width, m_maxglyphw);
91 m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh);
92 }
93
94 /* add extra pixel to give a border between rows of characters
95 without this, in some cases a faint line can be see on the edge
96 from the character above */
97 m_maxglyphh++;
98
99 int w = COLS_GLYPHS * m_maxglyphw;
100 int h = ROWS_GLYPHS * m_maxglyphh;
101
102 wxASSERT(w < 2048 && h < 2048);
103
104 /* make power of 2 */
105 for(tex_w = 1; tex_w < w; tex_w *= 2);
106 for(tex_h = 1; tex_h < h; tex_h *= 2);
107
108 wxBitmap tbmp(tex_w, tex_h);
109 wxMemoryDC dc;
110 dc.SelectObject(tbmp);
111 dc.SetFont( font );
112
113 /* fill bitmap with black */
114 dc.SetBackground( wxBrush( wxColour( 0, 0, 0 ) ) );
115 dc.Clear();
116
117 /* draw the text white */
118 dc.SetTextForeground( wxColour( 255, 255, 255 ) );
119
120 /* wxPen pen(wxColour( 255, 255, 255 ));
121 wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT);
122 dc.SetPen(pen);
123 dc.SetBrush(brush);
124 */
125 int row = 0, col = 0;
126 for( int i = MIN_GLYPH; i < MAX_GLYPH; i++ ) {
127 if(col == COLS_GLYPHS) {
128 col = 0;
129 row++;
130 }
131
132 tgi[i].x = col * m_maxglyphw;
133 tgi[i].y = row * m_maxglyphh;
134
135 wxString text;
136 if(i == DEGREE_GLYPH)
137 text = wxString::Format(_T("%c"), 0x00B0); //_T("°");
138 else
139 text = wxString::Format(_T("%c"), i);
140
141 dc.DrawText(text, tgi[i].x, tgi[i].y );
142
143 // dc.DrawRectangle(tgi[i].x, tgi[i].y, tgi[i].advance, tgi[i].height);
144 col++;
145 }
146
147 dc.SelectObject(wxNullBitmap);
148
149 wxImage image = tbmp.ConvertToImage();
150
151 GLuint format, internalformat;
152 int stride;
153
154 format = GL_ALPHA;
155 internalformat = format;
156 stride = 1;
157
158 if( m_blur )
159 image = image.Blur(1);
160
161 unsigned char *imgdata = image.GetData();
162
163 if(imgdata){
164 unsigned char *teximage = (unsigned char *) malloc( stride * tex_w * tex_h );
165
166 for( int j = 0; j < tex_w*tex_h; j++ )
167 for( int k = 0; k < stride; k++ )
168 teximage[j * stride + k] = imgdata[3*j];
169
170 Delete();
171
172 glGenTextures( 1, &texobj );
173 glBindTexture( GL_TEXTURE_2D, texobj );
174
175 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
176 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
177 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST/*GL_LINEAR*/ );
178 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
179
180 glTexImage2D( GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0,
181 format, GL_UNSIGNED_BYTE, teximage );
182
183 free(teximage);
184 }
185
186 m_built = true;
187 }
188
Delete()189 void TexFont::Delete( )
190 {
191 if (texobj) {
192 glDeleteTextures(1, &texobj);
193 texobj = 0;
194 }
195 }
196
GetTextExtent(const char * string,int * width,int * height)197 void TexFont::GetTextExtent(const char *string, int *width, int *height)
198 {
199 int w=0, h=0;
200
201 for(int i = 0; string[i]; i++ ) {
202 unsigned char c = string[i];
203 if(c == '\n') {
204 h += tgi[(int)'A'].height;
205 continue;
206 }
207 if(c == 0xc2 && (unsigned char)string[i+1] == 0xb0) {
208 c = DEGREE_GLYPH;
209 i++;
210 }
211 if( c < MIN_GLYPH || c >= MAX_GLYPH)
212 continue;
213
214 TexGlyphInfo &tgisi = tgi[c];
215 w += tgisi.advance;
216 if(tgisi.height > h)
217 h = tgisi.height;
218 }
219 if(width) *width = w;
220 if(height) *height = h;
221 }
222
GetTextExtent(const wxString & string,int * width,int * height)223 void TexFont::GetTextExtent(const wxString &string, int *width, int *height)
224 {
225 GetTextExtent((const char*)string.ToUTF8(), width, height);
226 }
227
RenderGlyph(int c)228 void TexFont::RenderGlyph( int c )
229 {
230
231 if( c < MIN_GLYPH || c >= MAX_GLYPH)
232 return;
233
234 TexGlyphInfo &tgic = tgi[c];
235
236 int x = tgic.x, y = tgic.y;
237 float w = m_maxglyphw, h = m_maxglyphh;
238 float tx1 = (float)x / (float)tex_w;
239 float tx2 = (float)(x + w) / (float)tex_w;
240 float ty1 = (float)y / (float)tex_h;
241 float ty2 = (float)(y + h) / (float)tex_h;
242
243 #ifndef USE_ANDROID_GLES2
244
245 glBegin( GL_QUADS );
246
247 glTexCoord2f( tx1, ty1 ); glVertex2i( 0, 0 );
248 glTexCoord2f( tx2, ty1 ); glVertex2i( w, 0 );
249 glTexCoord2f( tx2, ty2 ); glVertex2i( w, h );
250 glTexCoord2f( tx1, ty2 ); glVertex2i( 0, h );
251
252 glEnd();
253 glTranslatef( tgic.advance, 0.0, 0.0 );
254 #else
255
256 float uv[8];
257 float coords[8];
258
259 //normal uv
260 uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1;
261 uv[4] = tx2; uv[5] = ty2; uv[6] = tx1; uv[7] = ty2;
262
263 // pixels
264 coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0;
265 coords[4] = w; coords[5] = h; coords[6] = 0; coords[7] = h;
266
267 //glChartCanvas::RenderSingleTexture(coords, uv, cc1->GetpVP(), m_dx, m_dy, 0);
268
269 glUseProgram( pi_texture_2D_shader_program );
270
271 // Get pointers to the attributes in the program.
272 GLint mPosAttrib = glGetAttribLocation( pi_texture_2D_shader_program, "aPos" );
273 GLint mUvAttrib = glGetAttribLocation( pi_texture_2D_shader_program, "aUV" );
274
275 // Set up the texture sampler to texture unit 0
276 GLint texUni = glGetUniformLocation( pi_texture_2D_shader_program, "uTex" );
277 glUniform1i( texUni, 0 );
278
279 // Disable VBO's (vertex buffer objects) for attributes.
280 glBindBuffer( GL_ARRAY_BUFFER, 0 );
281 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
282
283 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
284 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
285 // ... and enable it.
286 glEnableVertexAttribArray( mPosAttrib );
287
288 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
289 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
290 // ... and enable it.
291 glEnableVertexAttribArray( mUvAttrib );
292
293 // Rotate
294 float angle = 0;
295 mat4x4 I, Q;
296 mat4x4_identity(I);
297 mat4x4_rotate_Z(Q, I, angle);
298
299 // Translate
300 Q[3][0] = m_dx;
301 Q[3][1] = m_dy;
302
303 GLint matloc = glGetUniformLocation(pi_texture_2D_shader_program,"TransformMatrix");
304 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
305
306 // Select the active texture unit.
307 glActiveTexture( GL_TEXTURE0 );
308
309
310 // For some reason, glDrawElements is busted on Android
311 // So we do this a hard ugly way, drawing two triangles...
312 #if 0
313 GLushort indices1[] = {0,1,3,2};
314 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
315 #else
316
317 float co1[8];
318 co1[0] = coords[0];
319 co1[1] = coords[1];
320 co1[2] = coords[2];
321 co1[3] = coords[3];
322 co1[4] = coords[6];
323 co1[5] = coords[7];
324 co1[6] = coords[4];
325 co1[7] = coords[5];
326
327 float tco1[8];
328 tco1[0] = uv[0];
329 tco1[1] = uv[1];
330 tco1[2] = uv[2];
331 tco1[3] = uv[3];
332 tco1[4] = uv[6];
333 tco1[5] = uv[7];
334 tco1[6] = uv[4];
335 tco1[7] = uv[5];
336
337 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 );
338 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 );
339
340 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
341 #endif
342
343
344
345
346
347 m_dx += tgic.advance;
348
349 #endif
350 }
351
RenderString(const char * string,int x,int y)352 void TexFont::RenderString( const char *string, int x, int y )
353 {
354
355 #ifndef USE_ANDROID_GLES2
356
357 glPushMatrix();
358 glTranslatef(x, y, 0);
359
360 glPushMatrix();
361 glBindTexture( GL_TEXTURE_2D, texobj);
362
363 for( int i = 0; string[i]; i++ ) {
364 if(string[i] == '\n') {
365 glPopMatrix();
366 glTranslatef(0, tgi[(int)'A'].height, 0);
367 glPushMatrix();
368 continue;
369 }
370 /* degree symbol */
371 if((unsigned char)string[i] == 0xc2 &&
372 (unsigned char)string[i+1] == 0xb0) {
373 RenderGlyph( DEGREE_GLYPH );
374 i++;
375 continue;
376 }
377 RenderGlyph( string[i] );
378 }
379
380 glPopMatrix();
381 glPopMatrix();
382 #else
383 m_dx = x;
384 m_dy = y;
385
386 glBindTexture( GL_TEXTURE_2D, texobj);
387
388 for( int i = 0; string[i]; i++ ) {
389 if(string[i] == '\n') {
390 m_dy += tgi[(int)'A'].height;
391 continue;
392 }
393 /* degree symbol */
394 if((unsigned char)string[i] == 0xc2 &&
395 (unsigned char)string[i+1] == 0xb0) {
396 RenderGlyph( DEGREE_GLYPH );
397 i++;
398 continue;
399 }
400 RenderGlyph( string[i] );
401 }
402
403 #endif
404 }
405
RenderString(const wxString & string,int x,int y)406 void TexFont::RenderString( const wxString &string, int x, int y )
407 {
408 RenderString((const char*)string.ToUTF8(), x, y);
409 }
410
411