X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=demo_prior;a=blobdiff_plain;f=libs%2Fdrawtext%2Fsrc%2Fdrawgl.c;fp=libs%2Fdrawtext%2Fsrc%2Fdrawgl.c;h=4bf161e101ac254777460c0d98c7561b9c3ffdac;hp=0000000000000000000000000000000000000000;hb=2f14a35e7d557da12f24056267b911f24774aa18;hpb=5eefe7b94c8d6c6caa2c10e3835ab0831a3c42a1 diff --git a/libs/drawtext/src/drawgl.c b/libs/drawtext/src/drawgl.c new file mode 100644 index 0000000..4bf161e --- /dev/null +++ b/libs/drawtext/src/drawgl.c @@ -0,0 +1,441 @@ +/* +libdrawtext - a simple library for fast text rendering in OpenGL +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "drawtext.h" +#include "drawtext_impl.h" + +struct quad { + struct dtx_vertex v[6]; +}; + +struct dtx_glyphmap *cur_gmap; + +#define QBUF_SZ 512 +static struct quad *qbuf; +static int num_quads; + +static dtx_user_draw_func user_draw_func; +static void *user_cls; + +static int dtx_draw_init(void); +static void cleanup(void); + +static void set_glyphmap_texture(struct dtx_glyphmap *gmap); +static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush); +static void flush_user(void); +static void add_glyph(struct glyph *g, float x, float y); + + +#ifndef NO_OPENGL +#include +#include +#include +#include + +#ifdef TARGET_IPHONE +#include +#ifndef GL_ES +#define GL_ES +#endif + +#elif defined(ANDROID) +#include +#ifndef GL_ES +#define GL_ES +#endif + +#else /* regular OpenGL */ + +#ifdef WIN32 +#include +#endif + +#ifdef __APPLE__ +#include + +#else + +#define GL_GLEXT_LEGACY /* don't include glext.h internally in gl.h */ +#include +#ifndef NO_GLU +#include +#endif + +#ifdef __unix__ +#define GLX_GLXEXT_LEGACY /* don't include glxext.h internally in glx.h */ +#include +#endif + +#endif /* !__APPLE__ */ +#endif /* !TARGET_IPHONE */ + +#ifdef GL_ES +#define GL_CLAMP GL_CLAMP_TO_EDGE +#endif + +static void dtx_gl_init(void); +static void cleanup(void); +static void flush(void); + +static int vattr = -1; +static int tattr = -1; +static int cattr = -1; +static unsigned int font_tex; + +#ifndef GL_ES +#ifndef GL_VERSION_1_5 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +typedef void (APIENTRY *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +static PFNGLBINDBUFFERPROC glBindBuffer; +#endif + +#ifndef GL_VERSION_2_0 +typedef void (APIENTRY *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (APIENTRY *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (APIENTRY *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; +static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; +static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +#endif + +#ifdef WIN32 +#define load_glfunc(s) wglGetProcAddress(s) +#elif defined(__unix__) +#define load_glfunc(s) glXGetProcAddress((unsigned char*)s) +#endif + +#endif /* !GL_ES */ + +void dtx_target_opengl(void) +{ + dtx_draw_init(); + dtx_drawchar = drawchar; + dtx_drawflush = flush; + + user_draw_func = 0; +} + +int dtx_gl_setopt(enum dtx_option opt, int val) +{ + switch(opt) { + case DTX_GL_ATTR_VERTEX: + vattr = val; + break; + case DTX_GL_ATTR_TEXCOORD: + tattr = val; + break; + case DTX_GL_ATTR_COLOR: + cattr = val; + break; + default: + return -1; + } + return 0; +} + +int dtx_gl_getopt(enum dtx_option opt, int *res) +{ + switch(opt) { + case DTX_GL_ATTR_VERTEX: + *res = vattr; + break; + case DTX_GL_ATTR_TEXCOORD: + *res = tattr; + break; + case DTX_GL_ATTR_COLOR: + *res = cattr; + break; + default: + return -1; + } + return 0; +} + +static void dtx_gl_init(void) +{ +#ifndef GL_ES +#ifndef GL_VERSION_1_5 + glBindBuffer = (PFNGLBINDBUFFERPROC)load_glfunc("glBindBuffer"); +#endif +#ifndef GL_VERSION_2_0 + glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load_glfunc("glEnableVertexAttribArray"); + glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load_glfunc("glDisableVertexAttribArray"); + glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load_glfunc("glVertexAttribPointer"); +#endif +#endif /* !GL_ES */ +} + + +void dtx_vertex_attribs(int vert_attr, int tex_attr) +{ + vattr = vert_attr; + tattr = tex_attr; +} + +static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) +{ + if(!gmap->tex) { + glGenTextures(1, &gmap->tex); + glBindTexture(GL_TEXTURE_2D, gmap->tex); +#if !defined(GL_ES) && defined(NO_GLU) + /* TODO: ideally we want to have mipmaps even without GLU, and we should + * just use SGIS_generate_mipmaps if available + */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + gmap->tex_valid = 0; + } + + if(!gmap->tex_valid) { + glBindTexture(GL_TEXTURE_2D, gmap->tex); +#ifdef GL_ES + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels); + glGenerateMipmap(GL_TEXTURE_2D); +#elif !defined(NO_GLU) + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels); +#endif + gmap->tex_valid = 1; + } + + font_tex = gmap->tex; +} + + + +static void flush(void) +{ + int vbo; + + if(!num_quads) { + return; + } + + if(glBindBuffer) { + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + +#ifndef GL_ES + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_TEXTURE_2D); +#endif + glBindTexture(GL_TEXTURE_2D, font_tex); + + if(vattr != -1 && glEnableVertexAttribArray) { + glEnableVertexAttribArray(vattr); + glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf); +#ifndef GL_ES + } else { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf); +#endif + } + if(tattr != -1 && glEnableVertexAttribArray) { + glEnableVertexAttribArray(tattr); + glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s); +#ifndef GL_ES + } else { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s); +#endif + } + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDepthMask(0); + + glDrawArrays(GL_TRIANGLES, 0, num_quads * 6); + + glDepthMask(1); + + if(vattr != -1 && glDisableVertexAttribArray) { + glDisableVertexAttribArray(vattr); +#ifndef GL_ES + } else { + glDisableClientState(GL_VERTEX_ARRAY); +#endif + } + if(tattr != -1 && glDisableVertexAttribArray) { + glDisableVertexAttribArray(tattr); +#ifndef GL_ES + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +#endif + } + +#ifndef GL_ES + glPopAttrib(); +#else + glDisable(GL_BLEND); +#endif + + if(glBindBuffer && vbo) { + glBindBuffer(GL_ARRAY_BUFFER, vbo); + } + + num_quads = 0; +} +#else + +/* no-opengl build, define all public gl functions as stubs */ +void dtx_target_opengl(void) {} +int dtx_gl_setopt(enum dtx_option opt, int val) { return -1; } +int dtx_gl_getopt(enum dtx_option opt, int *val) { return -1; } + +static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {} + +#endif /* !def NO_OPENGL */ + +static int dtx_draw_init(void) +{ + if(qbuf) { + return 0; /* already initialized */ + } + +#ifndef NO_OPENGL + dtx_gl_init(); +#endif + + if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) { + return -1; + } + num_quads = 0; + + atexit(cleanup); + return 0; +} + +static void cleanup(void) +{ + free(qbuf); +} + + +void dtx_target_user(dtx_user_draw_func func, void *cls) +{ + dtx_draw_init(); + + user_draw_func = func; + user_cls = cls; + + dtx_drawchar = drawchar; + dtx_drawflush = flush_user; +} + +void dtx_glyph(int code) +{ + struct dtx_glyphmap *gmap; + + if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) { + return; + } + set_glyphmap_texture(gmap); + + add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0); + dtx_flush(); +} + +static void set_glyphmap_texture(struct dtx_glyphmap *gmap) +{ + if(!user_draw_func) { + set_glyphmap_texture_gl(gmap); + } + + if(cur_gmap && gmap != cur_gmap) { + dtx_flush(); + } + cur_gmap = gmap; +} + +static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush) +{ + struct dtx_glyphmap *gmap; + float px, py; + int code = dtx_utf8_char_code(str); + str = dtx_utf8_next_char((char*)str); + + if(dtx_buf_mode == DTX_LBF && code == '\n') { + *should_flush = 1; + } + + px = *pos_x; + py = *pos_y; + + if((gmap = dtx_proc_char(code, pos_x, pos_y))) { + int idx = code - gmap->cstart; + + set_glyphmap_texture(gmap); + add_glyph(gmap->glyphs + idx, px, py); + } + return str; +} + +static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t) +{ + v->x = x; + v->y = y; + v->s = s; + v->t = t; +} + +static void add_glyph(struct glyph *g, float x, float y) +{ + struct quad *qptr = qbuf + num_quads; + + x -= g->orig_x; + y -= g->orig_y; + + qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight); + qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight); + qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny); + + qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight); + qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny); + qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny); + + if(++num_quads >= QBUF_SZ) { + dtx_flush(); + } +} + +static void flush_user(void) +{ + struct dtx_pixmap pixmap; + + if(!num_quads || !user_draw_func || !cur_gmap) { + return; + } + + pixmap.pixels = cur_gmap->pixels; + pixmap.width = cur_gmap->xsz; + pixmap.height = cur_gmap->ysz; + pixmap.udata = cur_gmap->udata; + + user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls); + cur_gmap->udata = pixmap.udata; + + num_quads = 0; +}