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