libdrawtext

changeset 52:34130f58141a

initial commit
author John Tsiombikas <nuclear@mutantstargoat.com>
date Thu, 15 Sep 2011 10:47:38 +0300
parents
children 8e93efcd23ae
files .hgignore Makefile.in configure src/drawgl.c src/drawtext.h src/drawtext_impl.h src/font.c src/utf8.c
diffstat 8 files changed, 1212 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Thu Sep 15 10:47:38 2011 +0300
     1.3 @@ -0,0 +1,6 @@
     1.4 +^libdrawtext\.a$
     1.5 +^libdrawtext\.so.*
     1.6 +drawtext\.dylib$
     1.7 +^Makefile$
     1.8 +\.d$
     1.9 +\.o$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/Makefile.in	Thu Sep 15 10:47:38 2011 +0300
     2.3 @@ -0,0 +1,66 @@
     2.4 +src = $(wildcard src/*.c)
     2.5 +obj = $(src:.c=.o)
     2.6 +dep = $(obj:.o=.d)
     2.7 +
     2.8 +abi = 0
     2.9 +rev = 0
    2.10 +
    2.11 +name = libdrawtext
    2.12 +lib_a = $(name).a
    2.13 +
    2.14 +ifeq ($(shell uname -s), Darwin)
    2.15 +	lib_so = $(name).dylib
    2.16 +	sharedopt = -dynamiclib
    2.17 +
    2.18 +	libgl = -framework OpenGL -lGLEW
    2.19 +else
    2.20 +	soname = $(name).so.$(abi)
    2.21 +	lib_so = $(soname).$(rev)
    2.22 +	sharedopt = -shared -Wl,-soname,$(soname)
    2.23 +
    2.24 +	libgl = -lGL -lGLU -lGLEW
    2.25 +endif
    2.26 +
    2.27 +CC = gcc
    2.28 +CFLAGS = -pedantic -Wall -g -fPIC $(ft2_cflags)
    2.29 +LDFLAGS = $(ft2_libs) $(libgl)
    2.30 +
    2.31 +.PHONY: all
    2.32 +all: $(lib_a) $(lib_so)
    2.33 +
    2.34 +$(lib_a): $(obj)
    2.35 +	$(AR) rcs $@ $(obj)
    2.36 +
    2.37 +$(lib_so): $(obj)
    2.38 +	$(CC) $(sharedopt) -o $@ $(obj) $(LDFLAGS)
    2.39 +
    2.40 +-include $(dep)
    2.41 +
    2.42 +%.d: %.c
    2.43 +	@$(CPP) $(CFLAGS) -MM $< >$@
    2.44 +
    2.45 +
    2.46 +.PHONY: clean
    2.47 +clean:
    2.48 +	rm -f $(obj) $(lib_a) $(lib_so)
    2.49 +
    2.50 +.PHONY: cleandep
    2.51 +cleandep:
    2.52 +	rm -f $(dep)
    2.53 +
    2.54 +.PHONY: install
    2.55 +install: $(lib_a) $(lib_so)
    2.56 +	mkdir -p $(PREFIX)/lib $(PREFIX)/include
    2.57 +	cp $(lib_a) $(PREFIX)/lib/$(lib_a)
    2.58 +	cp $(lib_so) $(PREFIX)/lib/$(lib_so)
    2.59 +	cp src/drawtext.h $(PREFIX)/include/drawtext.h
    2.60 +	rm -f $(PREFIX)/lib/$(name).so
    2.61 +	cd $(PREFIX)/lib && ln -s $(lib_so) $(name).so
    2.62 +
    2.63 +
    2.64 +.PHONY: uninstall
    2.65 +uninstall:
    2.66 +	rm -f $(PREFIX)/lib/$(lib_a)
    2.67 +	rm -f $(PREFIX)/lib/$(lib_so)
    2.68 +	rm -f $(PREFIX)/lib/$(name).so
    2.69 +	rm -f $(PREFIX)/include/drawtext.h
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/configure	Thu Sep 15 10:47:38 2011 +0300
     3.3 @@ -0,0 +1,50 @@
     3.4 +#!/bin/sh
     3.5 +
     3.6 +prefix=/usr/local
     3.7 +opt=false
     3.8 +dbg=true
     3.9 +use_ft2=true
    3.10 +
    3.11 +while [ $# != 0 ]; do
    3.12 +	case $1 in
    3.13 +	--prefix=*)
    3.14 +		prefix=`echo $1 | sed 's/^--prefix=//'`
    3.15 +		shift
    3.16 +		;;
    3.17 +	--enable-opt)
    3.18 +		opt=true
    3.19 +		;;
    3.20 +	--disable-opt)
    3.21 +		opt=false
    3.22 +		;;
    3.23 +	--enable-dbg)
    3.24 +		dbg=true
    3.25 +		;;
    3.26 +	--disable-dbg)
    3.27 +		dbg=false
    3.28 +		;;
    3.29 +	--enable-freetype)
    3.30 +		use_ft2=true
    3.31 +		;;
    3.32 +	--disable-freetype)
    3.33 +		use_ft2=false
    3.34 +		;;
    3.35 +	esac
    3.36 +done
    3.37 +
    3.38 +echo 'Configuring libdrawtext...'
    3.39 +
    3.40 +echo "# do not modify this file manually, it's generated by the configure script" >Makefile
    3.41 +echo "PREFIX = $prefix" >>Makefile
    3.42 +$opt && echo '-O3' | xargs echo 'opt =' >>Makefile
    3.43 +$dbg && echo '-g' | xargs echo 'dbg =' >>Makefile
    3.44 +if $use_ft2; then
    3.45 +	echo 'ft2_cflags = `pkg-config --cflags freetype2`' >>Makefile
    3.46 +	echo 'ft2_libs = `pkg-config --libs freetype2`' >>Makefile
    3.47 +else
    3.48 +	echo 'ft2_cflags = -DNO_FREETYPE' >>Makefile
    3.49 +fi
    3.50 +echo '# --- end of generated part, start of Makefile.in ---' >>Makefile
    3.51 +cat Makefile.in >>Makefile
    3.52 +
    3.53 +echo 'Done. Run make (or gmake) to compile.'
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/drawgl.c	Thu Sep 15 10:47:38 2011 +0300
     4.3 @@ -0,0 +1,255 @@
     4.4 +#ifndef NO_OPENGL
     4.5 +#include <math.h>
     4.6 +#include <ctype.h>
     4.7 +
     4.8 +#include <stdlib.h>
     4.9 +
    4.10 +#if defined(__IPHONE_3_0) || defined(__IPHONE_3_2) || defined(__IPHONE_4_0)
    4.11 +#include <OpenGLES/ES2/gl.h>
    4.12 +#else
    4.13 +#include <GL/glew.h>
    4.14 +#endif
    4.15 +
    4.16 +#include "drawtext.h"
    4.17 +#include "drawtext_impl.h"
    4.18 +
    4.19 +struct vertex {
    4.20 +	float x, y;
    4.21 +	float s, t;
    4.22 +};
    4.23 +
    4.24 +struct quad {
    4.25 +	struct vertex v[6];
    4.26 +};
    4.27 +
    4.28 +static int init(void);
    4.29 +static void cleanup(void);
    4.30 +static void add_glyph(struct glyph *g, float x, float y);
    4.31 +
    4.32 +#define QBUF_SZ		512
    4.33 +static struct quad *qbuf;
    4.34 +static int num_quads;
    4.35 +static int vattr = -1;
    4.36 +static int tattr = -1;
    4.37 +static unsigned int font_tex;
    4.38 +static int buf_mode = DTX_LBF;
    4.39 +
    4.40 +static struct dtx_font *font;
    4.41 +static int font_sz;
    4.42 +
    4.43 +
    4.44 +static int init(void)
    4.45 +{
    4.46 +	if(qbuf) {
    4.47 +		return 0;	/* already initialized */
    4.48 +	}
    4.49 +
    4.50 +	glewInit();
    4.51 +
    4.52 +	if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
    4.53 +		return -1;
    4.54 +	}
    4.55 +	num_quads = 0;
    4.56 +
    4.57 +	atexit(cleanup);
    4.58 +	return 0;
    4.59 +}
    4.60 +
    4.61 +static void cleanup(void)
    4.62 +{
    4.63 +	free(qbuf);
    4.64 +}
    4.65 +
    4.66 +
    4.67 +void dtx_use_font(struct dtx_font *fnt, int sz)
    4.68 +{
    4.69 +	init();
    4.70 +
    4.71 +	font = fnt;
    4.72 +	font_sz = sz;
    4.73 +}
    4.74 +
    4.75 +void dtx_vertex_attribs(int vert_attr, int tex_attr)
    4.76 +{
    4.77 +	vattr = vert_attr;
    4.78 +	tattr = tex_attr;
    4.79 +}
    4.80 +
    4.81 +static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
    4.82 +{
    4.83 +	if(!gmap->tex) {
    4.84 +		glGenTextures(1, &gmap->tex);
    4.85 +		glBindTexture(GL_TEXTURE_2D, gmap->tex);
    4.86 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    4.87 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    4.88 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    4.89 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    4.90 +
    4.91 +#ifdef GL_ES
    4.92 +		glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
    4.93 +		glGenerateMipmap(GL_TEXTURE_2D);
    4.94 +#else
    4.95 +		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
    4.96 +#endif
    4.97 +	}
    4.98 +
    4.99 +	if(font_tex != gmap->tex) {
   4.100 +		dtx_flush();
   4.101 +	}
   4.102 +	font_tex = gmap->tex;
   4.103 +}
   4.104 +
   4.105 +void dtx_glyph(int code)
   4.106 +{
   4.107 +	struct dtx_glyphmap *gmap;
   4.108 +
   4.109 +	if(!font || !(gmap = dtx_get_font_glyphmap(font, font_sz, code))) {
   4.110 +		return;
   4.111 +	}
   4.112 +	set_glyphmap_texture(gmap);
   4.113 +
   4.114 +	add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
   4.115 +	dtx_flush();
   4.116 +}
   4.117 +
   4.118 +void dtx_string(const char *str)
   4.119 +{
   4.120 +	struct dtx_glyphmap *gmap;
   4.121 +	int should_flush = buf_mode == DTX_NBF;
   4.122 +	float pos_x = 0.0f;
   4.123 +	float pos_y = 0.0f;
   4.124 +
   4.125 +	if(!font) {
   4.126 +		return;
   4.127 +	}
   4.128 +
   4.129 +	while(*str) {
   4.130 +		int code = dtx_utf8_char_code(str);
   4.131 +		str = dtx_utf8_next_char((char*)str);
   4.132 +
   4.133 +		switch(code) {
   4.134 +		case '\n':
   4.135 +			if(buf_mode == DTX_LBF) {
   4.136 +				should_flush = 1;
   4.137 +			}
   4.138 +			pos_x = 0.0;
   4.139 +			pos_y -= gmap->line_advance;
   4.140 +			break;
   4.141 +
   4.142 +		case '\t':
   4.143 +			pos_x = fmod(pos_x, 4.0) + 4.0;
   4.144 +			break;
   4.145 +
   4.146 +		case '\r':
   4.147 +			pos_x = 0.0;
   4.148 +			break;
   4.149 +
   4.150 +		default:
   4.151 +			if((gmap = dtx_get_font_glyphmap(font, font_sz, code))) {
   4.152 +				int idx = code - gmap->cstart;
   4.153 +
   4.154 +				set_glyphmap_texture(gmap);
   4.155 +				add_glyph(gmap->glyphs + idx, pos_x, pos_y);
   4.156 +				pos_x += gmap->glyphs[idx].advance;
   4.157 +			}
   4.158 +		}
   4.159 +	}
   4.160 +
   4.161 +	if(should_flush) {
   4.162 +		dtx_flush();
   4.163 +	}
   4.164 +}
   4.165 +
   4.166 +static void qvertex(struct vertex *v, float x, float y, float s, float t)
   4.167 +{
   4.168 +	v->x = x;
   4.169 +	v->y = y;
   4.170 +	v->s = s;
   4.171 +	v->t = t;
   4.172 +}
   4.173 +
   4.174 +static void add_glyph(struct glyph *g, float x, float y)
   4.175 +{
   4.176 +	struct quad *qptr = qbuf + num_quads;
   4.177 +
   4.178 +	x -= g->orig_x;
   4.179 +	y -= g->orig_y;
   4.180 +
   4.181 +	qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight);
   4.182 +	qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight);
   4.183 +	qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
   4.184 +
   4.185 +	qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight);
   4.186 +	qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
   4.187 +	qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny);
   4.188 +
   4.189 +	if(++num_quads >= QBUF_SZ) {
   4.190 +		dtx_flush();
   4.191 +	}
   4.192 +}
   4.193 +
   4.194 +void dtx_flush(void)
   4.195 +{
   4.196 +	if(!num_quads) {
   4.197 +		return;
   4.198 +	}
   4.199 +
   4.200 +#ifndef GL_ES
   4.201 +	glPushAttrib(GL_ENABLE_BIT);
   4.202 +	glEnable(GL_TEXTURE_2D);
   4.203 +#endif
   4.204 +	glBindTexture(GL_TEXTURE_2D, font_tex);
   4.205 +
   4.206 +	if(vattr != -1 && glEnableVertexAttribArray) {
   4.207 +		glEnableVertexAttribArray(vattr);
   4.208 +		glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct vertex), qbuf);
   4.209 +#ifndef GL_ES
   4.210 +	} else {
   4.211 +		glEnableClientState(GL_VERTEX_ARRAY);
   4.212 +		glVertexPointer(2, GL_FLOAT, sizeof(struct vertex), qbuf);
   4.213 +#endif
   4.214 +	}
   4.215 +	if(tattr != -1 && glEnableVertexAttribArray) {
   4.216 +		glEnableVertexAttribArray(tattr);
   4.217 +		glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct vertex), &qbuf->v[0].s);
   4.218 +#ifndef GL_ES
   4.219 +	} else {
   4.220 +		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   4.221 +		glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex), &qbuf->v[0].s);
   4.222 +#endif
   4.223 +	}
   4.224 +
   4.225 +	glEnable(GL_BLEND);
   4.226 +	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   4.227 +
   4.228 +	glDepthMask(0);
   4.229 +
   4.230 +	glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
   4.231 +
   4.232 +	glDepthMask(1);
   4.233 +
   4.234 +	if(vattr != -1 && glDisableVertexAttribArray) {
   4.235 +		glDisableVertexAttribArray(vattr);
   4.236 +#ifndef GL_ES
   4.237 +	} else {
   4.238 +		glDisableClientState(GL_VERTEX_ARRAY);
   4.239 +#endif
   4.240 +	}
   4.241 +	if(tattr != -1 && glDisableVertexAttribArray) {
   4.242 +		glDisableVertexAttribArray(tattr);
   4.243 +#ifndef GL_ES
   4.244 +	} else {
   4.245 +		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   4.246 +#endif
   4.247 +	}
   4.248 +
   4.249 +#ifndef GL_ES
   4.250 +	glPopAttrib();
   4.251 +#else
   4.252 +	glDisable(GL_BLEND);
   4.253 +#endif
   4.254 +
   4.255 +	num_quads = 0;
   4.256 +}
   4.257 +
   4.258 +#endif	/* !def NO_OPENGL */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/drawtext.h	Thu Sep 15 10:47:38 2011 +0300
     5.3 @@ -0,0 +1,159 @@
     5.4 +#ifndef TEXT_H_
     5.5 +#define TEXT_H_
     5.6 +
     5.7 +#include <stdio.h>
     5.8 +#include <stdlib.h>
     5.9 +
    5.10 +struct dtx_font;
    5.11 +struct dtx_glyphmap;
    5.12 +
    5.13 +/* draw buffering modes */
    5.14 +enum {
    5.15 +	DTX_NBF,	/* unbuffered */
    5.16 +	DTX_LBF,	/* line buffered */
    5.17 +	DTX_FBF		/* fully buffered */
    5.18 +};
    5.19 +
    5.20 +struct dtx_box {
    5.21 +	float x, y;
    5.22 +	float width, height;
    5.23 +};
    5.24 +
    5.25 +#ifdef __cplusplus
    5.26 +extern "C" {
    5.27 +#endif
    5.28 +
    5.29 +/* Open a truetype/opentype/whatever font.
    5.30 + *
    5.31 + * If sz is non-zero, the default ASCII glyphmap at the requested point size is
    5.32 + * automatically created as well, and ready to use.
    5.33 + *
    5.34 + * To use other unicode ranges and different font sizes you must first call
    5.35 + * dtx_prepare or dtx_prepare_range before issuing any drawing calls, otherwise
    5.36 + * nothing will be rendered.
    5.37 + */
    5.38 +struct dtx_font *dtx_open_font(const char *fname, int sz);
    5.39 +void dtx_close_font(struct dtx_font *fnt);
    5.40 +
    5.41 +/* prepare an ASCII glyphmap for the specified font size */
    5.42 +void dtx_prepare(struct dtx_font *fnt, int sz);
    5.43 +/* prepare an arbitrary unicode range glyphmap for the specified font size */
    5.44 +void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend);
    5.45 +
    5.46 +/* Finds the glyphmap that contains the specified character code and matches the requested size
    5.47 + * Returns null if it hasn't been created (you should call dtx_prepare/dtx_prepare_range).
    5.48 + */
    5.49 +struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code);
    5.50 +
    5.51 +/* Finds the glyphmap that contains the specified unicode range and matches the requested font size
    5.52 + * Will automatically generate one if it can't find it.
    5.53 + */
    5.54 +struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend);
    5.55 +
    5.56 +/* Creates and returns a glyphmap for a particular unicode range and font size.
    5.57 + * The generated glyphmap is added to the font's list of glyphmaps.
    5.58 + */
    5.59 +struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend);
    5.60 +/* free a glyphmap */
    5.61 +void dtx_free_glyphmap(struct dtx_glyphmap *gmap);
    5.62 +
    5.63 +/* returns a pointer to the raster image of a glyphmap (1 byte per pixel grayscale) */
    5.64 +unsigned char *dtx_get_glyphmap_image(struct dtx_glyphmap *gmap);
    5.65 +/* returns the width of the glyphmap image in pixels */
    5.66 +int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap);
    5.67 +/* returns the height of the glyphmap image in pixels */
    5.68 +int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap);
    5.69 +
    5.70 +/* The following functions can be used even when the library is compiled without
    5.71 + * freetype support. (TODO)
    5.72 + */
    5.73 +struct dtx_glyphmap *dtx_load_glyphmap(const char *fname);
    5.74 +struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp);
    5.75 +int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap);
    5.76 +int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap);
    5.77 +
    5.78 +
    5.79 +/* ---- rendering ---- */
    5.80 +
    5.81 +/* before drawing anything this function must set the font to use */
    5.82 +void dtx_use_font(struct dtx_font *fnt, int sz);
    5.83 +
    5.84 +/* sets the buffering mode
    5.85 + * - DTX_NBUF: every call to dtx_string gets rendered immediately.
    5.86 + * - DTX_LBUF: renders when the buffer is full or the string contains a newline.
    5.87 + * - DTX_FBUF: renders only when the buffer is full (you must call dtx_flush explicitly).
    5.88 + */
    5.89 +void dtx_draw_buffering(int mode);
    5.90 +
    5.91 +/* Sets the vertex attribute indices to use for passing vertex and texture coordinate
    5.92 + * data. By default both are -1 which means you don't have to use a shader, and if you
    5.93 + * do they are accessible through gl_Vertex and gl_MultiTexCoord0, as usual.
    5.94 + *
    5.95 + * NOTE: If you are using OpenGL ES 2.x or OpenGL >= 3.1 pure (non-compatibility) context
    5.96 + * you must specify valid attribute indices.
    5.97 + */
    5.98 +void dtx_vertex_attribs(int vert_attr, int tex_attr);
    5.99 +
   5.100 +/* draws a single glyph at the origin */
   5.101 +void dtx_glyph(int code);
   5.102 +/* draws a utf-8 string starting at the origin. \n \r and \t are handled appropriately. */
   5.103 +void dtx_string(const char *str);
   5.104 +
   5.105 +/* render any pending glyphs (see dtx_draw_buffering) */
   5.106 +void dtx_flush(void);
   5.107 +
   5.108 +
   5.109 +/* ---- encodings ---- */
   5.110 +
   5.111 +/* returns a pointer to the next character in a utf-8 stream */
   5.112 +char *dtx_utf8_next_char(char *str);
   5.113 +
   5.114 +/* returns the unicode character codepoint of the utf-8 character starting at str */
   5.115 +int dtx_utf8_char_code(const char *str);
   5.116 +
   5.117 +/* returns the number of bytes of the utf-8 character starting at str */
   5.118 +int dtx_utf8_nbytes(const char *str);
   5.119 +
   5.120 +/* returns the number of utf-8 character in a zero-terminated utf-8 string */
   5.121 +int dtx_utf8_char_count(const char *str);
   5.122 +
   5.123 +/* Converts a unicode code-point to a utf-8 character by filling in the buffer
   5.124 + * passed at the second argument, and returns the number of bytes taken by that
   5.125 + * utf-8 character.
   5.126 + * It's valid to pass a null buffer pointer, in which case only the byte count is
   5.127 + * returned (useful to figure out how much memory to allocate for a buffer).
   5.128 + */
   5.129 +size_t dtx_utf8_from_char_code(int code, char *buf);
   5.130 +
   5.131 +/* Converts a unicode utf-16 wchar_t string to utf-8, filling in the buffer passed
   5.132 + * at the second argument. Returns the size of the resulting string in bytes.
   5.133 + *
   5.134 + * It's valid to pass a null buffer pointer, in which case only the size gets
   5.135 + * calculated and returned, which is useful for figuring out how much memory to
   5.136 + * allocate for the utf-8 buffer.
   5.137 + */
   5.138 +size_t dtx_utf8_from_string(const wchar_t *str, char *buf);
   5.139 +
   5.140 +
   5.141 +/* ---- metrics ---- */
   5.142 +
   5.143 +/* rendered dimensions of a single glyph */
   5.144 +void dtx_glyph_box(int code, struct dtx_box *box);
   5.145 +float dtx_glyph_width(int code);
   5.146 +float dtx_glyph_height(int code);
   5.147 +
   5.148 +/* rendered dimensions of a string */
   5.149 +void dtx_string_box(const char *str, struct dtx_box *box);
   5.150 +float dtx_string_width(const char *str);
   5.151 +float dtx_string_height(const char *str);
   5.152 +
   5.153 +/* returns the horizontal position of the n-th character of the rendered string
   5.154 + * (useful for placing cursors)
   5.155 + */
   5.156 +float dtx_char_pos(const char *str, int n);
   5.157 +
   5.158 +#ifdef __cplusplus
   5.159 +}
   5.160 +#endif
   5.161 +
   5.162 +#endif	/* TEXT_H_ */
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/drawtext_impl.h	Thu Sep 15 10:47:38 2011 +0300
     6.3 @@ -0,0 +1,45 @@
     6.4 +#ifndef TEXT_IMPL_H_
     6.5 +#define TEXT_IMPL_H_
     6.6 +
     6.7 +struct glyph {
     6.8 +	int code;
     6.9 +	float x, y, width, height;
    6.10 +	/* normalized coords [0, 1] */
    6.11 +	float nx, ny, nwidth, nheight;
    6.12 +	float orig_x, orig_y;
    6.13 +	float advance;
    6.14 +	struct glyph *next;
    6.15 +};
    6.16 +
    6.17 +struct dtx_glyphmap {
    6.18 +	int ptsize;
    6.19 +
    6.20 +	int xsz, ysz;
    6.21 +	unsigned char *pixels;
    6.22 +	unsigned int tex;
    6.23 +
    6.24 +	int cstart, cend;	/* character range */
    6.25 +	int crange;
    6.26 +
    6.27 +	float line_advance;
    6.28 +
    6.29 +	struct glyph *glyphs;
    6.30 +	struct dtx_glyphmap *next;
    6.31 +};
    6.32 +
    6.33 +struct dtx_font {
    6.34 +	/* freetype FT_Face */
    6.35 +	void *face;
    6.36 +
    6.37 +	/* list of glyphmaps */
    6.38 +	struct dtx_glyphmap *gmaps;
    6.39 +
    6.40 +	/* last returned glyphmap (cache) */
    6.41 +	struct dtx_glyphmap *last_gmap;
    6.42 +};
    6.43 +
    6.44 +
    6.45 +#define fperror(str) \
    6.46 +	fprintf(stderr, "%s: %s: %s\n", __func__, (str), strerror(errno))
    6.47 +
    6.48 +#endif	/* TEXT_IMPL_H_ */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/font.c	Thu Sep 15 10:47:38 2011 +0300
     7.3 @@ -0,0 +1,493 @@
     7.4 +#ifndef NO_FREETYPE
     7.5 +#define USE_FREETYPE
     7.6 +#endif
     7.7 +
     7.8 +#include <stdio.h>
     7.9 +#include <stdlib.h>
    7.10 +#include <string.h>
    7.11 +#include <limits.h>
    7.12 +#include <ctype.h>
    7.13 +#include <errno.h>
    7.14 +#ifdef USE_FREETYPE
    7.15 +#include <ft2build.h>
    7.16 +#include FT_FREETYPE_H
    7.17 +#endif
    7.18 +#include "drawtext.h"
    7.19 +#include "drawtext_impl.h"
    7.20 +
    7.21 +#define FTSZ_TO_PIXELS(x)	((x) / 64)
    7.22 +#define MAX_IMG_WIDTH		4096
    7.23 +
    7.24 +
    7.25 +#ifdef USE_FREETYPE
    7.26 +static int init_freetype(void);
    7.27 +static void cleanup(void);
    7.28 +
    7.29 +static void calc_best_size(int total_width, int max_glyph_height, int padding, int pow2, int *imgw, int *imgh);
    7.30 +static int next_pow2(int x);
    7.31 +
    7.32 +static FT_Library ft;
    7.33 +
    7.34 +
    7.35 +static int init_done;
    7.36 +
    7.37 +static int init_freetype(void)
    7.38 +{
    7.39 +	if(!init_done) {
    7.40 +		if(FT_Init_FreeType(&ft) != 0) {
    7.41 +			return -1;
    7.42 +		}
    7.43 +		atexit(cleanup);
    7.44 +		init_done = 1;
    7.45 +	}
    7.46 +	return 0;
    7.47 +}
    7.48 +
    7.49 +static void cleanup(void)
    7.50 +{
    7.51 +	if(init_done) {
    7.52 +		FT_Done_FreeType(ft);
    7.53 +	}
    7.54 +}
    7.55 +
    7.56 +struct dtx_font *dtx_open_font(const char *fname, int sz)
    7.57 +{
    7.58 +	struct dtx_font *fnt;
    7.59 +
    7.60 +	init_freetype();
    7.61 +
    7.62 +	if(!(fnt = calloc(1, sizeof *fnt))) {
    7.63 +		fperror("failed to allocate font structure");
    7.64 +		return 0;
    7.65 +	}
    7.66 +
    7.67 +	if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) {
    7.68 +		fprintf(stderr, "failed to open font file: %s\n", fname);
    7.69 +		return 0;
    7.70 +	}
    7.71 +
    7.72 +	/* pre-create the extended ASCII range glyphmap */
    7.73 +	if(sz) {
    7.74 +		dtx_prepare_range(fnt, sz, 0, 256);
    7.75 +	}
    7.76 +
    7.77 +	return fnt;
    7.78 +}
    7.79 +
    7.80 +void dtx_close_font(struct dtx_font *fnt)
    7.81 +{
    7.82 +	if(!fnt) return;
    7.83 +
    7.84 +	FT_Done_Face(fnt->face);
    7.85 +
    7.86 +	/* destroy the glyphmaps */
    7.87 +	while(fnt->gmaps) {
    7.88 +		void *tmp = fnt->gmaps;
    7.89 +		fnt->gmaps = fnt->gmaps->next;
    7.90 +		dtx_free_glyphmap(tmp);
    7.91 +	}
    7.92 +
    7.93 +	free(fnt);
    7.94 +}
    7.95 +
    7.96 +void dtx_prepare(struct dtx_font *fnt, int sz)
    7.97 +{
    7.98 +	dtx_get_font_glyphmap_range(fnt, sz, 0, 256);
    7.99 +}
   7.100 +
   7.101 +void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend)
   7.102 +{
   7.103 +	dtx_get_font_glyphmap_range(fnt, sz, cstart, cend);
   7.104 +}
   7.105 +
   7.106 +struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code)
   7.107 +{
   7.108 +	struct dtx_glyphmap *gm;
   7.109 +
   7.110 +	/* check to see if the last we've given out fits the bill */
   7.111 +	if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) {
   7.112 +		return fnt->last_gmap;
   7.113 +	}
   7.114 +
   7.115 +	/* otherwise search for the appropriate glyphmap */
   7.116 +	gm = fnt->gmaps;
   7.117 +	while(gm) {
   7.118 +		if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) {
   7.119 +			fnt->last_gmap = gm;
   7.120 +			return gm;
   7.121 +		}
   7.122 +		gm = gm->next;
   7.123 +	}
   7.124 +	return 0;
   7.125 +}
   7.126 +
   7.127 +struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
   7.128 +{
   7.129 +	struct dtx_glyphmap *gm;
   7.130 +
   7.131 +	/* search the available glyphmaps to see if we've got one that includes
   7.132 +	 * the requested range
   7.133 +	 */
   7.134 +	gm = fnt->gmaps;
   7.135 +	while(gm) {
   7.136 +		if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) {
   7.137 +			return gm;
   7.138 +		}
   7.139 +		gm = gm->next;
   7.140 +	}
   7.141 +
   7.142 +	/* not found, create one and add it to the list */
   7.143 +	if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) {
   7.144 +		return 0;
   7.145 +	}
   7.146 +	return gm;
   7.147 +}
   7.148 +
   7.149 +struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
   7.150 +{
   7.151 +	FT_Face face = fnt->face;
   7.152 +	struct dtx_glyphmap *gmap;
   7.153 +	int i, j;
   7.154 +	int gx, gy;
   7.155 +	int padding = 4;
   7.156 +	int total_width = padding;
   7.157 +	int max_height = 0;
   7.158 +
   7.159 +	FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72);
   7.160 +
   7.161 +	if(!(gmap = malloc(sizeof *gmap))) {
   7.162 +		return 0;
   7.163 +	}
   7.164 +
   7.165 +	gmap->ptsize = sz;
   7.166 +	gmap->cstart = cstart;
   7.167 +	gmap->cend = cend;
   7.168 +	gmap->crange = cend - cstart;
   7.169 +	gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height);
   7.170 +
   7.171 +	if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) {
   7.172 +		free(gmap);
   7.173 +		return 0;
   7.174 +	}
   7.175 +
   7.176 +	for(i=0; i<gmap->crange; i++) {
   7.177 +		int h;
   7.178 +
   7.179 +		FT_Load_Char(face, i + cstart, 0);
   7.180 +		h = FTSZ_TO_PIXELS(face->glyph->metrics.height);
   7.181 +
   7.182 +		if(h > max_height) {
   7.183 +			max_height = h;
   7.184 +		}
   7.185 +		total_width += FTSZ_TO_PIXELS(face->glyph->metrics.width) + padding;
   7.186 +	}
   7.187 +
   7.188 +	calc_best_size(total_width, max_height, padding, 1, &gmap->xsz, &gmap->ysz);
   7.189 +
   7.190 +	if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) {
   7.191 +		free(gmap->glyphs);
   7.192 +		free(gmap);
   7.193 +		return 0;
   7.194 +	}
   7.195 +	memset(gmap->pixels, 0, gmap->xsz * gmap->ysz);
   7.196 +
   7.197 +	gx = padding;
   7.198 +	gy = padding;
   7.199 +
   7.200 +	for(i=0; i<gmap->crange; i++) {
   7.201 +		float gwidth, gheight;
   7.202 +		unsigned char *src, *dst;
   7.203 +		FT_GlyphSlot glyph;
   7.204 +
   7.205 +		FT_Load_Char(face, i + cstart, FT_LOAD_RENDER);
   7.206 +		glyph = face->glyph;
   7.207 +		gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width);
   7.208 +		gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height);
   7.209 +
   7.210 +		if(gx > gmap->xsz - gwidth - padding) {
   7.211 +			gx = padding;
   7.212 +			gy += max_height + padding;
   7.213 +		}
   7.214 +
   7.215 +		src = glyph->bitmap.buffer;
   7.216 +		dst = gmap->pixels + gy * gmap->xsz + gx;
   7.217 +
   7.218 +		for(j=0; j<glyph->bitmap.rows; j++) {
   7.219 +			memcpy(dst, src, glyph->bitmap.width);
   7.220 +			dst += gmap->xsz;
   7.221 +			src += glyph->bitmap.pitch;
   7.222 +		}
   7.223 +
   7.224 +		gmap->glyphs[i].code = i;
   7.225 +		gmap->glyphs[i].x = gx - 1;
   7.226 +		gmap->glyphs[i].y = gy - 1;
   7.227 +		gmap->glyphs[i].width = gwidth + 2;
   7.228 +		gmap->glyphs[i].height = gheight + 2;
   7.229 +		gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1;
   7.230 +		gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1;
   7.231 +		gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance);
   7.232 +		/* also precalc normalized */
   7.233 +		gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz;
   7.234 +		gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz;
   7.235 +		gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz;
   7.236 +		gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz;
   7.237 +
   7.238 +		gx += gwidth + padding;
   7.239 +	}
   7.240 +
   7.241 +	/* add it to the glyphmaps list of the font */
   7.242 +	gmap->next = fnt->gmaps;
   7.243 +	fnt->gmaps = gmap;
   7.244 +
   7.245 +	return gmap;
   7.246 +}
   7.247 +#endif	/* USE_FREETYPE */
   7.248 +
   7.249 +void dtx_free_glyphmap(struct dtx_glyphmap *gmap)
   7.250 +{
   7.251 +	if(gmap) {
   7.252 +		free(gmap->pixels);
   7.253 +		free(gmap->glyphs);
   7.254 +		free(gmap);
   7.255 +	}
   7.256 +}
   7.257 +
   7.258 +unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap)
   7.259 +{
   7.260 +	return gmap->pixels;
   7.261 +}
   7.262 +
   7.263 +int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap)
   7.264 +{
   7.265 +	return gmap->xsz;
   7.266 +}
   7.267 +
   7.268 +int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap)
   7.269 +{
   7.270 +	return gmap->ysz;
   7.271 +}
   7.272 +
   7.273 +struct dtx_glyphmap *dtx_load_glyphmap(const char *fname)
   7.274 +{
   7.275 +	FILE *fp;
   7.276 +	struct dtx_glyphmap *gmap;
   7.277 +
   7.278 +	if(!(fp = fopen(fname, "r"))) {
   7.279 +		return 0;
   7.280 +	}
   7.281 +	gmap = dtx_load_glyphmap_stream(fp);
   7.282 +	fclose(fp);
   7.283 +	return gmap;
   7.284 +}
   7.285 +
   7.286 +struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp)
   7.287 +{
   7.288 +	char buf[512];
   7.289 +	int hdr_lines = 0;
   7.290 +	struct dtx_glyphmap *gmap;
   7.291 +	struct glyph *glyphs = 0;
   7.292 +	int min_code = INT_MAX;
   7.293 +	int max_code = INT_MIN;
   7.294 +	int i, max_pixval, num_pixels;
   7.295 +
   7.296 +	if(!(gmap = calloc(1, sizeof *gmap))) {
   7.297 +		fperror("failed to allocate glyphmap");
   7.298 +		return 0;
   7.299 +	}
   7.300 +
   7.301 +	while(hdr_lines < 3) {
   7.302 +		char *line = buf;
   7.303 +		if(!fgets(buf, sizeof buf, fp)) {
   7.304 +			fperror("unexpected end of file");
   7.305 +			goto err;
   7.306 +		}
   7.307 +
   7.308 +		while(isspace(*line)) {
   7.309 +			line++;
   7.310 +		}
   7.311 +
   7.312 +		if(line[0] == '#') {
   7.313 +			struct glyph *g;
   7.314 +			int c;
   7.315 +			float x, y, xsz, ysz, res;
   7.316 +
   7.317 +			res = sscanf(line + 1, "%d: %fx%f+%f+%f\n", &c, &xsz, &ysz, &x, &y);
   7.318 +			if(res != 5) {
   7.319 +				fprintf(stderr, "%s: invalid glyph info line\n", __func__);
   7.320 +				goto err;
   7.321 +			}
   7.322 +
   7.323 +			if(!(g = malloc(sizeof *g))) {
   7.324 +				fperror("failed to allocate glyph");
   7.325 +				goto err;
   7.326 +			}
   7.327 +			g->code = c;
   7.328 +			g->x = x;
   7.329 +			g->y = y;
   7.330 +			g->width = xsz;
   7.331 +			g->height = ysz;
   7.332 +			g->next = glyphs;
   7.333 +			glyphs = g;
   7.334 +
   7.335 +			if(c < min_code) {
   7.336 +				min_code = c;
   7.337 +			}
   7.338 +			if(c > max_code) {
   7.339 +				max_code = c;
   7.340 +			}
   7.341 +		} else {
   7.342 +			switch(hdr_lines) {
   7.343 +			case 0:
   7.344 +				if(0[line] != 'P' || 1[line] != '6') {
   7.345 +					fprintf(stderr, "%s: invalid file format (magic)\n", __func__);
   7.346 +					goto err;
   7.347 +				}
   7.348 +				break;
   7.349 +
   7.350 +			case 1:
   7.351 +				if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) {
   7.352 +					fprintf(stderr, "%s: invalid file format (dim)\n", __func__);
   7.353 +					goto err;
   7.354 +				}
   7.355 +				break;
   7.356 +
   7.357 +			case 2:
   7.358 +				{
   7.359 +					char *endp;
   7.360 +					max_pixval = strtol(line, &endp, 10);
   7.361 +					if(endp == line) {
   7.362 +						fprintf(stderr, "%s: invalid file format (maxval)\n", __func__);
   7.363 +						goto err;
   7.364 +					}
   7.365 +				}
   7.366 +				break;
   7.367 +
   7.368 +			default:
   7.369 +				break;	/* can't happen */
   7.370 +			}
   7.371 +			hdr_lines++;
   7.372 +		}
   7.373 +	}
   7.374 +
   7.375 +	num_pixels = gmap->xsz * gmap->ysz;
   7.376 +	if(!(gmap->pixels = malloc(num_pixels))) {
   7.377 +		fperror("failed to allocate pixels");
   7.378 +		goto err;
   7.379 +	}
   7.380 +
   7.381 +	for(i=0; i<num_pixels; i++) {
   7.382 +		long c = fgetc(fp);
   7.383 +		if(c == -1) {
   7.384 +			fprintf(stderr, "unexpected end of file while reading pixels\n");
   7.385 +			goto err;
   7.386 +		}
   7.387 +		gmap->pixels[i] = 255 * c / max_pixval;
   7.388 +		fseek(fp, 2, SEEK_CUR);
   7.389 +	}
   7.390 +
   7.391 +	gmap->cstart = min_code;
   7.392 +	gmap->cend = max_code + 1;
   7.393 +	gmap->crange = gmap->cend - gmap->cstart;
   7.394 +
   7.395 +	if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) {
   7.396 +		fperror("failed to allocate glyph info");
   7.397 +		goto err;
   7.398 +	}
   7.399 +
   7.400 +	while(glyphs) {
   7.401 +		struct glyph *g = glyphs;
   7.402 +		glyphs = glyphs->next;
   7.403 +
   7.404 +		gmap->glyphs[g->code - gmap->cstart] = *g;
   7.405 +		free(g);
   7.406 +	}
   7.407 +	return gmap;
   7.408 +
   7.409 +err:
   7.410 +	dtx_free_glyphmap(gmap);
   7.411 +	while(glyphs) {
   7.412 +		void *tmp = glyphs;
   7.413 +		glyphs = glyphs->next;
   7.414 +		free(tmp);
   7.415 +	}
   7.416 +	return 0;
   7.417 +}
   7.418 +
   7.419 +int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap)
   7.420 +{
   7.421 +	FILE *fp;
   7.422 +	int res;
   7.423 +
   7.424 +	if(!(fp = fopen(fname, "wb"))) {
   7.425 +		fprintf(stderr, "%s: failed to open file: %s: %s\n", __func__, fname, strerror(errno));
   7.426 +		return -1;
   7.427 +	}
   7.428 +	res = dtx_save_glyphmap_stream(fp, gmap);
   7.429 +	fclose(fp);
   7.430 +	return res;
   7.431 +}
   7.432 +
   7.433 +int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap)
   7.434 +{
   7.435 +	int i, num_pixels;
   7.436 +	struct glyph *g = gmap->glyphs;
   7.437 +
   7.438 +	fprintf(fp, "P6\n%d %d\n", gmap->xsz, gmap->ysz);
   7.439 +	for(i=0; i<gmap->crange; i++) {
   7.440 +		fprintf(fp, "# %d: %fx%f+%f+%f\n", g->code, g->width, g->height, g->x, g->y);
   7.441 +		g++;
   7.442 +	}
   7.443 +	fprintf(fp, "255\n");
   7.444 +
   7.445 +	num_pixels = gmap->xsz * gmap->ysz;
   7.446 +	for(i=0; i<num_pixels; i++) {
   7.447 +		int c = gmap->pixels[i];
   7.448 +		fputc(c, fp);
   7.449 +		fputc(c, fp);
   7.450 +		fputc(c, fp);
   7.451 +	}
   7.452 +	return 0;
   7.453 +}
   7.454 +
   7.455 +
   7.456 +static void calc_best_size(int total_width, int max_glyph_height, int padding, int pow2, int *imgw, int *imgh)
   7.457 +{
   7.458 +	int xsz, ysz, num_rows;
   7.459 +	float aspect;
   7.460 +
   7.461 +	for(xsz=2; xsz<=MAX_IMG_WIDTH; xsz *= 2) {
   7.462 +		num_rows = total_width / xsz + 1;
   7.463 +
   7.464 +		/* take into account the one extra padding for each row after the first */
   7.465 +		num_rows = (total_width + padding * (num_rows - 1)) / xsz + 1;
   7.466 +
   7.467 +		ysz = num_rows * (max_glyph_height + padding) + padding;
   7.468 +		if(pow2) {
   7.469 +			ysz = next_pow2(ysz);
   7.470 +		}
   7.471 +		aspect = (float)xsz / (float)ysz;
   7.472 +
   7.473 +		if(aspect >= 1.0) {
   7.474 +			break;
   7.475 +		}
   7.476 +	}
   7.477 +
   7.478 +	if(xsz > MAX_IMG_WIDTH) {
   7.479 +		xsz = MAX_IMG_WIDTH;
   7.480 +	}
   7.481 +
   7.482 +	*imgw = xsz;
   7.483 +	*imgh = ysz;
   7.484 +}
   7.485 +
   7.486 +
   7.487 +static int next_pow2(int x)
   7.488 +{
   7.489 +	x--;
   7.490 +	x = (x >> 1) | x;
   7.491 +	x = (x >> 2) | x;
   7.492 +	x = (x >> 4) | x;
   7.493 +	x = (x >> 8) | x;
   7.494 +	x = (x >> 16) | x;
   7.495 +	return x + 1;
   7.496 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/utf8.c	Thu Sep 15 10:47:38 2011 +0300
     8.3 @@ -0,0 +1,138 @@
     8.4 +#include "drawtext.h"
     8.5 +
     8.6 +#define	U8_IS_FIRST(x)		(((((x) >> 7) & 1) == 0) || ((((x) >> 6) & 3) == 3))
     8.7 +
     8.8 +static const char first_mask[] = {
     8.9 +	0,
    8.10 +	0x7f,	/* single byte, 7 bits valid */
    8.11 +	0x1f,	/* two-bytes, 5 bits valid */
    8.12 +	0xf,	/* three-bytes, 4 bits valid */
    8.13 +	0x7		/* four-bytes, 3 bits valid */
    8.14 +};
    8.15 +static const char first_shift[] = { 7, 5, 4, 3 };	/* see above */
    8.16 +
    8.17 +#define CONT_PREFIX	0x80
    8.18 +#define CONT_MASK	0x3f
    8.19 +#define CONT_SHIFT	6
    8.20 +
    8.21 +/* last charcodes for 1, 2, 3 or 4-byte utf8 chars */
    8.22 +static const int utf8_lastcode[] = { 0x7f, 0x7ff, 0xfff, 0x1fffff };
    8.23 +
    8.24 +#define prefix_mask(x)	(~first_mask[x])
    8.25 +#define prefix(x)		((prefix_mask(x) << 1) & 0xff)
    8.26 +
    8.27 +
    8.28 +char *dtx_utf8_next_char(char *str)
    8.29 +{
    8.30 +	return str + dtx_utf8_nbytes(str);
    8.31 +}
    8.32 +
    8.33 +int dtx_utf8_char_code(const char *str)
    8.34 +{
    8.35 +	int i, nbytes, shift, code = 0;
    8.36 +	char mask;
    8.37 +
    8.38 +	if(!U8_IS_FIRST(*str)) {
    8.39 +		return -1;
    8.40 +	}
    8.41 +
    8.42 +	nbytes = dtx_utf8_nbytes(str);
    8.43 +	mask = first_mask[nbytes];
    8.44 +	shift = 0;
    8.45 +
    8.46 +	for(i=0; i<nbytes; i++) {
    8.47 +		if(!*str) {
    8.48 +			break;
    8.49 +		}
    8.50 +
    8.51 +		code = (code << shift) | (*str++ & mask);
    8.52 +		mask = 0x3f;
    8.53 +		shift = i == 0 ? first_shift[nbytes] : 6;
    8.54 +	}
    8.55 +
    8.56 +	return code;
    8.57 +}
    8.58 +
    8.59 +int dtx_utf8_nbytes(const char *str)
    8.60 +{
    8.61 +	int i, numset = 0;
    8.62 +	int c = *str;
    8.63 +
    8.64 +	if(!U8_IS_FIRST(c)) {
    8.65 +		for(i=0; !U8_IS_FIRST(str[i]); i++);
    8.66 +		return i;
    8.67 +	}
    8.68 +
    8.69 +	/* count the leading 1s */
    8.70 +	for(i=0; i<4; i++) {
    8.71 +		if(((c >> (7 - i)) & 1) == 0) {
    8.72 +			break;
    8.73 +		}
    8.74 +		numset++;
    8.75 +	}
    8.76 +
    8.77 +	if(!numset) {
    8.78 +		return 1;
    8.79 +	}
    8.80 +	return numset;
    8.81 +}
    8.82 +
    8.83 +int dtx_utf8_char_count(const char *str)
    8.84 +{
    8.85 +	int n = 0;
    8.86 +
    8.87 +	while(*str) {
    8.88 +		n++;
    8.89 +		str = dtx_utf8_next_char((char*)str);
    8.90 +	}
    8.91 +	return n;
    8.92 +}
    8.93 +
    8.94 +size_t dtx_utf8_from_char_code(int code, char *buf)
    8.95 +{
    8.96 +	size_t nbytes = 0;
    8.97 +	int i;
    8.98 +
    8.99 +	for(i=0; i<4; i++) {
   8.100 +		if(code <= utf8_lastcode[i]) {
   8.101 +			nbytes = i + 1;
   8.102 +			break;
   8.103 +		}
   8.104 +	}
   8.105 +
   8.106 +	if(!nbytes && buf) {
   8.107 +		for(i=0; i<nbytes; i++) {
   8.108 +			int idx = nbytes - i - 1;
   8.109 +			int mask, shift, prefix;
   8.110 +
   8.111 +			if(idx > 0) {
   8.112 +				mask = CONT_MASK;
   8.113 +				shift = CONT_SHIFT;
   8.114 +				prefix = CONT_PREFIX;
   8.115 +			} else {
   8.116 +				mask = first_mask[nbytes];
   8.117 +				shift = first_shift[nbytes];
   8.118 +				prefix = prefix(nbytes);
   8.119 +			}
   8.120 +
   8.121 +			buf[idx] = (code & mask) | (prefix & ~mask);
   8.122 +			code >>= shift;
   8.123 +		}
   8.124 +	}
   8.125 +	return nbytes;
   8.126 +}
   8.127 +
   8.128 +size_t dtx_utf8_from_string(const wchar_t *str, char *buf)
   8.129 +{
   8.130 +	size_t nbytes = 0;
   8.131 +	char *ptr = buf;
   8.132 +
   8.133 +	while(*str) {
   8.134 +		int cbytes = dtx_utf8_from_char_code(*str++, ptr);
   8.135 +		if(ptr) {
   8.136 +			ptr += cbytes;
   8.137 +		}
   8.138 +		nbytes += cbytes;
   8.139 +	}
   8.140 +	return nbytes;
   8.141 +}