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