# HG changeset patch # User John Tsiombikas # Date 1345734191 -10800 # Node ID e122ba214ee1858d0400db7d23f80637b5e1b6ce # Parent fa8f89d06f6fe6f064871b1f76de14aac8a0522f implementing the command console diff -r fa8f89d06f6f -r e122ba214ee1 prototype/Makefile --- a/prototype/Makefile Thu Aug 23 00:10:10 2012 +0300 +++ b/prototype/Makefile Thu Aug 23 18:03:11 2012 +0300 @@ -1,4 +1,4 @@ -csrc = $(wildcard src/*.c) $(wildcard vmath/*.c) +csrc = $(wildcard src/*.c) $(wildcard vmath/*.c) $(wildcard drawtext/*.c) ccsrc = $(wildcard src/*.cc) $(wildcard vmath/*.cc) obj = $(csrc:.c=.o) $(ccsrc:.cc=.o) dep = $(obj:.o=.d) @@ -8,9 +8,11 @@ dbg = -g warn = -Wall -Wno-format-extra-args -Wno-char-subscripts -CFLAGS = -pedantic $(warn) $(dbg) $(opt) -Ivmath +inc = -Ivmath -Idrawtext `pkg-config --cflags freetype2` + +CFLAGS = -pedantic $(warn) $(dbg) $(opt) $(inc) CXXFLAGS = $(CFLAGS) -std=c++11 -LDFLAGS = $(libgl) -lm -lassimp -limago +LDFLAGS = $(libgl) -lm -lassimp -limago `pkg-config --libs freetype2` ifeq ($(shell uname -s), Darwin) libgl = -framework OpenGL -framework GLUT -lglew diff -r fa8f89d06f6f -r e122ba214ee1 prototype/data/droid_sans_mono.ttf Binary file prototype/data/droid_sans_mono.ttf has changed diff -r fa8f89d06f6f -r e122ba214ee1 prototype/drawtext/drawgl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/drawtext/drawgl.c Thu Aug 23 18:03:11 2012 +0300 @@ -0,0 +1,248 @@ +/* +libdrawtext - a simple library for fast text rendering in OpenGL +Copyright (C) 2011 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 . +*/ +#ifndef NO_OPENGL +#include +#include + +#include + +#ifdef TARGET_IPHONE +#include +#else +#include +#endif + +#include "drawtext.h" +#include "drawtext_impl.h" + +struct vertex { + float x, y; + float s, t; +}; + +struct quad { + struct vertex v[6]; +}; + +static void cleanup(void); +static void add_glyph(struct glyph *g, float x, float y); + +#define QBUF_SZ 512 +static struct quad *qbuf; +static int num_quads; +static int vattr = -1; +static int tattr = -1; +static unsigned int font_tex; +static int buf_mode = DTX_NBF; + + +int dtx_gl_init(void) +{ + if(qbuf) { + return 0; /* already initialized */ + } + + glewInit(); + + if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) { + return -1; + } + num_quads = 0; + + atexit(cleanup); + return 0; +} + +static void cleanup(void) +{ + free(qbuf); +} + + +void dtx_vertex_attribs(int vert_attr, int tex_attr) +{ + vattr = vert_attr; + tattr = tex_attr; +} + +static void set_glyphmap_texture(struct dtx_glyphmap *gmap) +{ + if(!gmap->tex) { + glGenTextures(1, &gmap->tex); + glBindTexture(GL_TEXTURE_2D, gmap->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + 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); + +#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); +#else + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels); +#endif + } + + if(font_tex != gmap->tex) { + dtx_flush(); + } + font_tex = gmap->tex; +} + +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(); +} + +void dtx_string(const char *str) +{ + struct dtx_glyphmap *gmap; + int should_flush = buf_mode == DTX_NBF; + float pos_x = 0.0f; + float pos_y = 0.0f; + + if(!dtx_font) { + return; + } + + while(*str) { + float px, py; + int code = dtx_utf8_char_code(str); + str = dtx_utf8_next_char((char*)str); + + if(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); + } + } + + if(should_flush) { + dtx_flush(); + } +} + +static void qvertex(struct 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(); + } +} + +void dtx_flush(void) +{ + if(!num_quads) { + return; + } + +#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 vertex), qbuf); +#ifndef GL_ES + } else { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, sizeof(struct vertex), qbuf); +#endif + } + if(tattr != -1 && glEnableVertexAttribArray) { + glEnableVertexAttribArray(tattr); + glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct vertex), &qbuf->v[0].s); +#ifndef GL_ES + } else { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(struct 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 + + num_quads = 0; +} + +#endif /* !def NO_OPENGL */ diff -r fa8f89d06f6f -r e122ba214ee1 prototype/drawtext/drawtext.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/drawtext/drawtext.h Thu Aug 23 18:03:11 2012 +0300 @@ -0,0 +1,178 @@ +/* +libdrawtext - a simple library for fast text rendering in OpenGL +Copyright (C) 2011 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 . +*/ +#ifndef TEXT_H_ +#define TEXT_H_ + +#include +#include + +struct dtx_font; +struct dtx_glyphmap; + +/* draw buffering modes */ +enum { + DTX_NBF, /* unbuffered */ + DTX_LBF, /* line buffered */ + DTX_FBF /* fully buffered */ +}; + +struct dtx_box { + float x, y; + float width, height; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Open a truetype/opentype/whatever font. + * + * If sz is non-zero, the default ASCII glyphmap at the requested point size is + * automatically created as well, and ready to use. + * + * To use other unicode ranges and different font sizes you must first call + * dtx_prepare or dtx_prepare_range before issuing any drawing calls, otherwise + * nothing will be rendered. + */ +struct dtx_font *dtx_open_font(const char *fname, int sz); +void dtx_close_font(struct dtx_font *fnt); + +/* prepare an ASCII glyphmap for the specified font size */ +void dtx_prepare(struct dtx_font *fnt, int sz); +/* prepare an arbitrary unicode range glyphmap for the specified font size */ +void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend); + +/* Finds the glyphmap that contains the specified character code and matches the requested size + * Returns null if it hasn't been created (you should call dtx_prepare/dtx_prepare_range). + */ +struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code); + +/* Finds the glyphmap that contains the specified unicode range and matches the requested font size + * Will automatically generate one if it can't find it. + */ +struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend); + +/* Creates and returns a glyphmap for a particular unicode range and font size. + * The generated glyphmap is added to the font's list of glyphmaps. + */ +struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend); +/* free a glyphmap */ +void dtx_free_glyphmap(struct dtx_glyphmap *gmap); + +/* returns a pointer to the raster image of a glyphmap (1 byte per pixel grayscale) */ +unsigned char *dtx_get_glyphmap_image(struct dtx_glyphmap *gmap); +/* returns the width of the glyphmap image in pixels */ +int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap); +/* returns the height of the glyphmap image in pixels */ +int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap); + +/* The following functions can be used even when the library is compiled without + * freetype support. (TODO) + */ +struct dtx_glyphmap *dtx_load_glyphmap(const char *fname); +struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp); +int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap); +int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap); + + +/* ---- rendering ---- */ + +/* before drawing anything this function must set the font to use */ +void dtx_use_font(struct dtx_font *fnt, int sz); + +/* sets the buffering mode + * - DTX_NBUF: every call to dtx_string gets rendered immediately. + * - DTX_LBUF: renders when the buffer is full or the string contains a newline. + * - DTX_FBUF: renders only when the buffer is full (you must call dtx_flush explicitly). + */ +void dtx_draw_buffering(int mode); + +/* Sets the vertex attribute indices to use for passing vertex and texture coordinate + * data. By default both are -1 which means you don't have to use a shader, and if you + * do they are accessible through gl_Vertex and gl_MultiTexCoord0, as usual. + * + * NOTE: If you are using OpenGL ES 2.x or OpenGL >= 3.1 pure (non-compatibility) context + * you must specify valid attribute indices. + */ +void dtx_vertex_attribs(int vert_attr, int tex_attr); + +/* draws a single glyph at the origin */ +void dtx_glyph(int code); +/* draws a utf-8 string starting at the origin. \n \r and \t are handled appropriately. */ +void dtx_string(const char *str); + +/* render any pending glyphs (see dtx_draw_buffering) */ +void dtx_flush(void); + + +/* ---- encodings ---- */ + +/* returns a pointer to the next character in a utf-8 stream */ +char *dtx_utf8_next_char(char *str); + +/* returns the unicode character codepoint of the utf-8 character starting at str */ +int dtx_utf8_char_code(const char *str); + +/* returns the number of bytes of the utf-8 character starting at str */ +int dtx_utf8_nbytes(const char *str); + +/* returns the number of utf-8 character in a zero-terminated utf-8 string */ +int dtx_utf8_char_count(const char *str); + +/* Converts a unicode code-point to a utf-8 character by filling in the buffer + * passed at the second argument, and returns the number of bytes taken by that + * utf-8 character. + * It's valid to pass a null buffer pointer, in which case only the byte count is + * returned (useful to figure out how much memory to allocate for a buffer). + */ +size_t dtx_utf8_from_char_code(int code, char *buf); + +/* Converts a unicode utf-16 wchar_t string to utf-8, filling in the buffer passed + * at the second argument. Returns the size of the resulting string in bytes. + * + * It's valid to pass a null buffer pointer, in which case only the size gets + * calculated and returned, which is useful for figuring out how much memory to + * allocate for the utf-8 buffer. + */ +size_t dtx_utf8_from_string(const wchar_t *str, char *buf); + + +/* ---- metrics ---- */ + +/* rendered dimensions of a single glyph */ +void dtx_glyph_box(int code, struct dtx_box *box); +float dtx_glyph_width(int code); +float dtx_glyph_height(int code); + +/* rendered dimensions of a string */ +void dtx_string_box(const char *str, struct dtx_box *box); +float dtx_string_width(const char *str); +float dtx_string_height(const char *str); + +/* returns the horizontal position of the n-th character of the rendered string + * (useful for placing cursors) + */ +float dtx_char_pos(const char *str, int n); + +int dtx_char_at_pt(const char *str, float pt); + +#ifdef __cplusplus +} +#endif + +#endif /* TEXT_H_ */ diff -r fa8f89d06f6f -r e122ba214ee1 prototype/drawtext/drawtext_impl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/drawtext/drawtext_impl.h Thu Aug 23 18:03:11 2012 +0300 @@ -0,0 +1,72 @@ +/* +libdrawtext - a simple library for fast text rendering in OpenGL +Copyright (C) 2011 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 . +*/ +#ifndef TEXT_IMPL_H_ +#define TEXT_IMPL_H_ + +struct glyph { + int code; + float x, y, width, height; + /* normalized coords [0, 1] */ + float nx, ny, nwidth, nheight; + float orig_x, orig_y; + float advance; + struct glyph *next; +}; + +struct dtx_glyphmap { + int ptsize; + + int xsz, ysz; + unsigned char *pixels; + unsigned int tex; + + int cstart, cend; /* character range */ + int crange; + + float line_advance; + + struct glyph *glyphs; + struct dtx_glyphmap *next; +}; + +struct dtx_font { + /* freetype FT_Face */ + void *face; + + /* list of glyphmaps */ + struct dtx_glyphmap *gmaps; + + /* last returned glyphmap (cache) */ + struct dtx_glyphmap *last_gmap; +}; + + +struct dtx_font *dtx_font; +int dtx_font_sz; + + +#define fperror(str) \ + fprintf(stderr, "%s: %s: %s\n", __func__, (str), strerror(errno)) + +int dtx_gl_init(void); + +/* returns zero if it should NOT be printed and modifies xpos/ypos */ +/* implemented in font.c */ +struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos); + +#endif /* TEXT_IMPL_H_ */ diff -r fa8f89d06f6f -r e122ba214ee1 prototype/drawtext/font.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/drawtext/font.c Thu Aug 23 18:03:11 2012 +0300 @@ -0,0 +1,678 @@ +/* +libdrawtext - a simple library for fast text rendering in OpenGL +Copyright (C) 2011 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 . +*/ +#ifndef NO_FREETYPE +#define USE_FREETYPE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_FREETYPE +#include +#include FT_FREETYPE_H +#endif +#include "drawtext.h" +#include "drawtext_impl.h" + +#define FTSZ_TO_PIXELS(x) ((x) / 64) +#define MAX_IMG_WIDTH 4096 + + +#ifdef USE_FREETYPE +static int init_freetype(void); +static void cleanup(void); + +static void calc_best_size(int total_width, int max_glyph_height, int padding, int pow2, int *imgw, int *imgh); +static int next_pow2(int x); + +static FT_Library ft; + + +static int init_done; + +static int init_freetype(void) +{ + if(!init_done) { + if(FT_Init_FreeType(&ft) != 0) { + return -1; + } + atexit(cleanup); + init_done = 1; + } + return 0; +} + +static void cleanup(void) +{ + if(init_done) { + FT_Done_FreeType(ft); + } +} + +struct dtx_font *dtx_open_font(const char *fname, int sz) +{ + struct dtx_font *fnt; + + init_freetype(); + + if(!(fnt = calloc(1, sizeof *fnt))) { + fperror("failed to allocate font structure"); + return 0; + } + + if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) { + fprintf(stderr, "failed to open font file: %s\n", fname); + return 0; + } + + /* pre-create the extended ASCII range glyphmap */ + if(sz) { + dtx_prepare_range(fnt, sz, 0, 256); + } + + return fnt; +} + +void dtx_close_font(struct dtx_font *fnt) +{ + if(!fnt) return; + + FT_Done_Face(fnt->face); + + /* destroy the glyphmaps */ + while(fnt->gmaps) { + void *tmp = fnt->gmaps; + fnt->gmaps = fnt->gmaps->next; + dtx_free_glyphmap(tmp); + } + + free(fnt); +} + +void dtx_prepare(struct dtx_font *fnt, int sz) +{ + dtx_get_font_glyphmap_range(fnt, sz, 0, 256); +} + +void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend) +{ + dtx_get_font_glyphmap_range(fnt, sz, cstart, cend); +} + +struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code) +{ + struct dtx_glyphmap *gm; + + /* check to see if the last we've given out fits the bill */ + if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) { + return fnt->last_gmap; + } + + /* otherwise search for the appropriate glyphmap */ + gm = fnt->gmaps; + while(gm) { + if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) { + fnt->last_gmap = gm; + return gm; + } + gm = gm->next; + } + return 0; +} + +struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) +{ + struct dtx_glyphmap *gm; + + /* search the available glyphmaps to see if we've got one that includes + * the requested range + */ + gm = fnt->gmaps; + while(gm) { + if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) { + return gm; + } + gm = gm->next; + } + + /* not found, create one and add it to the list */ + if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) { + return 0; + } + return gm; +} + +struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) +{ + FT_Face face = fnt->face; + struct dtx_glyphmap *gmap; + int i, j; + int gx, gy; + int padding = 4; + int total_width = padding; + int max_height = 0; + + FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72); + + if(!(gmap = calloc(1, sizeof *gmap))) { + return 0; + } + + gmap->ptsize = sz; + gmap->cstart = cstart; + gmap->cend = cend; + gmap->crange = cend - cstart; + gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height); + + if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) { + free(gmap); + return 0; + } + + for(i=0; icrange; i++) { + int h; + + FT_Load_Char(face, i + cstart, 0); + h = FTSZ_TO_PIXELS(face->glyph->metrics.height); + + if(h > max_height) { + max_height = h; + } + total_width += FTSZ_TO_PIXELS(face->glyph->metrics.width) + padding; + } + + calc_best_size(total_width, max_height, padding, 1, &gmap->xsz, &gmap->ysz); + + if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) { + free(gmap->glyphs); + free(gmap); + return 0; + } + memset(gmap->pixels, 0, gmap->xsz * gmap->ysz); + + gx = padding; + gy = padding; + + for(i=0; icrange; i++) { + float gwidth, gheight; + unsigned char *src, *dst; + FT_GlyphSlot glyph; + + FT_Load_Char(face, i + cstart, FT_LOAD_RENDER); + glyph = face->glyph; + gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width); + gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height); + + if(gx > gmap->xsz - gwidth - padding) { + gx = padding; + gy += max_height + padding; + } + + src = glyph->bitmap.buffer; + dst = gmap->pixels + gy * gmap->xsz + gx; + + for(j=0; jbitmap.rows; j++) { + memcpy(dst, src, glyph->bitmap.width); + dst += gmap->xsz; + src += glyph->bitmap.pitch; + } + + gmap->glyphs[i].code = i; + gmap->glyphs[i].x = gx - 1; + gmap->glyphs[i].y = gy - 1; + gmap->glyphs[i].width = gwidth + 2; + gmap->glyphs[i].height = gheight + 2; + gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1; + gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1; + gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance); + /* also precalc normalized */ + gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz; + gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz; + gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz; + gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz; + + gx += gwidth + padding; + } + + /* add it to the glyphmaps list of the font */ + gmap->next = fnt->gmaps; + fnt->gmaps = gmap; + + return gmap; +} +#endif /* USE_FREETYPE */ + +void dtx_free_glyphmap(struct dtx_glyphmap *gmap) +{ + if(gmap) { + free(gmap->pixels); + free(gmap->glyphs); + free(gmap); + } +} + +unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap) +{ + return gmap->pixels; +} + +int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap) +{ + return gmap->xsz; +} + +int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap) +{ + return gmap->ysz; +} + +struct dtx_glyphmap *dtx_load_glyphmap(const char *fname) +{ + FILE *fp; + struct dtx_glyphmap *gmap; + + if(!(fp = fopen(fname, "r"))) { + return 0; + } + gmap = dtx_load_glyphmap_stream(fp); + fclose(fp); + return gmap; +} + +struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp) +{ + char buf[512]; + int hdr_lines = 0; + struct dtx_glyphmap *gmap; + struct glyph *glyphs = 0; + int min_code = INT_MAX; + int max_code = INT_MIN; + int i, max_pixval, num_pixels; + + if(!(gmap = calloc(1, sizeof *gmap))) { + fperror("failed to allocate glyphmap"); + return 0; + } + + while(hdr_lines < 3) { + char *line = buf; + if(!fgets(buf, sizeof buf, fp)) { + fperror("unexpected end of file"); + goto err; + } + + while(isspace(*line)) { + line++; + } + + if(line[0] == '#') { + struct glyph *g; + int c; + float x, y, xsz, ysz, res; + + res = sscanf(line + 1, "%d: %fx%f+%f+%f\n", &c, &xsz, &ysz, &x, &y); + if(res != 5) { + fprintf(stderr, "%s: invalid glyph info line\n", __func__); + goto err; + } + + if(!(g = malloc(sizeof *g))) { + fperror("failed to allocate glyph"); + goto err; + } + g->code = c; + g->x = x; + g->y = y; + g->width = xsz; + g->height = ysz; + g->next = glyphs; + glyphs = g; + + if(c < min_code) { + min_code = c; + } + if(c > max_code) { + max_code = c; + } + } else { + switch(hdr_lines) { + case 0: + if(0[line] != 'P' || 1[line] != '6') { + fprintf(stderr, "%s: invalid file format (magic)\n", __func__); + goto err; + } + break; + + case 1: + if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) { + fprintf(stderr, "%s: invalid file format (dim)\n", __func__); + goto err; + } + break; + + case 2: + { + char *endp; + max_pixval = strtol(line, &endp, 10); + if(endp == line) { + fprintf(stderr, "%s: invalid file format (maxval)\n", __func__); + goto err; + } + } + break; + + default: + break; /* can't happen */ + } + hdr_lines++; + } + } + + num_pixels = gmap->xsz * gmap->ysz; + if(!(gmap->pixels = malloc(num_pixels))) { + fperror("failed to allocate pixels"); + goto err; + } + + for(i=0; ipixels[i] = 255 * c / max_pixval; + fseek(fp, 2, SEEK_CUR); + } + + gmap->cstart = min_code; + gmap->cend = max_code + 1; + gmap->crange = gmap->cend - gmap->cstart; + + if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) { + fperror("failed to allocate glyph info"); + goto err; + } + + while(glyphs) { + struct glyph *g = glyphs; + glyphs = glyphs->next; + + gmap->glyphs[g->code - gmap->cstart] = *g; + free(g); + } + return gmap; + +err: + dtx_free_glyphmap(gmap); + while(glyphs) { + void *tmp = glyphs; + glyphs = glyphs->next; + free(tmp); + } + return 0; +} + +int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap) +{ + FILE *fp; + int res; + + if(!(fp = fopen(fname, "wb"))) { + fprintf(stderr, "%s: failed to open file: %s: %s\n", __func__, fname, strerror(errno)); + return -1; + } + res = dtx_save_glyphmap_stream(fp, gmap); + fclose(fp); + return res; +} + +int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap) +{ + int i, num_pixels; + struct glyph *g = gmap->glyphs; + + fprintf(fp, "P6\n%d %d\n", gmap->xsz, gmap->ysz); + for(i=0; icrange; i++) { + fprintf(fp, "# %d: %fx%f+%f+%f\n", g->code, g->width, g->height, g->x, g->y); + g++; + } + fprintf(fp, "255\n"); + + num_pixels = gmap->xsz * gmap->ysz; + for(i=0; ipixels[i]; + fputc(c, fp); + fputc(c, fp); + fputc(c, fp); + } + return 0; +} + + +void dtx_use_font(struct dtx_font *fnt, int sz) +{ + dtx_gl_init(); + + dtx_font = fnt; + dtx_font_sz = sz; +} + +void dtx_glyph_box(int code, struct dtx_box *box) +{ + int cidx; + struct dtx_glyphmap *gmap; + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); + + cidx = code - gmap->cstart; + + box->x = gmap->glyphs[cidx].orig_x; + box->y = gmap->glyphs[cidx].orig_y; + box->width = gmap->glyphs[cidx].width; + box->height = gmap->glyphs[cidx].height; +} + +float dtx_glyph_width(int code) +{ + struct dtx_box box; + dtx_glyph_box(code, &box); + return box.width; +} + +float dtx_glyph_height(int code) +{ + struct dtx_box box; + dtx_glyph_box(code, &box); + return box.height; +} + +void dtx_string_box(const char *str, struct dtx_box *box) +{ + int code; + float pos_x = 0.0f, pos_y = 0.0f; + struct glyph *g = 0; + float x0, y0, x1, y1; + + x0 = y0 = FLT_MAX; + x1 = y1 = -FLT_MAX; + + while(*str) { + float px, py; + struct dtx_glyphmap *gmap; + + code = dtx_utf8_char_code(str); + str = dtx_utf8_next_char((char*)str); + + px = pos_x; + py = pos_y; + + if((gmap = dtx_proc_char(code, &pos_x, &pos_y))) { + g = gmap->glyphs + code - gmap->cstart; + + if(px + g->orig_x < x0) { + x0 = px + g->orig_x; + } + if(py - g->orig_y < y0) { + y0 = py - g->orig_y; + } + if(px + g->orig_x + g->width > x1) { + x1 = px + g->orig_x + g->width; + } + if(py - g->orig_y + g->height > y1) { + y1 = py - g->orig_y + g->height; + } + } + } + + box->x = x0; + box->y = y0; + box->width = x1 - x0; + box->height = y1 - y0; +} + +float dtx_string_width(const char *str) +{ + struct dtx_box box; + + dtx_string_box(str, &box); + return box.width; +} + +float dtx_string_height(const char *str) +{ + struct dtx_box box; + + dtx_string_box(str, &box); + return box.height; +} + +float dtx_char_pos(const char *str, int n) +{ + int i; + float pos = 0.0; + struct dtx_glyphmap *gmap; + + for(i=0; iglyphs[i].advance; + } + return pos; +} + +int dtx_char_at_pt(const char *str, float pt) +{ + int i; + float prev_pos = 0.0f, pos = 0.0f; + struct dtx_glyphmap *gmap; + + for(i=0; *str; i++) { + int code = dtx_utf8_char_code(str); + str = dtx_utf8_next_char((char*)str); + + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); + pos += gmap->glyphs[i].advance; + + if(fabs(pt - prev_pos) < fabs(pt - pos)) { + break; + } + prev_pos = pos; + } + return i; +} + +struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos) +{ + struct dtx_glyphmap *gmap; + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); + + switch(code) { + case '\n': + *xpos = 0.0; + if(gmap) { + *ypos -= gmap->line_advance; + } + return 0; + + case '\t': + if(gmap) { + *xpos = (fmod(*xpos, 4.0) + 4.0) * gmap->glyphs[0].advance; + } + return 0; + + case '\r': + *xpos = 0.0; + return 0; + + default: + break; + } + + if(gmap) { + *xpos += gmap->glyphs[code - gmap->cstart].advance; + } + return gmap; +} + +static void calc_best_size(int total_width, int max_glyph_height, int padding, int pow2, int *imgw, int *imgh) +{ + int xsz, ysz, num_rows; + float aspect; + + for(xsz=2; xsz<=MAX_IMG_WIDTH; xsz *= 2) { + num_rows = total_width / xsz + 1; + + /* take into account the one extra padding for each row after the first */ + num_rows = (total_width + padding * (num_rows - 1)) / xsz + 1; + + ysz = num_rows * (max_glyph_height + padding) + padding; + if(pow2) { + ysz = next_pow2(ysz); + } + aspect = (float)xsz / (float)ysz; + + if(aspect >= 1.0) { + break; + } + } + + if(xsz > MAX_IMG_WIDTH) { + xsz = MAX_IMG_WIDTH; + } + + *imgw = xsz; + *imgh = ysz; +} + + +static int next_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} diff -r fa8f89d06f6f -r e122ba214ee1 prototype/drawtext/utf8.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/drawtext/utf8.c Thu Aug 23 18:03:11 2012 +0300 @@ -0,0 +1,154 @@ +/* +libdrawtext - a simple library for fast text rendering in OpenGL +Copyright (C) 2011 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" + +#define U8_IS_FIRST(x) (((((x) >> 7) & 1) == 0) || ((((x) >> 6) & 3) == 3)) + +static const char first_mask[] = { + 0, + 0x7f, /* single byte, 7 bits valid */ + 0x1f, /* two-bytes, 5 bits valid */ + 0xf, /* three-bytes, 4 bits valid */ + 0x7 /* four-bytes, 3 bits valid */ +}; +static const char first_shift[] = { 0, 7, 5, 4, 3 }; /* see above */ + +#define CONT_PREFIX 0x80 +#define CONT_MASK 0x3f +#define CONT_SHIFT 6 + +/* last charcodes for 1, 2, 3 or 4-byte utf8 chars */ +static const int utf8_lastcode[] = { 0x7f, 0x7ff, 0xfff, 0x1fffff }; + +#define prefix_mask(x) (~first_mask[x]) +#define prefix(x) ((prefix_mask(x) << 1) & 0xff) + + +char *dtx_utf8_next_char(char *str) +{ + return str + dtx_utf8_nbytes(str); +} + +int dtx_utf8_char_code(const char *str) +{ + int i, nbytes, shift, code = 0; + int mask; + + if(!U8_IS_FIRST(*str)) { + return -1; + } + + nbytes = dtx_utf8_nbytes(str); + mask = first_mask[nbytes]; + shift = 0; + + for(i=0; i> (7 - i)) & 1) == 0) { + break; + } + numset++; + } + + if(!numset) { + return 1; + } + return numset; +} + +int dtx_utf8_char_count(const char *str) +{ + int n = 0; + + while(*str) { + n++; + str = dtx_utf8_next_char((char*)str); + } + return n; +} + +size_t dtx_utf8_from_char_code(int code, char *buf) +{ + size_t nbytes = 0; + int i; + + for(i=0; i<4; i++) { + if(code <= utf8_lastcode[i]) { + nbytes = i + 1; + break; + } + } + + if(!nbytes && buf) { + for(i=0; i 0) { + mask = CONT_MASK; + shift = CONT_SHIFT; + prefix = CONT_PREFIX; + } else { + mask = first_mask[nbytes]; + shift = first_shift[nbytes]; + prefix = prefix(nbytes); + } + + buf[idx] = (code & mask) | (prefix & ~mask); + code >>= shift; + } + } + return nbytes; +} + +size_t dtx_utf8_from_string(const wchar_t *str, char *buf) +{ + size_t nbytes = 0; + char *ptr = buf; + + while(*str) { + int cbytes = dtx_utf8_from_char_code(*str++, ptr); + if(ptr) { + ptr += cbytes; + } + nbytes += cbytes; + } + return nbytes; +} diff -r fa8f89d06f6f -r e122ba214ee1 prototype/src/cmdcon.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/src/cmdcon.cc Thu Aug 23 18:03:11 2012 +0300 @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "cmdcon.h" +#include "drawtext.h" +#include "datapath.h" +#include "cfg.h" + +#define FONT_FILENAME "droid_sans_mono.ttf" + +static void runcmd(); +static void complete(); + +static struct dtx_font *font; + +std::string cmdline; + +bool init_cmdcon() +{ + const char *path = datafile_path(FONT_FILENAME); + if(!path) { + fprintf(stderr, "failed to locate font file: %s\n", FONT_FILENAME); + return false; + } + + if(!(font = dtx_open_font(path, 12))) { + fprintf(stderr, "failed to open font file: %s\n", path); + return false; + } + dtx_use_font(font, 12); + return true; +} + +void cleanup_cmdcon() +{ + if(font) { + dtx_close_font(font); + } +} + +void cmdcon_keypress(int key) +{ + if(isprint(key)) { + cmdline.push_back(key); + return; + } + + switch(key) { + case '\n': + case '\r': + runcmd(); + cmdline.clear(); + break; + + case '\t': + complete(); + break; + + case '\b': + if(!cmdline.empty()) { + cmdline.erase(cmdline.back()); + } + break; + + default: + break; + } +} + +void draw_cmdcon() +{ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, cfg.width, -cfg.height, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT); + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glUseProgram(0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_QUADS); + glColor4f(0.4, 0.4, 0.4, 0.5); + glVertex2f(-10, -10); + glVertex2f(10, -10); + glVertex2f(10, 10); + glVertex2f(-10, 10); + glEnd(); + + glTranslatef(cfg.width / 2, cfg.height / 2, 0); + + glColor4f(0.2, 0.9, 0.3, 1.0); + dtx_string(cmdline.c_str()); + dtx_flush(); + + glPopAttrib(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +static bool cmd_toggle_wire(std::stringstream &strin) +{ + static bool wire; + wire = !wire; + glPolygonMode(GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL); + return true; +} + +static bool cmd_quit(std::stringstream &strin) +{ + exit(0); +} + +static struct { + std::string name; + bool (*func)(std::stringstream &strin); +} cmd[] = { + {"wire", cmd_toggle_wire}, + {"quit", cmd_quit}, + {"exit", cmd_quit} +}; +#define NUM_CMD (int)(sizeof cmd / sizeof *cmd) + +static void runcmd() +{ + std::stringstream strin(cmdline); + + std::string token; + strin >> token; + + for(int i=0; i