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