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