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 +}