nuclear@7: /* nuclear@7: libdrawtext - a simple library for fast text rendering in OpenGL nuclear@7: Copyright (C) 2011-2012 John Tsiombikas nuclear@7: nuclear@7: This program is free software: you can redistribute it and/or modify nuclear@7: it under the terms of the GNU Lesser General Public License as published by nuclear@7: the Free Software Foundation, either version 3 of the License, or nuclear@7: (at your option) any later version. nuclear@7: nuclear@7: This program is distributed in the hope that it will be useful, nuclear@7: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@7: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@7: GNU Lesser General Public License for more details. nuclear@7: nuclear@7: You should have received a copy of the GNU Lesser General Public License nuclear@7: along with this program. If not, see . nuclear@7: */ nuclear@7: #ifdef HAVE_CONFIG_H nuclear@7: #include "config.h" nuclear@7: #endif nuclear@7: nuclear@7: #ifndef NO_OPENGL nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: nuclear@7: #include nuclear@7: nuclear@7: #ifndef _MSC_VER nuclear@7: #include nuclear@7: #else nuclear@7: #include nuclear@7: #endif nuclear@7: nuclear@7: #ifdef TARGET_IPHONE nuclear@7: #include nuclear@7: #define GL_ES nuclear@7: #elif defined(ANDROID) nuclear@7: #include nuclear@7: #define GL_ES nuclear@7: #else nuclear@7: #include nuclear@7: #endif nuclear@7: nuclear@7: #include "drawtext.h" nuclear@7: #include "drawtext_impl.h" nuclear@7: nuclear@7: struct vertex { nuclear@7: float x, y; nuclear@7: float s, t; nuclear@7: }; nuclear@7: nuclear@7: struct quad { nuclear@7: struct vertex v[6]; nuclear@7: }; nuclear@7: nuclear@7: static void cleanup(void); nuclear@7: static void add_glyph(struct glyph *g, float x, float y); nuclear@7: nuclear@7: #define QBUF_SZ 512 nuclear@7: static struct quad *qbuf; nuclear@7: static int num_quads; nuclear@7: static int vattr = -1; nuclear@7: static int tattr = -1; nuclear@7: static unsigned int font_tex; nuclear@7: static int buf_mode = DTX_NBF; nuclear@7: nuclear@7: nuclear@7: int dtx_gl_init(void) nuclear@7: { nuclear@7: if(qbuf) { nuclear@7: return 0; /* already initialized */ nuclear@7: } nuclear@7: nuclear@7: #ifdef __glew_h__ nuclear@7: glewInit(); nuclear@7: #endif nuclear@7: nuclear@7: if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) { nuclear@7: return -1; nuclear@7: } nuclear@7: num_quads = 0; nuclear@7: nuclear@7: atexit(cleanup); nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: static void cleanup(void) nuclear@7: { nuclear@7: free(qbuf); nuclear@7: } nuclear@7: nuclear@7: nuclear@7: void dtx_vertex_attribs(int vert_attr, int tex_attr) nuclear@7: { nuclear@7: vattr = vert_attr; nuclear@7: tattr = tex_attr; nuclear@7: } nuclear@7: nuclear@7: static void set_glyphmap_texture(struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: if(!gmap->tex) { nuclear@7: glGenTextures(1, &gmap->tex); nuclear@7: glBindTexture(GL_TEXTURE_2D, gmap->tex); nuclear@7: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); nuclear@7: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@7: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); nuclear@7: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); nuclear@7: nuclear@7: #ifdef GL_ES nuclear@7: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels); nuclear@7: glGenerateMipmap(GL_TEXTURE_2D); nuclear@7: #else nuclear@7: gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels); nuclear@7: #endif nuclear@7: } nuclear@7: nuclear@7: if(font_tex != gmap->tex) { nuclear@7: dtx_flush(); nuclear@7: } nuclear@7: font_tex = gmap->tex; nuclear@7: } nuclear@7: nuclear@7: void dtx_glyph(int code) nuclear@7: { nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: nuclear@7: if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) { nuclear@7: return; nuclear@7: } nuclear@7: set_glyphmap_texture(gmap); nuclear@7: nuclear@7: add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0); nuclear@7: dtx_flush(); nuclear@7: } nuclear@7: nuclear@7: static const char *put_char(const char *str, float *pos_x, float *pos_y, int *should_flush) nuclear@7: { nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: float px, py; nuclear@7: int code = dtx_utf8_char_code(str); nuclear@7: str = dtx_utf8_next_char((char*)str); nuclear@7: nuclear@7: if(buf_mode == DTX_LBF && code == '\n') { nuclear@7: *should_flush = 1; nuclear@7: } nuclear@7: nuclear@7: px = *pos_x; nuclear@7: py = *pos_y; nuclear@7: nuclear@7: if((gmap = dtx_proc_char(code, pos_x, pos_y))) { nuclear@7: int idx = code - gmap->cstart; nuclear@7: nuclear@7: set_glyphmap_texture(gmap); nuclear@7: add_glyph(gmap->glyphs + idx, px, py); nuclear@7: } nuclear@7: return str; nuclear@7: } nuclear@7: nuclear@7: void dtx_string(const char *str) nuclear@7: { nuclear@7: int should_flush = buf_mode == DTX_NBF; nuclear@7: float pos_x = 0.0f; nuclear@7: float pos_y = 0.0f; nuclear@7: nuclear@7: if(!dtx_font) { nuclear@7: return; nuclear@7: } nuclear@7: nuclear@7: while(*str) { nuclear@7: str = put_char(str, &pos_x, &pos_y, &should_flush); nuclear@7: } nuclear@7: nuclear@7: if(should_flush) { nuclear@7: dtx_flush(); nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: void dtx_printf(const char *fmt, ...) nuclear@7: { nuclear@7: va_list ap; nuclear@7: int buf_size; nuclear@7: char *buf, tmp; nuclear@7: nuclear@7: if(!dtx_font) { nuclear@7: return; nuclear@7: } nuclear@7: nuclear@7: va_start(ap, fmt); nuclear@7: buf_size = vsnprintf(&tmp, 0, fmt, ap); nuclear@7: va_end(ap); nuclear@7: nuclear@7: if(buf_size == -1) { nuclear@7: buf_size = 512; nuclear@7: } nuclear@7: nuclear@7: buf = alloca(buf_size + 1); nuclear@7: va_start(ap, fmt); nuclear@7: vsnprintf(buf, buf_size + 1, fmt, ap); nuclear@7: va_end(ap); nuclear@7: nuclear@7: dtx_string(buf); nuclear@7: } nuclear@7: nuclear@7: static void qvertex(struct vertex *v, float x, float y, float s, float t) nuclear@7: { nuclear@7: v->x = x; nuclear@7: v->y = y; nuclear@7: v->s = s; nuclear@7: v->t = t; nuclear@7: } nuclear@7: nuclear@7: static void add_glyph(struct glyph *g, float x, float y) nuclear@7: { nuclear@7: struct quad *qptr = qbuf + num_quads; nuclear@7: nuclear@7: x -= g->orig_x; nuclear@7: y -= g->orig_y; nuclear@7: nuclear@7: qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight); nuclear@7: qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight); nuclear@7: qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny); nuclear@7: nuclear@7: qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight); nuclear@7: qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny); nuclear@7: qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny); nuclear@7: nuclear@7: if(++num_quads >= QBUF_SZ) { nuclear@7: dtx_flush(); nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: void dtx_flush(void) nuclear@7: { nuclear@7: int vbo; nuclear@7: nuclear@7: if(!num_quads) { nuclear@7: return; nuclear@7: } nuclear@7: nuclear@7: glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo); nuclear@7: glBindBuffer(GL_ARRAY_BUFFER, 0); nuclear@7: nuclear@7: #ifndef GL_ES nuclear@7: glPushAttrib(GL_ENABLE_BIT); nuclear@7: glEnable(GL_TEXTURE_2D); nuclear@7: #endif nuclear@7: glBindTexture(GL_TEXTURE_2D, font_tex); nuclear@7: nuclear@7: if(vattr != -1) { nuclear@7: glEnableVertexAttribArray(vattr); nuclear@7: glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct vertex), qbuf); nuclear@7: #ifndef GL_ES nuclear@7: } else { nuclear@7: glEnableClientState(GL_VERTEX_ARRAY); nuclear@7: glVertexPointer(2, GL_FLOAT, sizeof(struct vertex), qbuf); nuclear@7: #endif nuclear@7: } nuclear@7: if(tattr != -1) { nuclear@7: glEnableVertexAttribArray(tattr); nuclear@7: glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct vertex), &qbuf->v[0].s); nuclear@7: #ifndef GL_ES nuclear@7: } else { nuclear@7: glEnableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@7: glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex), &qbuf->v[0].s); nuclear@7: #endif nuclear@7: } nuclear@7: nuclear@7: glEnable(GL_BLEND); nuclear@7: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); nuclear@7: nuclear@7: glDepthMask(0); nuclear@7: nuclear@7: glDrawArrays(GL_TRIANGLES, 0, num_quads * 6); nuclear@7: nuclear@7: glDepthMask(1); nuclear@7: nuclear@7: if(vattr != -1) { nuclear@7: glDisableVertexAttribArray(vattr); nuclear@7: #ifndef GL_ES nuclear@7: } else { nuclear@7: glDisableClientState(GL_VERTEX_ARRAY); nuclear@7: #endif nuclear@7: } nuclear@7: if(tattr != -1) { nuclear@7: glDisableVertexAttribArray(tattr); nuclear@7: #ifndef GL_ES nuclear@7: } else { nuclear@7: glDisableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@7: #endif nuclear@7: } nuclear@7: nuclear@7: #ifndef GL_ES nuclear@7: glPopAttrib(); nuclear@7: #else nuclear@7: glDisable(GL_BLEND); nuclear@7: #endif nuclear@7: nuclear@7: if(vbo) { nuclear@7: glBindBuffer(GL_ARRAY_BUFFER, vbo); nuclear@7: } nuclear@7: nuclear@7: num_quads = 0; nuclear@7: } nuclear@7: nuclear@7: #endif /* !def NO_OPENGL */