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