- added libdrawtext
[demo_prior] / libs / drawtext / src / drawgl.c
1 /*
2 libdrawtext - a simple library for fast text rendering in OpenGL
3 Copyright (C) 2011-2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "drawtext.h"
19 #include "drawtext_impl.h"
20
21 struct quad {
22         struct dtx_vertex v[6];
23 };
24
25 struct dtx_glyphmap *cur_gmap;
26
27 #define QBUF_SZ         512
28 static struct quad *qbuf;
29 static int num_quads;
30
31 static dtx_user_draw_func user_draw_func;
32 static void *user_cls;
33
34 static int dtx_draw_init(void);
35 static void cleanup(void);
36
37 static void set_glyphmap_texture(struct dtx_glyphmap *gmap);
38 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush);
39 static void flush_user(void);
40 static void add_glyph(struct glyph *g, float x, float y);
41
42
43 #ifndef NO_OPENGL
44 #include <stdarg.h>
45 #include <math.h>
46 #include <ctype.h>
47 #include <stdlib.h>
48
49 #ifdef TARGET_IPHONE
50 #include <OpenGLES/ES2/gl.h>
51 #ifndef GL_ES
52 #define GL_ES
53 #endif
54
55 #elif defined(ANDROID)
56 #include <GLES2/gl2.h>
57 #ifndef GL_ES
58 #define GL_ES
59 #endif
60
61 #else   /* regular OpenGL */
62
63 #ifdef WIN32
64 #include <windows.h>
65 #endif
66
67 #ifdef __APPLE__
68 #include <OpenGL/gl.h>
69
70 #else
71
72 #define GL_GLEXT_LEGACY         /* don't include glext.h internally in gl.h */
73 #include <GL/gl.h>
74 #ifndef NO_GLU
75 #include <GL/glu.h>
76 #endif
77
78 #ifdef __unix__
79 #define GLX_GLXEXT_LEGACY       /* don't include glxext.h internally in glx.h */
80 #include <GL/glx.h>
81 #endif
82
83 #endif  /* !__APPLE__ */
84 #endif  /* !TARGET_IPHONE */
85
86 #ifdef GL_ES
87 #define GL_CLAMP GL_CLAMP_TO_EDGE
88 #endif
89
90 static void dtx_gl_init(void);
91 static void cleanup(void);
92 static void flush(void);
93
94 static int vattr = -1;
95 static int tattr = -1;
96 static int cattr = -1;
97 static unsigned int font_tex;
98
99 #ifndef GL_ES
100 #ifndef GL_VERSION_1_5
101 #define GL_ARRAY_BUFFER 0x8892
102 #define GL_ARRAY_BUFFER_BINDING 0x8894
103 typedef void (APIENTRY *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
104 static PFNGLBINDBUFFERPROC glBindBuffer;
105 #endif
106
107 #ifndef GL_VERSION_2_0
108 typedef void (APIENTRY *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
109 typedef void (APIENTRY *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);
110 typedef void (APIENTRY *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
111 static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
112 static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
113 static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
114 #endif
115
116 #ifdef WIN32
117 #define load_glfunc(s)  wglGetProcAddress(s)
118 #elif defined(__unix__)
119 #define load_glfunc(s)  glXGetProcAddress((unsigned char*)s)
120 #endif
121
122 #endif  /* !GL_ES */
123
124 void dtx_target_opengl(void)
125 {
126         dtx_draw_init();
127         dtx_drawchar = drawchar;
128         dtx_drawflush = flush;
129
130         user_draw_func = 0;
131 }
132
133 int dtx_gl_setopt(enum dtx_option opt, int val)
134 {
135         switch(opt) {
136         case DTX_GL_ATTR_VERTEX:
137                 vattr = val;
138                 break;
139         case DTX_GL_ATTR_TEXCOORD:
140                 tattr = val;
141                 break;
142         case DTX_GL_ATTR_COLOR:
143                 cattr = val;
144                 break;
145         default:
146                 return -1;
147         }
148         return 0;
149 }
150
151 int dtx_gl_getopt(enum dtx_option opt, int *res)
152 {
153         switch(opt) {
154         case DTX_GL_ATTR_VERTEX:
155                 *res = vattr;
156                 break;
157         case DTX_GL_ATTR_TEXCOORD:
158                 *res = tattr;
159                 break;
160         case DTX_GL_ATTR_COLOR:
161                 *res = cattr;
162                 break;
163         default:
164                 return -1;
165         }
166         return 0;
167 }
168
169 static void dtx_gl_init(void)
170 {
171 #ifndef GL_ES
172 #ifndef GL_VERSION_1_5
173         glBindBuffer = (PFNGLBINDBUFFERPROC)load_glfunc("glBindBuffer");
174 #endif
175 #ifndef GL_VERSION_2_0
176         glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load_glfunc("glEnableVertexAttribArray");
177         glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load_glfunc("glDisableVertexAttribArray");
178         glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load_glfunc("glVertexAttribPointer");
179 #endif
180 #endif  /* !GL_ES */
181 }
182
183
184 void dtx_vertex_attribs(int vert_attr, int tex_attr)
185 {
186         vattr = vert_attr;
187         tattr = tex_attr;
188 }
189
190 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap)
191 {
192         if(!gmap->tex) {
193                 glGenTextures(1, &gmap->tex);
194                 glBindTexture(GL_TEXTURE_2D, gmap->tex);
195 #if !defined(GL_ES) && defined(NO_GLU)
196                 /* TODO: ideally we want to have mipmaps even without GLU, and we should
197                  * just use SGIS_generate_mipmaps if available
198                  */
199                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
200 #else
201                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
202 #endif
203                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
204                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
205                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
206                 gmap->tex_valid = 0;
207         }
208
209         if(!gmap->tex_valid) {
210                 glBindTexture(GL_TEXTURE_2D, gmap->tex);
211 #ifdef GL_ES
212                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
213                 glGenerateMipmap(GL_TEXTURE_2D);
214 #elif !defined(NO_GLU)
215                 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
216 #else
217                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
218 #endif
219                 gmap->tex_valid = 1;
220         }
221
222         font_tex = gmap->tex;
223 }
224
225
226
227 static void flush(void)
228 {
229         int vbo;
230
231         if(!num_quads) {
232                 return;
233         }
234
235         if(glBindBuffer) {
236                 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
237                 glBindBuffer(GL_ARRAY_BUFFER, 0);
238         }
239
240 #ifndef GL_ES
241         glPushAttrib(GL_ENABLE_BIT);
242         glEnable(GL_TEXTURE_2D);
243 #endif
244         glBindTexture(GL_TEXTURE_2D, font_tex);
245
246         if(vattr != -1 && glEnableVertexAttribArray) {
247                 glEnableVertexAttribArray(vattr);
248                 glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf);
249 #ifndef GL_ES
250         } else {
251                 glEnableClientState(GL_VERTEX_ARRAY);
252                 glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf);
253 #endif
254         }
255         if(tattr != -1 && glEnableVertexAttribArray) {
256                 glEnableVertexAttribArray(tattr);
257                 glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s);
258 #ifndef GL_ES
259         } else {
260                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
261                 glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s);
262 #endif
263         }
264
265         glEnable(GL_BLEND);
266         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
267
268         glDepthMask(0);
269
270         glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
271
272         glDepthMask(1);
273
274         if(vattr != -1 && glDisableVertexAttribArray) {
275                 glDisableVertexAttribArray(vattr);
276 #ifndef GL_ES
277         } else {
278                 glDisableClientState(GL_VERTEX_ARRAY);
279 #endif
280         }
281         if(tattr != -1 && glDisableVertexAttribArray) {
282                 glDisableVertexAttribArray(tattr);
283 #ifndef GL_ES
284         } else {
285                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
286 #endif
287         }
288
289 #ifndef GL_ES
290         glPopAttrib();
291 #else
292         glDisable(GL_BLEND);
293 #endif
294
295         if(glBindBuffer && vbo) {
296                 glBindBuffer(GL_ARRAY_BUFFER, vbo);
297         }
298
299         num_quads = 0;
300 }
301 #else
302
303 /* no-opengl build, define all public gl functions as stubs */
304 void dtx_target_opengl(void) {}
305 int dtx_gl_setopt(enum dtx_option opt, int val) { return -1; }
306 int dtx_gl_getopt(enum dtx_option opt, int *val) { return -1; }
307
308 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {}
309
310 #endif  /* !def NO_OPENGL */
311
312 static int dtx_draw_init(void)
313 {
314         if(qbuf) {
315                 return 0;       /* already initialized */
316         }
317
318 #ifndef NO_OPENGL
319         dtx_gl_init();
320 #endif
321
322         if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
323                 return -1;
324         }
325         num_quads = 0;
326
327         atexit(cleanup);
328         return 0;
329 }
330
331 static void cleanup(void)
332 {
333         free(qbuf);
334 }
335
336
337 void dtx_target_user(dtx_user_draw_func func, void *cls)
338 {
339         dtx_draw_init();
340
341         user_draw_func = func;
342         user_cls = cls;
343
344         dtx_drawchar = drawchar;
345         dtx_drawflush = flush_user;
346 }
347
348 void dtx_glyph(int code)
349 {
350         struct dtx_glyphmap *gmap;
351
352         if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
353                 return;
354         }
355         set_glyphmap_texture(gmap);
356
357         add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
358         dtx_flush();
359 }
360
361 static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
362 {
363         if(!user_draw_func) {
364                 set_glyphmap_texture_gl(gmap);
365         }
366
367         if(cur_gmap && gmap != cur_gmap) {
368                 dtx_flush();
369         }
370         cur_gmap = gmap;
371 }
372
373 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush)
374 {
375         struct dtx_glyphmap *gmap;
376         float px, py;
377         int code = dtx_utf8_char_code(str);
378         str = dtx_utf8_next_char((char*)str);
379
380         if(dtx_buf_mode == DTX_LBF && code == '\n') {
381                 *should_flush = 1;
382         }
383
384         px = *pos_x;
385         py = *pos_y;
386
387         if((gmap = dtx_proc_char(code, pos_x, pos_y))) {
388                 int idx = code - gmap->cstart;
389
390                 set_glyphmap_texture(gmap);
391                 add_glyph(gmap->glyphs + idx, px, py);
392         }
393         return str;
394 }
395
396 static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t)
397 {
398         v->x = x;
399         v->y = y;
400         v->s = s;
401         v->t = t;
402 }
403
404 static void add_glyph(struct glyph *g, float x, float y)
405 {
406         struct quad *qptr = qbuf + num_quads;
407
408         x -= g->orig_x;
409         y -= g->orig_y;
410
411         qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight);
412         qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight);
413         qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
414
415         qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight);
416         qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
417         qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny);
418
419         if(++num_quads >= QBUF_SZ) {
420                 dtx_flush();
421         }
422 }
423
424 static void flush_user(void)
425 {
426         struct dtx_pixmap pixmap;
427
428         if(!num_quads || !user_draw_func || !cur_gmap) {
429                 return;
430         }
431
432         pixmap.pixels = cur_gmap->pixels;
433         pixmap.width = cur_gmap->xsz;
434         pixmap.height = cur_gmap->ysz;
435         pixmap.udata = cur_gmap->udata;
436
437         user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls);
438         cur_gmap->udata = pixmap.udata;
439
440         num_quads = 0;
441 }