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