OpenCPN Partial API docs
Loading...
Searching...
No Matches
pi_tex_font.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2014 Sean D'Epagnier *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <wx/wx.h>
25
26#ifdef __ANDROID__
27#include "qdebug.h"
28#endif
29
30#include "pi_tex_font.h"
31#include "pi_ocpndc.h"
32
33#ifdef USE_ANDROID_GLES2
34#include <GLES2/gl2.h>
35#include "linmath.h"
36#include "pi_shaders.h"
37#endif
38
39TexFont::TexFont() {
40 texobj = 0;
41 m_blur = false;
42 m_built = false;
43}
44
45TexFont::~TexFont() { Delete(); }
46
47void TexFont::Build(wxFont &font, bool blur) {
48 // qDebug() << "pi_Texfont build";
49 /* avoid rebuilding if the parameters are the same */
50 if (font == m_font && blur == m_blur) return;
51
52 m_font = font;
53 m_blur = blur;
54
55 m_maxglyphw = 0;
56 m_maxglyphh = 0;
57
58 wxScreenDC sdc;
59
60 sdc.SetFont(font);
61
62 for (int i = MIN_GLYPH; i < MAX_GLYPH; i++) {
63 wxCoord gw, gh;
64 wxString text;
65 if (i == DEGREE_GLYPH)
66 text = wxString::Format("%c", 0x00B0); //"°";
67 else
68 text = wxString::Format("%c", i);
69 wxCoord descent, exlead;
70 sdc.GetTextExtent(text, &gw, &gh, &descent, &exlead,
71 &font); // measure the text
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("%c", 0x00B0); //"°";
127 else
128 text = wxString::Format("%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 auto *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 /*we need to handle multiline*/
188 int w = 0, wo = 0, h = 0;
189
190 for (int i = 0; string[i]; i++) {
191 unsigned char c = string[i];
192 if (c == '\n') {
193 h += tgi[(int)'A'].height;
194 wo = wxMax(w, wo);
195 w = 0;
196 continue;
197 }
198 if (c == 0xc2 && (unsigned char)string[i + 1] == 0xb0) {
199 c = DEGREE_GLYPH;
200 i++;
201 }
202 if (c < MIN_GLYPH || c >= MAX_GLYPH) continue;
203
204 TexGlyphInfo &tgisi = tgi[c];
205 w += tgisi.advance;
206 if (tgisi.height > h) h = tgisi.height;
207 }
208 if (width) *width = wxMax(w, wo);
209 if (height) *height = h;
210}
211
212void TexFont::GetTextExtent(const wxString &string, int *width, int *height) {
213 GetTextExtent((const char *)string.ToUTF8(), width, height);
214}
215
216void TexFont::RenderGlyph(int c) {
217 if (c < MIN_GLYPH || c >= MAX_GLYPH) return;
218
219 TexGlyphInfo &tgic = tgi[c];
220
221 int x = tgic.x, y = tgic.y;
222 float w = m_maxglyphw, h = m_maxglyphh;
223 float tx1 = (float)x / (float)tex_w;
224 float tx2 = (float)(x + w) / (float)tex_w;
225 float ty1 = (float)y / (float)tex_h;
226 float ty2 = (float)(y + h) / (float)tex_h;
227
228#ifndef USE_ANDROID_GLES2
229
230 glBegin(GL_QUADS);
231
232 glTexCoord2f(tx1, ty1);
233 glVertex2i(0, 0);
234 glTexCoord2f(tx2, ty1);
235 glVertex2i(w, 0);
236 glTexCoord2f(tx2, ty2);
237 glVertex2i(w, h);
238 glTexCoord2f(tx1, ty2);
239 glVertex2i(0, h);
240
241 glEnd();
242 glTranslatef(tgic.advance, 0.0, 0.0);
243#else
244
245 float uv[8];
246 float coords[8];
247
248 // normal uv
249 uv[0] = tx1;
250 uv[1] = ty1;
251 uv[2] = tx2;
252 uv[3] = ty1;
253 uv[4] = tx2;
254 uv[5] = ty2;
255 uv[6] = tx1;
256 uv[7] = ty2;
257
258 // pixels
259 coords[0] = 0;
260 coords[1] = 0;
261 coords[2] = w;
262 coords[3] = 0;
263 coords[4] = w;
264 coords[5] = h;
265 coords[6] = 0;
266 coords[7] = h;
267
268 // glChartCanvas::RenderSingleTexture(coords, uv, cc1->GetpVP(), m_dx, m_dy,
269 // 0);
270
271 glUseProgram(pi_texture_2D_shader_program);
272
273 // Get pointers to the attributes in the program.
274 GLint mPosAttrib = glGetAttribLocation(pi_texture_2D_shader_program, "aPos");
275 GLint mUvAttrib = glGetAttribLocation(pi_texture_2D_shader_program, "aUV");
276
277 // Set up the texture sampler to texture unit 0
278 GLint texUni = glGetUniformLocation(pi_texture_2D_shader_program, "uTex");
279 glUniform1i(texUni, 0);
280
281 // Disable VBO's (vertex buffer objects) for attributes.
282 glBindBuffer(GL_ARRAY_BUFFER, 0);
283 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
284
285 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
286 glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
287 // ... and enable it.
288 glEnableVertexAttribArray(mPosAttrib);
289
290 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
291 glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv);
292 // ... and enable it.
293 glEnableVertexAttribArray(mUvAttrib);
294
295 // Rotate
296 float angle = 0;
297 mat4x4 I, Q;
298 mat4x4_identity(I);
299 mat4x4_rotate_Z(Q, I, angle);
300
301 // Translate
302 Q[3][0] = m_dx;
303 Q[3][1] = m_dy;
304
305 GLint matloc =
306 glGetUniformLocation(pi_texture_2D_shader_program, "TransformMatrix");
307 glUniformMatrix4fv(matloc, 1, GL_FALSE, (const GLfloat *)Q);
308
309 // Select the active texture unit.
310 glActiveTexture(GL_TEXTURE0);
311
312// For some reason, glDrawElements is busted on Android
313// So we do this a hard ugly way, drawing two triangles...
314#if 0
315 GLushort indices1[] = {0,1,3,2};
316 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
317#else
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#endif
344
345 m_dx += tgic.advance;
346
347#endif
348}
349
350void TexFont::RenderString(const char *string, int x, int y) {
351#ifndef USE_ANDROID_GLES2
352
353 glPushMatrix();
354 glTranslatef(x, y, 0);
355
356 glPushMatrix();
357 glBindTexture(GL_TEXTURE_2D, texobj);
358
359 for (int i = 0; string[i]; i++) {
360 if (string[i] == '\n') {
361 glPopMatrix();
362 glTranslatef(0, tgi[(int)'A'].height, 0);
363 glPushMatrix();
364 continue;
365 }
366 /* degree symbol */
367 if ((unsigned char)string[i] == 0xc2 &&
368 (unsigned char)string[i + 1] == 0xb0) {
369 RenderGlyph(DEGREE_GLYPH);
370 i++;
371 continue;
372 }
373 RenderGlyph(string[i]);
374 }
375
376 glPopMatrix();
377 glPopMatrix();
378#else
379 m_dx = x;
380 m_dy = y;
381
382 glBindTexture(GL_TEXTURE_2D, texobj);
383
384 for (int i = 0; string[i]; i++) {
385 if (string[i] == '\n') {
386 m_dy += tgi[(int)'A'].height;
387 continue;
388 }
389 /* degree symbol */
390 if ((unsigned char)string[i] == 0xc2 &&
391 (unsigned char)string[i + 1] == 0xb0) {
392 RenderGlyph(DEGREE_GLYPH);
393 i++;
394 continue;
395 }
396 RenderGlyph(string[i]);
397 }
398
399#endif
400}
401
402void TexFont::RenderString(const wxString &string, int x, int y) {
403 RenderString((const char *)string.ToUTF8(), x, y);
404}
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
Graphics abstraction layer on top of wxDC or OpenGL.
OpenGL text rendering.