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 */
78 #if defined(__unix__) || defined(unix)
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;
99 static int use_blend = 1;
100 static int use_atest;
101 static float atest_thres;
108 #ifndef GL_VERSION_1_5
109 #define GL_ARRAY_BUFFER 0x8892
110 #define GL_ARRAY_BUFFER_BINDING 0x8894
111 typedef void (APIENTRY *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
112 static PFNGLBINDBUFFERPROC glBindBuffer;
115 #ifndef GL_VERSION_2_0
116 typedef void (APIENTRY *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
117 typedef void (APIENTRY *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);
118 typedef void (APIENTRY *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
119 static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
120 static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
121 static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
125 #define load_glfunc(s) wglGetProcAddress(s)
126 #elif defined(__unix__) || defined(unix)
127 #define load_glfunc(s) glXGetProcAddress((unsigned char*)s)
132 void dtx_target_opengl(void)
135 dtx_drawchar = drawchar;
136 dtx_drawflush = flush;
141 int dtx_gl_setopt(enum dtx_option opt, int val)
144 case DTX_GL_ATTR_VERTEX:
147 case DTX_GL_ATTR_TEXCOORD:
150 case DTX_GL_ATTR_COLOR:
156 case DTX_GL_ALPHATEST:
159 atest_thres = val / 255.0f;
171 int dtx_gl_getopt(enum dtx_option opt, int *res)
174 case DTX_GL_ATTR_VERTEX:
177 case DTX_GL_ATTR_TEXCOORD:
180 case DTX_GL_ATTR_COLOR:
186 case DTX_GL_ALPHATEST:
187 *res = use_atest ? (int)(atest_thres * 255.0f) : 0;
195 static void dtx_gl_init(void)
198 #ifndef GL_VERSION_1_5
199 glBindBuffer = (PFNGLBINDBUFFERPROC)load_glfunc("glBindBuffer");
201 #ifndef GL_VERSION_2_0
202 glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load_glfunc("glEnableVertexAttribArray");
203 glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load_glfunc("glDisableVertexAttribArray");
204 glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load_glfunc("glVertexAttribPointer");
210 void dtx_vertex_attribs(int vert_attr, int tex_attr)
216 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap)
219 glGenTextures(1, &gmap->tex);
220 glBindTexture(GL_TEXTURE_2D, gmap->tex);
221 #if !defined(GL_ES) && defined(NO_GLU)
222 /* TODO: ideally we want to have mipmaps even without GLU, and we should
223 * just use SGIS_generate_mipmaps if available
225 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
227 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
229 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
230 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
231 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
235 if(!gmap->tex_valid) {
236 glBindTexture(GL_TEXTURE_2D, gmap->tex);
238 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
239 glGenerateMipmap(GL_TEXTURE_2D);
240 #elif !defined(NO_GLU)
241 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
243 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
248 if(font_tex != gmap->tex) {
251 font_tex = gmap->tex;
254 void dtxhack_replace_texture(struct dtx_glyphmap *gmap, unsigned int tex)
261 static void flush(void)
270 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
271 glBindBuffer(GL_ARRAY_BUFFER, 0);
275 glPushAttrib(GL_ENABLE_BIT);
276 glEnable(GL_TEXTURE_2D);
278 glBindTexture(GL_TEXTURE_2D, font_tex);
280 if(vattr != -1 && glEnableVertexAttribArray) {
281 glEnableVertexAttribArray(vattr);
282 glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf);
285 glEnableClientState(GL_VERTEX_ARRAY);
286 glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf);
289 if(tattr != -1 && glEnableVertexAttribArray) {
290 glEnableVertexAttribArray(tattr);
291 glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s);
294 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
295 glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s);
301 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
306 glEnable(GL_ALPHA_TEST);
307 glAlphaFunc(GL_GREATER, atest_thres);
309 glDisable(GL_ALPHA_TEST);
314 glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
318 if(vattr != -1 && glDisableVertexAttribArray) {
319 glDisableVertexAttribArray(vattr);
322 glDisableClientState(GL_VERTEX_ARRAY);
325 if(tattr != -1 && glDisableVertexAttribArray) {
326 glDisableVertexAttribArray(tattr);
329 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
340 glDisable(GL_ALPHA_TEST);
344 if(glBindBuffer && vbo) {
345 glBindBuffer(GL_ARRAY_BUFFER, vbo);
352 /* no-opengl build, define all public gl functions as stubs */
353 void dtx_target_opengl(void) {}
354 int dtx_gl_setopt(enum dtx_option opt, int val) { return -1; }
355 int dtx_gl_getopt(enum dtx_option opt, int *val) { return -1; }
357 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {}
359 #endif /* !def NO_OPENGL */
361 static int dtx_draw_init(void)
364 return 0; /* already initialized */
371 if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
380 static void cleanup(void)
386 void dtx_target_user(dtx_user_draw_func func, void *cls)
390 user_draw_func = func;
393 dtx_drawchar = drawchar;
394 dtx_drawflush = flush_user;
397 void dtx_glyph(int code)
399 struct dtx_glyphmap *gmap;
401 if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
404 set_glyphmap_texture(gmap);
406 add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
410 static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
412 if(!user_draw_func) {
413 set_glyphmap_texture_gl(gmap);
416 if(cur_gmap && gmap != cur_gmap) {
422 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush)
424 struct dtx_glyphmap *gmap;
426 int code = dtx_utf8_char_code(str);
427 str = dtx_utf8_next_char((char*)str);
429 if(dtx_buf_mode == DTX_LBF && code == '\n') {
436 if((gmap = dtx_proc_char(code, pos_x, pos_y))) {
437 int idx = code - gmap->cstart;
439 set_glyphmap_texture(gmap);
440 add_glyph(gmap->glyphs + idx, px, py);
445 static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t)
453 static void add_glyph(struct glyph *g, float x, float y)
455 struct quad *qptr = qbuf + num_quads;
460 qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight);
461 qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight);
462 qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
464 qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight);
465 qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
466 qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny);
468 if(++num_quads >= QBUF_SZ) {
473 static void flush_user(void)
475 struct dtx_pixmap pixmap;
477 if(!num_quads || !user_draw_func || !cur_gmap) {
481 pixmap.pixels = cur_gmap->pixels;
482 pixmap.width = cur_gmap->xsz;
483 pixmap.height = cur_gmap->ysz;
484 pixmap.udata = cur_gmap->udata;
486 user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls);
487 cur_gmap->udata = pixmap.udata;