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 if(font_tex != gmap->tex) {
225 font_tex = gmap->tex;
230 static void flush(void)
239 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
240 glBindBuffer(GL_ARRAY_BUFFER, 0);
244 glPushAttrib(GL_ENABLE_BIT);
245 glEnable(GL_TEXTURE_2D);
247 glBindTexture(GL_TEXTURE_2D, font_tex);
249 if(vattr != -1 && glEnableVertexAttribArray) {
250 glEnableVertexAttribArray(vattr);
251 glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf);
254 glEnableClientState(GL_VERTEX_ARRAY);
255 glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf);
258 if(tattr != -1 && glEnableVertexAttribArray) {
259 glEnableVertexAttribArray(tattr);
260 glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s);
263 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
264 glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s);
269 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
273 glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
277 if(vattr != -1 && glDisableVertexAttribArray) {
278 glDisableVertexAttribArray(vattr);
281 glDisableClientState(GL_VERTEX_ARRAY);
284 if(tattr != -1 && glDisableVertexAttribArray) {
285 glDisableVertexAttribArray(tattr);
288 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
298 if(glBindBuffer && vbo) {
299 glBindBuffer(GL_ARRAY_BUFFER, vbo);
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; }
311 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {}
313 #endif /* !def NO_OPENGL */
315 static int dtx_draw_init(void)
318 return 0; /* already initialized */
325 if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
334 static void cleanup(void)
340 void dtx_target_user(dtx_user_draw_func func, void *cls)
344 user_draw_func = func;
347 dtx_drawchar = drawchar;
348 dtx_drawflush = flush_user;
351 void dtx_glyph(int code)
353 struct dtx_glyphmap *gmap;
355 if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
358 set_glyphmap_texture(gmap);
360 add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
364 static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
366 if(!user_draw_func) {
367 set_glyphmap_texture_gl(gmap);
370 if(cur_gmap && gmap != cur_gmap) {
376 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush)
378 struct dtx_glyphmap *gmap;
380 int code = dtx_utf8_char_code(str);
381 str = dtx_utf8_next_char((char*)str);
383 if(dtx_buf_mode == DTX_LBF && code == '\n') {
390 if((gmap = dtx_proc_char(code, pos_x, pos_y))) {
391 int idx = code - gmap->cstart;
393 set_glyphmap_texture(gmap);
394 add_glyph(gmap->glyphs + idx, px, py);
399 static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t)
407 static void add_glyph(struct glyph *g, float x, float y)
409 struct quad *qptr = qbuf + num_quads;
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);
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);
422 if(++num_quads >= QBUF_SZ) {
427 static void flush_user(void)
429 struct dtx_pixmap pixmap;
431 if(!num_quads || !user_draw_func || !cur_gmap) {
435 pixmap.pixels = cur_gmap->pixels;
436 pixmap.width = cur_gmap->xsz;
437 pixmap.height = cur_gmap->ysz;
438 pixmap.udata = cur_gmap->udata;
440 user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls);
441 cur_gmap->udata = pixmap.udata;