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