2 libdrawtext - a simple library for fast text rendering in OpenGL
3 Copyright (C) 2011-2018 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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/>.
19 #include "drawtext_impl.h"
22 struct dtx_vertex v[6];
25 struct dtx_glyphmap *cur_gmap;
28 static struct quad *qbuf;
31 static dtx_user_draw_func user_draw_func;
32 static void *user_cls;
34 static int dtx_draw_init(void);
35 static void cleanup(void);
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);
50 #include <OpenGLES/ES2/gl.h>
55 #elif defined(ANDROID)
56 #include <GLES2/gl2.h>
61 #else /* regular OpenGL */
68 #include <OpenGL/gl.h>
72 #define GL_GLEXT_LEGACY /* don't include glext.h internally in gl.h */
79 #define GLX_GLXEXT_LEGACY /* don't include glxext.h internally in glx.h */
83 #endif /* !__APPLE__ */
84 #endif /* !TARGET_IPHONE */
87 #define GL_CLAMP GL_CLAMP_TO_EDGE
90 static void dtx_gl_init(void);
91 static void cleanup(void);
92 static void flush(void);
94 static int vattr = -1;
95 static int tattr = -1;
96 static int cattr = -1;
97 static unsigned int font_tex;
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;
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;
117 #define load_glfunc(s) wglGetProcAddress(s)
118 #elif defined(__unix__)
119 #define load_glfunc(s) glXGetProcAddress((unsigned char*)s)
124 void dtx_target_opengl(void)
127 dtx_drawchar = drawchar;
128 dtx_drawflush = flush;
133 int dtx_gl_setopt(enum dtx_option opt, int val)
136 case DTX_GL_ATTR_VERTEX:
139 case DTX_GL_ATTR_TEXCOORD:
142 case DTX_GL_ATTR_COLOR:
151 int dtx_gl_getopt(enum dtx_option opt, int *res)
154 case DTX_GL_ATTR_VERTEX:
157 case DTX_GL_ATTR_TEXCOORD:
160 case DTX_GL_ATTR_COLOR:
169 static void dtx_gl_init(void)
172 #ifndef GL_VERSION_1_5
173 glBindBuffer = (PFNGLBINDBUFFERPROC)load_glfunc("glBindBuffer");
175 #ifndef GL_VERSION_2_0
176 glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load_glfunc("glEnableVertexAttribArray");
177 glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load_glfunc("glDisableVertexAttribArray");
178 glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load_glfunc("glVertexAttribPointer");
184 void dtx_vertex_attribs(int vert_attr, int tex_attr)
190 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap)
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
199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
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);
209 if(!gmap->tex_valid) {
210 glBindTexture(GL_TEXTURE_2D, gmap->tex);
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);
217 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
222 font_tex = gmap->tex;
227 static void flush(void)
236 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
237 glBindBuffer(GL_ARRAY_BUFFER, 0);
241 glPushAttrib(GL_ENABLE_BIT);
242 glEnable(GL_TEXTURE_2D);
244 glBindTexture(GL_TEXTURE_2D, font_tex);
246 if(vattr != -1 && glEnableVertexAttribArray) {
247 glEnableVertexAttribArray(vattr);
248 glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf);
251 glEnableClientState(GL_VERTEX_ARRAY);
252 glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf);
255 if(tattr != -1 && glEnableVertexAttribArray) {
256 glEnableVertexAttribArray(tattr);
257 glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s);
260 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
261 glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s);
266 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
270 glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
274 if(vattr != -1 && glDisableVertexAttribArray) {
275 glDisableVertexAttribArray(vattr);
278 glDisableClientState(GL_VERTEX_ARRAY);
281 if(tattr != -1 && glDisableVertexAttribArray) {
282 glDisableVertexAttribArray(tattr);
285 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
295 if(glBindBuffer && vbo) {
296 glBindBuffer(GL_ARRAY_BUFFER, vbo);
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; }
308 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {}
310 #endif /* !def NO_OPENGL */
312 static int dtx_draw_init(void)
315 return 0; /* already initialized */
322 if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
331 static void cleanup(void)
337 void dtx_target_user(dtx_user_draw_func func, void *cls)
341 user_draw_func = func;
344 dtx_drawchar = drawchar;
345 dtx_drawflush = flush_user;
348 void dtx_glyph(int code)
350 struct dtx_glyphmap *gmap;
352 if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
355 set_glyphmap_texture(gmap);
357 add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
361 static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
363 if(!user_draw_func) {
364 set_glyphmap_texture_gl(gmap);
367 if(cur_gmap && gmap != cur_gmap) {
373 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush)
375 struct dtx_glyphmap *gmap;
377 int code = dtx_utf8_char_code(str);
378 str = dtx_utf8_next_char((char*)str);
380 if(dtx_buf_mode == DTX_LBF && code == '\n') {
387 if((gmap = dtx_proc_char(code, pos_x, pos_y))) {
388 int idx = code - gmap->cstart;
390 set_glyphmap_texture(gmap);
391 add_glyph(gmap->glyphs + idx, px, py);
396 static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t)
404 static void add_glyph(struct glyph *g, float x, float y)
406 struct quad *qptr = qbuf + num_quads;
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);
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);
419 if(++num_quads >= QBUF_SZ) {
424 static void flush_user(void)
426 struct dtx_pixmap pixmap;
428 if(!num_quads || !user_draw_func || !cur_gmap) {
432 pixmap.pixels = cur_gmap->pixels;
433 pixmap.width = cur_gmap->xsz;
434 pixmap.height = cur_gmap->ysz;
435 pixmap.udata = cur_gmap->udata;
437 user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls);
438 cur_gmap->udata = pixmap.udata;