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