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