1/*
2libdrawtext - a simple library for fast text rendering in OpenGL
3Copyright (C) 2011-2012 John Tsiombikas <nuclear@member.fsf.org>
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU Lesser General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU Lesser General Public License for more details.
14
15You should have received a copy of the GNU Lesser General Public License
16along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18#ifndef NO_OPENGL
19#include <stdarg.h>
20#include <math.h>
21#include <ctype.h>
22#include <float.h>
23
24#include <stdlib.h>
25#include <stdio.h>
26
27#ifndef _MSC_VER
28//#include <alloca.h>
29#include <malloc.h>
30#else
31#include <malloc.h>
32#endif
33
34#include "config.h"
35
36#ifdef USE_GLES
37 #include <GLES2/gl2.h>
38 #include <GLES2/gl2ext.h>
39#else
40 #include <GL/glew.h>
41#endif
42
43#include "drawtext.h"
44#include "drawtext_impl.h"
45
46struct vertex {
47 float x, y;
48 float s, t;
49};
50
51struct quad {
52 struct vertex v[6];
53};
54
55static void add_glyph(struct glyph *g, float x, float y);
56
57#define QBUF_SZ 2048
58//512
59static struct quad *qbuf;
60static int num_quads;
61static unsigned int font_tex;
62static int buf_mode = DTX_NBF;
63static struct dtx_box prepared_box;
64
65#ifndef USE_GLES
66 static GLuint vao;
67#endif
68static GLuint vbo;
69
70int dtx_gl_init(void)
71{
72 if(!(qbuf = (struct quad*)malloc(QBUF_SZ * sizeof *qbuf)))
73 {
74 return -1;
75 }
76
77 // currently there are no characters to render
78 num_quads = 0;
79
80 // generate our default vertex array object and the one we'll use for rendering
81 #ifndef USE_GLES
82 glGenVertexArrays(1, &vao);
83 glBindVertexArray(vao);
84 #endif
85
86 // set up our buffer object and allocate enough space for the entire character buffer
87 glGenBuffers(1, &vbo);
88 glBindBuffer(GL_ARRAY_BUFFER, vbo);
89 glBufferData(GL_ARRAY_BUFFER, sizeof(struct vertex) * QBUF_SZ * 6, NULL, GL_DYNAMIC_DRAW);
90
91 // vertex position attributes
92 glEnableVertexAttribArray(0);
93 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), 0);
94
95 // vertex tex coord attributes
96 glEnableVertexAttribArray(1);
97 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (GLvoid*)(sizeof(float) * 2));
98
99 return 0;
100}
101
102void dtx_gl_kill()
103{
104 free(qbuf);
105 qbuf = 0;
106
107 glDeleteBuffers(1, &vbo);
108 #ifndef USE_GLES
109 glDeleteVertexArrays(1, &vao);
110 #endif
111}
112
113void dtx_update_texture_interpolation()
114{
115 if(dtx_interpolation)
116 {
117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
119 }
120 else
121 {
122 // for heaven's sake don't let OpenGL interpolate what should be crisp text
123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
124 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
125 }
126}
127
128static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
129{
130 if(!gmap->tex)
131 {
132 // generate and bind our texture
133 glGenTextures(1, &gmap->tex);
134 glBindTexture(GL_TEXTURE_2D, gmap->tex);
135
136 // specify any mipmapping levels
137 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
138 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
139
140 // don't tile the texture
141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
143
144 // fill our texture data
145 #ifdef USE_GLES
146 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
147 #else
148 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, gmap->xsz, gmap->ysz, 0, GL_RED, GL_UNSIGNED_BYTE, gmap->pixels);
149 #endif
150 glGenerateMipmap(GL_TEXTURE_2D);
151 }
152
153 if(font_tex != gmap->tex)
154 {
155 dtx_flush();
156 }
157
158 font_tex = gmap->tex;
159}
160
161void dtx_glyph(int code)
162{
163 struct dtx_glyphmap *gmap;
164
165 if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
166 return;
167 }
168 set_glyphmap_texture(gmap);
169
170 add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
171 dtx_flush();
172}
173
174static const char *put_char(struct dtx_glyphmap *gmap, const char *str, float *pos_x, float *pos_y, int *should_flush)
175{
176 float px, py;
177 int index;
178 int code = *str;//dtx_utf8_char_code(str);
179 str++;// = dtx_utf8_next_char((char*)str);
180
181 if(buf_mode == DTX_LBF && code == '\n') {
182 *should_flush = 1;
183 }
184
185 px = *pos_x;
186 py = *pos_y;
187
188 // process special characters
189 if(code == '\n')
190 {
191 *pos_y -= gmap->line_advance;
192 *pos_x = 0.0;
193 }
194 else if(code == '\t') *pos_x = (float)((fmod(*pos_x, 4.0) + 4.0) * gmap->glyphs[0].advance);
195 else if(code == '\r') *pos_x = 0.0f;
196 else
197 {
198 // advance the cursor normally
199 *pos_x += gmap->glyphs[code - gmap->cstart].advance;
200
201 // add the character
202 index = code - gmap->cstart;
203 set_glyphmap_texture(gmap);
204 add_glyph(gmap->glyphs + index, px, py);
205 }
206
207 return str;
208}
209
210void dtx_string(const char *str)
211{
212 int should_flush = buf_mode == DTX_NBF;
213 struct dtx_glyphmap *gmap;
214 float pos_x = 0.0;
215 float pos_y = 0.0;
216
217 if(dtx_font)
218 {
219 // get the glyphmap *first*, then render each character with it. Christ...
220 gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, *str);
221
222 while(*str)
223 {
224 str = put_char(gmap, str, &pos_x, &pos_y, &should_flush);
225 }
226 }
227
228 if(should_flush)
229 {
230 dtx_flush();
231 }
232}
233
234void dtx_printf(const char *fmt, ...)
235{
236 va_list ap;
237 int buf_size;
238 char *buf, tmp;
239
240 if(!dtx_font) {
241 return;
242 }
243
244 va_start(ap, fmt);
245 buf_size = vsnprintf(&tmp, 0, fmt, ap);
246 va_end(ap);
247
248 if(buf_size == -1) {
249 buf_size = 512;
250 }
251
252 buf = (char*)alloca(buf_size + 1);
253 va_start(ap, fmt);
254 vsnprintf(buf, buf_size + 1, fmt, ap);
255 va_end(ap);
256
257 dtx_string(buf);
258}
259
260static void prepare_char(struct dtx_glyphmap *gmap, const char *str, float *pos_x, float *pos_y)
261{
262 float px, py;
263 int index;
264 int code = *str;
265
266 px = *pos_x;
267 py = *pos_y;
268
269 // process special characters
270 if(code == '\n')
271 {
272 *pos_y -= gmap->line_advance;
273 *pos_x = 0.0;
274 }
275 else if(code == '\t') *pos_x = (float)((fmod(*pos_x, 4.0) + 4.0) * gmap->glyphs[0].advance);
276 else if(code == '\r') *pos_x = 0.0f;
277 else
278 {
279 // advance the cursor normally
280 *pos_x += gmap->glyphs[code - gmap->cstart].advance;
281
282 // add the character
283 index = code - gmap->cstart;
284 set_glyphmap_texture(gmap);
285 add_glyph(gmap->glyphs + index, px, py);
286 }
287}
288
289void dtx_prepare_string(const char *str)
290{
291 struct dtx_glyphmap *gmap;
292 struct glyph *g = 0;
293 float pos_x = 0.0f;
294 float pos_y = 0.0f;
295
296 float x0, y0, x1, y1;
297
298 x0 = y0 = FLT_MAX;
299 x1 = y1 = -FLT_MAX;
300
301 // clear whatever we were prepared to render previously
302 num_quads = 0;
303
304 // make sure we have a valid font object
305 if(dtx_font)
306 {
307 // get the glyphmap *first*, then render each character with it. Christ...
308 gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, *str);
309
310 while(*str)
311 {
312 // retrieve the glyph that we're going to prepare
313 g = gmap->glyphs + (int)(*str) - gmap->cstart;
314
315 // prepare the character by adding it to the vertex list and the text box
316 prepare_char(gmap, str, &pos_x, &pos_y);
317 str++;
318
319 // adjust the size of the rendering box
320 if(pos_x + g->orig_x < x0) x0 = pos_x + g->orig_x;
321 if(pos_y - g->orig_y < y0) y0 = pos_y - g->orig_y;
322 if(pos_x + g->orig_x + g->width > x1) x1 = pos_x + g->orig_x + g->width;
323 if(pos_y - g->orig_y + g->height > y1) y1 = pos_y - g->orig_y + g->height;
324 }
325 }
326
327 // prepare the rendering box dimensions so they can retrieved later
328 prepared_box.x = x0;
329 prepared_box.y = y0;
330 prepared_box.width = x1 - x0;
331 prepared_box.height = y1 - y0;
332
333 // don't render or flush; wait for render call
334}
335
336float dtx_get_prepared_width()
337{
338 return prepared_box.width;
339}
340
341float dtx_get_prepared_height()
342{
343 return prepared_box.height;
344}
345
346void dtx_render_prepared()
347{
348 dtx_render();
349}
350
351static void qvertex(struct vertex *v, float x, float y, float s, float t)
352{
353 v->x = x;
354 v->y = y;
355 v->s = s;
356 v->t = t;
357}
358
359static void add_glyph(struct glyph *g, float x, float y)
360{
361 struct quad *qptr = qbuf + num_quads;
362
363 x -= g->orig_x;
364 y -= g->orig_y;
365
366 qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight);
367 qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight);
368 qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
369
370 qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight);
371 qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
372 qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny);
373
374 if(++num_quads >= QBUF_SZ) {
375 dtx_flush();
376 }
377}
378
379void dtx_render()
380{
381 // don't do anything unless we have more text to render
382 if(num_quads)
383 {
384 // enable texturing and set the texture we want to use that contains our text portions
385 glBindTexture(GL_TEXTURE_2D, font_tex);
386 dtx_update_texture_interpolation();
387
388 // activate our vertex array object to bring in attribute states
389 #ifndef USE_GLES
390 glBindVertexArray(vao);
391 #endif
392
393 // fill the buffer with our new quad data
394 glBindBuffer(GL_ARRAY_BUFFER, vbo);
395 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(struct vertex) * num_quads * 6, &qbuf[0]);
396 #ifdef USE_GLES
397 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), 0);
398 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (GLvoid*)(sizeof(float) * 2));
399 #endif
400
401 // render the text itself
402 glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
403 }
404}
405
406void dtx_flush(void)
407{
408 dtx_render();
409 num_quads = 0;
410}
411
412#endif /* !def NO_OPENGL */
413