# HG changeset patch # User John Tsiombikas # Date 1316072858 -10800 # Node ID 34130f58141acb6883cc29aecb90e6095120075f initial commit diff -r 000000000000 -r 34130f58141a .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,6 @@ +^libdrawtext\.a$ +^libdrawtext\.so.* +drawtext\.dylib$ +^Makefile$ +\.d$ +\.o$ diff -r 000000000000 -r 34130f58141a Makefile.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.in Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,66 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +dep = $(obj:.o=.d) + +abi = 0 +rev = 0 + +name = libdrawtext +lib_a = $(name).a + +ifeq ($(shell uname -s), Darwin) + lib_so = $(name).dylib + sharedopt = -dynamiclib + + libgl = -framework OpenGL -lGLEW +else + soname = $(name).so.$(abi) + lib_so = $(soname).$(rev) + sharedopt = -shared -Wl,-soname,$(soname) + + libgl = -lGL -lGLU -lGLEW +endif + +CC = gcc +CFLAGS = -pedantic -Wall -g -fPIC $(ft2_cflags) +LDFLAGS = $(ft2_libs) $(libgl) + +.PHONY: all +all: $(lib_a) $(lib_so) + +$(lib_a): $(obj) + $(AR) rcs $@ $(obj) + +$(lib_so): $(obj) + $(CC) $(sharedopt) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) -MM $< >$@ + + +.PHONY: clean +clean: + rm -f $(obj) $(lib_a) $(lib_so) + +.PHONY: cleandep +cleandep: + rm -f $(dep) + +.PHONY: install +install: $(lib_a) $(lib_so) + mkdir -p $(PREFIX)/lib $(PREFIX)/include + cp $(lib_a) $(PREFIX)/lib/$(lib_a) + cp $(lib_so) $(PREFIX)/lib/$(lib_so) + cp src/drawtext.h $(PREFIX)/include/drawtext.h + rm -f $(PREFIX)/lib/$(name).so + cd $(PREFIX)/lib && ln -s $(lib_so) $(name).so + + +.PHONY: uninstall +uninstall: + rm -f $(PREFIX)/lib/$(lib_a) + rm -f $(PREFIX)/lib/$(lib_so) + rm -f $(PREFIX)/lib/$(name).so + rm -f $(PREFIX)/include/drawtext.h diff -r 000000000000 -r 34130f58141a configure --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,50 @@ +#!/bin/sh + +prefix=/usr/local +opt=false +dbg=true +use_ft2=true + +while [ $# != 0 ]; do + case $1 in + --prefix=*) + prefix=`echo $1 | sed 's/^--prefix=//'` + shift + ;; + --enable-opt) + opt=true + ;; + --disable-opt) + opt=false + ;; + --enable-dbg) + dbg=true + ;; + --disable-dbg) + dbg=false + ;; + --enable-freetype) + use_ft2=true + ;; + --disable-freetype) + use_ft2=false + ;; + esac +done + +echo 'Configuring libdrawtext...' + +echo "# do not modify this file manually, it's generated by the configure script" >Makefile +echo "PREFIX = $prefix" >>Makefile +$opt && echo '-O3' | xargs echo 'opt =' >>Makefile +$dbg && echo '-g' | xargs echo 'dbg =' >>Makefile +if $use_ft2; then + echo 'ft2_cflags = `pkg-config --cflags freetype2`' >>Makefile + echo 'ft2_libs = `pkg-config --libs freetype2`' >>Makefile +else + echo 'ft2_cflags = -DNO_FREETYPE' >>Makefile +fi +echo '# --- end of generated part, start of Makefile.in ---' >>Makefile +cat Makefile.in >>Makefile + +echo 'Done. Run make (or gmake) to compile.' diff -r 000000000000 -r 34130f58141a src/drawgl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/drawgl.c Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,255 @@ +#ifndef NO_OPENGL +#include +#include + +#include + +#if defined(__IPHONE_3_0) || defined(__IPHONE_3_2) || defined(__IPHONE_4_0) +#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 int init(void); +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_LBF; + +static struct dtx_font *font; +static int font_sz; + + +static int 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_use_font(struct dtx_font *fnt, int sz) +{ + init(); + + font = fnt; + font_sz = sz; +} + +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(!font || !(gmap = dtx_get_font_glyphmap(font, 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(!font) { + return; + } + + while(*str) { + int code = dtx_utf8_char_code(str); + str = dtx_utf8_next_char((char*)str); + + switch(code) { + case '\n': + if(buf_mode == DTX_LBF) { + should_flush = 1; + } + pos_x = 0.0; + pos_y -= gmap->line_advance; + break; + + case '\t': + pos_x = fmod(pos_x, 4.0) + 4.0; + break; + + case '\r': + pos_x = 0.0; + break; + + default: + if((gmap = dtx_get_font_glyphmap(font, font_sz, code))) { + int idx = code - gmap->cstart; + + set_glyphmap_texture(gmap); + add_glyph(gmap->glyphs + idx, pos_x, pos_y); + pos_x += gmap->glyphs[idx].advance; + } + } + } + + 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 000000000000 -r 34130f58141a src/drawtext.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/drawtext.h Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,159 @@ +#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); + +#ifdef __cplusplus +} +#endif + +#endif /* TEXT_H_ */ diff -r 000000000000 -r 34130f58141a src/drawtext_impl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/drawtext_impl.h Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,45 @@ +#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; +}; + + +#define fperror(str) \ + fprintf(stderr, "%s: %s: %s\n", __func__, (str), strerror(errno)) + +#endif /* TEXT_IMPL_H_ */ diff -r 000000000000 -r 34130f58141a src/font.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/font.c Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,493 @@ +#ifndef NO_FREETYPE +#define USE_FREETYPE +#endif + +#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 = malloc(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; +} + + +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 000000000000 -r 34130f58141a src/utf8.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/utf8.c Thu Sep 15 10:47:38 2011 +0300 @@ -0,0 +1,138 @@ +#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[] = { 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; + char 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; +}