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