added a bunch of libraries
[vrlugburz] / libs / drawtext / 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         if(font_tex != gmap->tex) {
223                 dtx_flush();
224         }
225         font_tex = gmap->tex;
226 }
227
228
229
230 static void flush(void)
231 {
232         int vbo;
233
234         if(!num_quads) {
235                 return;
236         }
237
238         if(glBindBuffer) {
239                 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
240                 glBindBuffer(GL_ARRAY_BUFFER, 0);
241         }
242
243 #ifndef GL_ES
244         glPushAttrib(GL_ENABLE_BIT);
245         glEnable(GL_TEXTURE_2D);
246 #endif
247         glBindTexture(GL_TEXTURE_2D, font_tex);
248
249         if(vattr != -1 && glEnableVertexAttribArray) {
250                 glEnableVertexAttribArray(vattr);
251                 glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf);
252 #ifndef GL_ES
253         } else {
254                 glEnableClientState(GL_VERTEX_ARRAY);
255                 glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf);
256 #endif
257         }
258         if(tattr != -1 && glEnableVertexAttribArray) {
259                 glEnableVertexAttribArray(tattr);
260                 glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s);
261 #ifndef GL_ES
262         } else {
263                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
264                 glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s);
265 #endif
266         }
267
268         glEnable(GL_BLEND);
269         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
270
271         glDepthMask(0);
272
273         glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
274
275         glDepthMask(1);
276
277         if(vattr != -1 && glDisableVertexAttribArray) {
278                 glDisableVertexAttribArray(vattr);
279 #ifndef GL_ES
280         } else {
281                 glDisableClientState(GL_VERTEX_ARRAY);
282 #endif
283         }
284         if(tattr != -1 && glDisableVertexAttribArray) {
285                 glDisableVertexAttribArray(tattr);
286 #ifndef GL_ES
287         } else {
288                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
289 #endif
290         }
291
292 #ifndef GL_ES
293         glPopAttrib();
294 #else
295         glDisable(GL_BLEND);
296 #endif
297
298         if(glBindBuffer && vbo) {
299                 glBindBuffer(GL_ARRAY_BUFFER, vbo);
300         }
301
302         num_quads = 0;
303 }
304 #else
305
306 /* no-opengl build, define all public gl functions as stubs */
307 void dtx_target_opengl(void) {}
308 int dtx_gl_setopt(enum dtx_option opt, int val) { return -1; }
309 int dtx_gl_getopt(enum dtx_option opt, int *val) { return -1; }
310
311 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {}
312
313 #endif  /* !def NO_OPENGL */
314
315 static int dtx_draw_init(void)
316 {
317         if(qbuf) {
318                 return 0;       /* already initialized */
319         }
320
321 #ifndef NO_OPENGL
322         dtx_gl_init();
323 #endif
324
325         if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
326                 return -1;
327         }
328         num_quads = 0;
329
330         atexit(cleanup);
331         return 0;
332 }
333
334 static void cleanup(void)
335 {
336         free(qbuf);
337 }
338
339
340 void dtx_target_user(dtx_user_draw_func func, void *cls)
341 {
342         dtx_draw_init();
343
344         user_draw_func = func;
345         user_cls = cls;
346
347         dtx_drawchar = drawchar;
348         dtx_drawflush = flush_user;
349 }
350
351 void dtx_glyph(int code)
352 {
353         struct dtx_glyphmap *gmap;
354
355         if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
356                 return;
357         }
358         set_glyphmap_texture(gmap);
359
360         add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
361         dtx_flush();
362 }
363
364 static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
365 {
366         if(!user_draw_func) {
367                 set_glyphmap_texture_gl(gmap);
368         }
369
370         if(cur_gmap && gmap != cur_gmap) {
371                 dtx_flush();
372         }
373         cur_gmap = gmap;
374 }
375
376 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush)
377 {
378         struct dtx_glyphmap *gmap;
379         float px, py;
380         int code = dtx_utf8_char_code(str);
381         str = dtx_utf8_next_char((char*)str);
382
383         if(dtx_buf_mode == DTX_LBF && code == '\n') {
384                 *should_flush = 1;
385         }
386
387         px = *pos_x;
388         py = *pos_y;
389
390         if((gmap = dtx_proc_char(code, pos_x, pos_y))) {
391                 int idx = code - gmap->cstart;
392
393                 set_glyphmap_texture(gmap);
394                 add_glyph(gmap->glyphs + idx, px, py);
395         }
396         return str;
397 }
398
399 static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t)
400 {
401         v->x = x;
402         v->y = y;
403         v->s = s;
404         v->t = t;
405 }
406
407 static void add_glyph(struct glyph *g, float x, float y)
408 {
409         struct quad *qptr = qbuf + num_quads;
410
411         x -= g->orig_x;
412         y -= g->orig_y;
413
414         qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight);
415         qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight);
416         qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
417
418         qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight);
419         qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
420         qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny);
421
422         if(++num_quads >= QBUF_SZ) {
423                 dtx_flush();
424         }
425 }
426
427 static void flush_user(void)
428 {
429         struct dtx_pixmap pixmap;
430
431         if(!num_quads || !user_draw_func || !cur_gmap) {
432                 return;
433         }
434
435         pixmap.pixels = cur_gmap->pixels;
436         pixmap.width = cur_gmap->xsz;
437         pixmap.height = cur_gmap->ysz;
438         pixmap.udata = cur_gmap->udata;
439
440         user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls);
441         cur_gmap->udata = pixmap.udata;
442
443         num_quads = 0;
444 }