1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdarg.h>
5 #include <limits.h>
6 #include <math.h>
7 #include <time.h>
8 #include <zlib.h>
9 #include <ft2build.h>
10 #include FT_FREETYPE_H
11 #include FT_STROKER_H
12 #include FT_GLYPH_H
13
14 typedef unsigned char uchar;
15 typedef unsigned short ushort;
16 typedef unsigned int uint;
17
imin(int a,int b)18 static inline int imin(int a, int b) { return a < b ? a : b; }
imax(int a,int b)19 static inline int imax(int a, int b) { return a > b ? a : b; }
iclamp(int n,int l,int h)20 static inline int iclamp(int n, int l, int h) { return imax(l, imin(n, h)); }
fclamp(float n,float l,float h)21 static inline float fclamp(float n, float l, float h) { return fmax(l, fmin(n, h)); }
22
fatal(const char * fmt,...)23 void fatal(const char *fmt, ...)
24 {
25 va_list v;
26 va_start(v, fmt);
27 vfprintf(stderr, fmt, v);
28 va_end(v);
29 fputc('\n', stderr);
30
31 exit(EXIT_FAILURE);
32 }
33
bigswap(uint n)34 uint bigswap(uint n)
35 {
36 const int islittleendian = 1;
37 return *(const uchar *)&islittleendian ? (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000) : n;
38 }
39
writebig(FILE * f,uint n)40 size_t writebig(FILE *f, uint n)
41 {
42 n = bigswap(n);
43 return fwrite(&n, 1, sizeof(n), f);
44 }
45
writepngchunk(FILE * f,const char * type,uchar * data,uint len)46 void writepngchunk(FILE *f, const char *type, uchar *data, uint len)
47 {
48 uint crc;
49 writebig(f, len);
50 fwrite(type, 1, 4, f);
51 fwrite(data, 1, len, f);
52
53 crc = crc32(0, Z_NULL, 0);
54 crc = crc32(crc, (const Bytef *)type, 4);
55 if(data) crc = crc32(crc, data, len);
56 writebig(f, crc);
57 }
58
59 struct pngihdr
60 {
61 uint width, height;
62 uchar bitdepth, colortype, compress, filter, interlace;
63 };
64
savepng(const char * filename,uchar * data,int w,int h,int bpp,int flip)65 void savepng(const char *filename, uchar *data, int w, int h, int bpp, int flip)
66 {
67 const uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
68 struct pngihdr ihdr;
69 FILE *f;
70 long idat;
71 uint len, crc;
72 z_stream z;
73 uchar buf[1<<12];
74 int i, j;
75
76 memset(&ihdr, 0, sizeof(ihdr));
77 ihdr.width = bigswap(w);
78 ihdr.height = bigswap(h);
79 ihdr.bitdepth = 8;
80 switch(bpp)
81 {
82 case 1: ihdr.colortype = 0; break;
83 case 2: ihdr.colortype = 4; break;
84 case 3: ihdr.colortype = 2; break;
85 case 4: ihdr.colortype = 6; break;
86 default: fatal("tessfont: invalid PNG bpp"); return;
87 }
88 f = fopen(filename, "wb");
89 if(!f) { fatal("tessfont: could not write to %s", filename); return; }
90
91 fwrite(signature, 1, sizeof(signature), f);
92
93 writepngchunk(f, "IHDR", (uchar *)&ihdr, 13);
94
95 idat = ftell(f);
96 len = 0;
97 fwrite("\0\0\0\0IDAT", 1, 8, f);
98 crc = crc32(0, Z_NULL, 0);
99 crc = crc32(crc, (const Bytef *)"IDAT", 4);
100
101 z.zalloc = NULL;
102 z.zfree = NULL;
103 z.opaque = NULL;
104
105 if(deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK)
106 goto error;
107
108 z.next_out = (Bytef *)buf;
109 z.avail_out = sizeof(buf);
110
111 for(i = 0; i < h; i++)
112 {
113 uchar filter = 0;
114 for(j = 0; j < 2; j++)
115 {
116 z.next_in = j ? (Bytef *)data + (flip ? h-i-1 : i)*w*bpp : (Bytef *)&filter;
117 z.avail_in = j ? w*bpp : 1;
118 while(z.avail_in > 0)
119 {
120 if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror;
121 #define FLUSHZ do { \
122 int flush = sizeof(buf) - z.avail_out; \
123 crc = crc32(crc, buf, flush); \
124 len += flush; \
125 fwrite(buf, 1, flush, f); \
126 z.next_out = (Bytef *)buf; \
127 z.avail_out = sizeof(buf); \
128 } while(0)
129 FLUSHZ;
130 }
131 }
132 }
133
134 for(;;)
135 {
136 int err = deflate(&z, Z_FINISH);
137 if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror;
138 FLUSHZ;
139 if(err == Z_STREAM_END) break;
140 }
141
142 deflateEnd(&z);
143
144 fseek(f, idat, SEEK_SET);
145 writebig(f, len);
146 fseek(f, 0, SEEK_END);
147 writebig(f, crc);
148
149 writepngchunk(f, "IEND", NULL, 0);
150
151 fclose(f);
152 return;
153
154 cleanuperror:
155 deflateEnd(&z);
156
157 error:
158 fclose(f);
159
160 fatal("tessfont: failed saving PNG to %s", filename);
161 }
162
163 enum
164 {
165 CT_PRINT = 1<<0,
166 CT_SPACE = 1<<1,
167 CT_DIGIT = 1<<2,
168 CT_ALPHA = 1<<3,
169 CT_LOWER = 1<<4,
170 CT_UPPER = 1<<5,
171 CT_UNICODE = 1<<6
172 };
173 #define CUBECTYPE(s, p, d, a, A, u, U) \
174 0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \
175 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
176 s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \
177 d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \
178 p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \
179 A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \
180 p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \
181 a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \
182 U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \
183 u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \
184 u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
185 u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
186 u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \
187 U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
188 U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \
189 u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u
190 const uchar cubectype[256] =
191 {
192 CUBECTYPE(CT_SPACE,
193 CT_PRINT,
194 CT_PRINT|CT_DIGIT,
195 CT_PRINT|CT_ALPHA|CT_LOWER,
196 CT_PRINT|CT_ALPHA|CT_UPPER,
197 CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER,
198 CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER)
199 };
iscubeprint(uchar c)200 int iscubeprint(uchar c) { return cubectype[c]&CT_PRINT; }
iscubespace(uchar c)201 int iscubespace(uchar c) { return cubectype[c]&CT_SPACE; }
iscubealpha(uchar c)202 int iscubealpha(uchar c) { return cubectype[c]&CT_ALPHA; }
iscubealnum(uchar c)203 int iscubealnum(uchar c) { return cubectype[c]&(CT_ALPHA|CT_DIGIT); }
iscubelower(uchar c)204 int iscubelower(uchar c) { return cubectype[c]&CT_LOWER; }
iscubeupper(uchar c)205 int iscubeupper(uchar c) { return cubectype[c]&CT_UPPER; }
206 const int cube2unichars[256] =
207 {
208 0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201,
209 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219,
210 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
211 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
212 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
213 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
214 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
215 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220,
216 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
217 238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104,
218 0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141,
219 0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160,
220 0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411,
221 0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B,
222 0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D,
223 0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491
224 };
cube2uni(uchar c)225 int cube2uni(uchar c)
226 {
227 return cube2unichars[c];
228 }
229
encodeutf8(int uni)230 const char *encodeutf8(int uni)
231 {
232 static char buf[7];
233 char *dst = buf;
234 if(uni <= 0x7F) { *dst++ = uni; goto uni1; }
235 else if(uni <= 0x7FF) { *dst++ = 0xC0 | (uni>>6); goto uni2; }
236 else if(uni <= 0xFFFF) { *dst++ = 0xE0 | (uni>>12); goto uni3; }
237 else if(uni <= 0x1FFFFF) { *dst++ = 0xF0 | (uni>>18); goto uni4; }
238 else if(uni <= 0x3FFFFFF) { *dst++ = 0xF8 | (uni>>24); goto uni5; }
239 else if(uni <= 0x7FFFFFFF) { *dst++ = 0xFC | (uni>>30); goto uni6; }
240 else goto uni1;
241 uni6: *dst++ = 0x80 | ((uni>>24)&0x3F);
242 uni5: *dst++ = 0x80 | ((uni>>18)&0x3F);
243 uni4: *dst++ = 0x80 | ((uni>>12)&0x3F);
244 uni3: *dst++ = 0x80 | ((uni>>6)&0x3F);
245 uni2: *dst++ = 0x80 | (uni&0x3F);
246 uni1: *dst++ = '\0';
247 return buf;
248 }
249
250 struct fontchar { int code, uni, tex, x, y, sdfradius, sdfpitch, sdfx, sdfy, sdfw, sdfh; float w, h, left, top, advance; FT_BitmapGlyph glyph; uchar *sdf; };
251
252 const char *texdir = "";
253
texfilename(const char * name,int texnum)254 const char *texfilename(const char *name, int texnum)
255 {
256 static char file[256];
257 snprintf(file, sizeof(file), "%s%d.png", name, texnum);
258 return file;
259 }
260
texname(const char * name,int texnum)261 const char *texname(const char *name, int texnum)
262 {
263 static char file[512];
264 snprintf(file, sizeof(file), "<grey>%s%s", texdir, texfilename(name, texnum));
265 return file;
266 }
267
268 static int sdist;
searchdist(int x,int y,int cx,int cy)269 static inline void searchdist(int x, int y, int cx, int cy)
270 {
271 int dx = cx - x, dy = cy - y, dist = dx*dx + dy*dy;
272 if(dist < sdist) sdist = dist;
273 }
274
275 static int sx1, ex1;
search1(int x,int y,int w,int radius,int cy,uchar * src)276 static inline int search1(int x, int y, int w, int radius, int cy, uchar *src)
277 {
278 int cx = imin(ex1, x)-1;
279 if(cx >= sx1)
280 {
281 uchar *bsrc = &src[cx>>3];
282 int bx = cx&~7, bits = *bsrc;
283 if(bits==0xFF) cx = bx-1;
284 else
285 {
286 bits >>= 7-(cx&7);
287 if(bx <= sx1)
288 {
289 for(; cx >= sx1; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; }
290 goto foundbelow1;
291 }
292 else for(; cx >= bx; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; }
293 }
294 while(cx >= sx1)
295 {
296 bits = *--bsrc;
297 if(bits==0xFF) cx -= 8;
298 else if((bx = cx - 7) <= sx1)
299 {
300 for(; cx >= sx1; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; }
301 goto foundbelow1;
302 }
303 else for(; cx >= bx; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; }
304
305 }
306 }
307 foundbelow1:
308 cx = imax(sx1, x);
309 if(cx < ex1)
310 {
311 uchar *bsrc = &src[cx>>3];
312 int bx = (cx&~7) + 8, bits = *bsrc;
313 if(bits==0xFF) cx = bx;
314 else
315 {
316 bits <<= cx&7;
317 if(bx >= ex1)
318 {
319 for(; cx < ex1; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; }
320 goto foundabove1;
321 }
322 else for(; cx < bx; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; }
323 }
324 while(cx < ex1)
325 {
326 bits = *++bsrc;
327 if(bits==0xFF) cx += 8;
328 else if((bx = cx + 8) >= ex1)
329 {
330 for(; cx < ex1; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; }
331 goto foundabove1;
332 }
333 else for(; cx < bx; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; }
334 }
335 }
336 foundabove1:
337 if(x - radius < 0) searchdist(x, y, -1, cy);
338 if(x + radius > w) searchdist(x, y, w, cy);
339 return sx1 < ex1;
340 }
341
342 static int sx0, ex0;
search0(int x,int y,int w,int radius,int cy,uchar * src)343 static inline int search0(int x, int y, int w, int radius, int cy, uchar *src)
344 {
345 int cx = imin(ex0, x)-1;
346 if(cx >= sx0)
347 {
348 uchar *bsrc = &src[cx>>3];
349 int bx = cx&~7, bits = *bsrc;
350 if(!bits) cx = bx-1;
351 else
352 {
353 bits >>= 7-(cx&7);
354 if(bx <= sx0)
355 {
356 for(; cx >= sx0; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; }
357 goto foundbelow0;
358 }
359 else for(; cx >= bx; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; }
360 }
361 while(cx >= sx0)
362 {
363 bits = *--bsrc;
364 if(!bits) cx -= 8;
365 else if((bx = cx - 7) <= sx0)
366 {
367 for(; cx >= sx0; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; }
368 goto foundbelow0;
369 }
370 else for(; cx >= bx; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; }
371 }
372 }
373 foundbelow0:
374 cx = imax(sx0, x);
375 if(cx < ex0)
376 {
377 uchar *bsrc = &src[cx>>3];
378 int bx = (cx&~7) + 8, bits = *bsrc;
379 if(!bits) cx = bx;
380 else
381 {
382 bits <<= cx&7;
383 if(bx >= ex0)
384 {
385 for(; cx < ex0; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; }
386 goto foundabove0;
387 }
388 else for(; cx < bx; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; }
389 }
390 while(cx < ex0)
391 {
392 bits = *++bsrc;
393 if(!bits) cx += 8;
394 else if((bx = cx + 8) >= ex0)
395 {
396 for(; cx < ex0; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; }
397 goto foundabove0;
398 }
399 else for(; cx < bx; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; }
400 }
401 }
402 foundabove0:
403 return sx0 < ex0;
404 }
405
406 #define SUPERSAMPLE_MIN 1
407 #define SUPERSAMPLE_MAX 32
408 static int supersample = SUPERSAMPLE_MIN;
409
gensdf(struct fontchar * c)410 void gensdf(struct fontchar *c)
411 {
412 int w = c->glyph->bitmap.width, h = c->glyph->bitmap.rows, radius = c->sdfradius*supersample;
413 int dx, dy, dw = (w + 2*radius + supersample-1)/supersample, dh = (h + 2*radius + supersample-1)/supersample;
414 int x, y, x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
415 uchar *dst = (uchar *)malloc(dw*dh), *src;
416 if(!dst) fatal("tessfont: failed allocating signed distance field");
417 c->sdfpitch = dw;
418 c->sdf = dst;
419 for(dy = 0; dy < dh; dy++)
420 for(dx = 0; dx < dw; dx++)
421 {
422 double total = 0;
423 for(y = dy*supersample - radius; y < (dy+1)*supersample - radius; y++)
424 for(x = dx*supersample - radius; x < (dx+1)*supersample - radius; x++)
425 {
426 int sx = imax(x - radius, 0), sy = imax(y - radius, 0), ex = imin(x + radius, w), ey = imin(y + radius, h), cy, val = 0;
427 if(y >= 0 && y < h && x >= 0 && x < w)
428 {
429 uchar *center = (uchar *)c->glyph->bitmap.buffer + y*c->glyph->bitmap.pitch;
430 val = (center[x>>3]<<(x&7))&0x80;
431 }
432 sdist = INT_MAX;
433 if(val)
434 {
435 for(cy = imin(ey, y)-1, sx1 = sx, ex1 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch;
436 cy >= sy && search1(x, y, w, radius, cy, src);
437 cy--, src -= c->glyph->bitmap.pitch);
438 for(cy = imax(sy, y), sx1 = sx, ex1 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch;
439 cy < ey && search1(x, y, w, radius, cy, src);
440 cy++, src += c->glyph->bitmap.pitch);
441 if(y - radius < 0) searchdist(x, y, x, -1);
442 if(y + radius > h) searchdist(x, y, x, h);
443 }
444 else
445 {
446 for(cy = imin(ey, y)-1, sx0 = sx, ex0 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch;
447 cy >= sy && search0(x, y, w, radius, cy, src);
448 cy--, src -= c->glyph->bitmap.pitch);
449 for(cy = imax(sy, y), sx0 = sx, ex0 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch;
450 cy < ey && search0(x, y, w, radius, cy, src);
451 cy++, src += c->glyph->bitmap.pitch);
452 }
453 if(val) total += sqrt(sdist);
454 else total -= sqrt(sdist);
455 }
456 *dst = (uchar)iclamp((int)round(127.5 + (127.5/(supersample*supersample))*total/radius), 0, 255);
457 if(*dst)
458 {
459 x1 = imin(x1, dx);
460 y1 = imin(y1, dy);
461 x2 = imax(x2, dx);
462 y2 = imax(y2, dy);
463 }
464 dst++;
465 }
466 if(x1 <= x2 && y1 <= y2)
467 {
468 c->sdfx = x1;
469 c->sdfy = y1;
470 c->sdfw = x2 - x1 + 1;
471 c->sdfh = y2 - y1 + 1;
472 }
473 }
474
writetexs(const char * name,struct fontchar * chars,int numchars,int numtexs,int tw,int th)475 void writetexs(const char *name, struct fontchar *chars, int numchars, int numtexs, int tw, int th)
476 {
477 int tex;
478 uchar *pixels = (uchar *)malloc(tw*th);
479 if(!pixels) fatal("tessfont: failed allocating textures");
480 for(tex = 0; tex < numtexs; tex++)
481 {
482 const char *file = texfilename(name, tex);
483 int texchars = 0, i;
484 uchar *dst, *src;
485 memset(pixels, 0, tw*th);
486 for(i = 0; i < numchars; i++)
487 {
488 struct fontchar *c = &chars[i];
489 int y;
490 if(c->tex != tex) continue;
491 texchars++;
492 dst = &pixels[c->y*tw + c->x];
493 src = c->sdf + c->sdfy*c->sdfpitch + c->sdfx;
494 for(y = 0; y < c->sdfh; y++)
495 {
496 memcpy(dst, src, c->sdfw);
497 dst += tw;
498 src += c->sdfpitch;
499 }
500 }
501 printf("tessfont: writing %d chars to %s\n", texchars, file);
502 savepng(file, pixels, tw, th, 1, 0);
503 }
504 free(pixels);
505 }
506
507 static float offsetx = 0, offsety = 0, border = 0, border2 = 0, outline = 0, outline2 = 0;
508 static int scale = 0;
509
writecfg(const char * name,struct fontchar * chars,int numchars,float x1,float y1,float x2,float y2,int sw,int sh,int argc,char ** argv)510 void writecfg(const char *name, struct fontchar *chars, int numchars, float x1, float y1, float x2, float y2, int sw, int sh, int argc, char **argv)
511 {
512 FILE *f;
513 char file[256];
514 int i, lastcode = 0, lasttex = 0;
515 snprintf(file, sizeof(file), "%s.cfg", name);
516 f = fopen(file, "w");
517 if(!f) fatal("tessfont: failed writing %s", file);
518 printf("tessfont: writing %d chars to %s\n", numchars, file);
519 fprintf(f, "//");
520 for(i = 1; i < argc; i++)
521 fprintf(f, " %s", argv[i]);
522 fprintf(f, "\n");
523 fprintf(f, "font \"%s\" \"%s\" %d %d\n", name, texname(name, 0), sw, sh);
524 if(scale > 0) fprintf(f, "fontscale %d\n", scale);
525 if(border2) fprintf(f, "fontborder %g %g\n", border, border2);
526 else if(border) fprintf(f, "fontborder %g\n", border);
527 if(outline2) fprintf(f, "fontoutline %g %g\n", outline, outline2);
528 else if(outline) fprintf(f, "fontoutline %g\n", outline);
529 for(i = 0; i < numchars; i++)
530 {
531 struct fontchar *c = &chars[i];
532 if(!lastcode && lastcode < c->code)
533 {
534 fprintf(f, "fontoffset \"%s\"\n", encodeutf8(c->uni));
535 lastcode = c->code;
536 }
537 else if(lastcode < c->code)
538 {
539 if(lastcode + 1 == c->code)
540 fprintf(f, "fontskip // %d\n", lastcode);
541 else
542 fprintf(f, "fontskip %d // %d .. %d\n", c->code - lastcode, lastcode, c->code-1);
543 lastcode = c->code;
544 }
545 if(lasttex != c->tex)
546 {
547 fprintf(f, "\nfonttex \"%s\"\n", texname(name, c->tex));
548 lasttex = c->tex;
549 }
550 float offx = c->sdfx-c->sdfradius + c->left + offsetx, offy = c->sdfy-c->sdfradius + y2-c->top + offsety;
551 if(c->code != c->uni)
552 fprintf(f, "fontchar %d %d %d %d %g %g %g // %s (%d -> 0x%X)\n", c->x, c->y, c->sdfw, c->sdfh, offx, offy, c->advance, encodeutf8(c->uni), c->code, c->uni);
553 else
554 fprintf(f, "fontchar %d %d %d %d %g %g %g // %s (%d)\n", c->x, c->y, c->sdfw, c->sdfh, offx, offy, c->advance, encodeutf8(c->uni), c->code);
555 lastcode++;
556 }
557 fclose(f);
558 }
559
groupchar(int c)560 int groupchar(int c)
561 {
562 switch(c)
563 {
564 case 0x152: case 0x153: case 0x178: return 1;
565 }
566 if(c < 127 || c >= 0x2000) return 0;
567 if(c < 0x100) return 1;
568 if(c < 0x400) return 2;
569 return 3;
570 }
571
sortchars(const void * x,const void * y)572 int sortchars(const void *x, const void *y)
573 {
574 const struct fontchar *xc = *(const struct fontchar **)x, *yc = *(const struct fontchar **)y;
575 int xg = groupchar(xc->uni), yg = groupchar(yc->uni);
576 if(xg < yg) return -1;
577 if(xg > yg) return 1;
578 if(xc->sdfh != yc->sdfh) return yc->sdfh - xc->sdfh;
579 if(xc->sdfw != yc->sdfw) return yc->sdfw - xc->sdfw;
580 return yc->uni - xc->uni;
581 }
582
scorechar(struct fontchar * f,int pad,int tw,int th,int rw,int rh,int ry)583 int scorechar(struct fontchar *f, int pad, int tw, int th, int rw, int rh, int ry)
584 {
585 int score = 0;
586 if(rw + f->sdfw > tw) { ry += rh + pad; score = 1; }
587 if(ry + f->sdfh > th) score = 2;
588 return score;
589 }
590
main(int argc,char ** argv)591 int main(int argc, char **argv)
592 {
593 FT_Library l;
594 FT_Face f;
595 int i, radius, pad, w, h, tw, th, c, numgen = 0, trial = -2, rw = 0, rh = 0, ry = 0, sw = 0, sh = 0;
596 float advance, x1 = INT_MAX, x2 = INT_MIN, y1 = INT_MAX, y2 = INT_MIN, w2 = 0, h2 = 0;
597 time_t starttime, endtime;
598 struct fontchar chars[256];
599 struct fontchar *order[256];
600 int numchars = 0, numtex = 0;
601 if(argc < 13)
602 fatal("Usage: tessfont infile outfile supersample border[:border2[:outline:outline2]] radius pad offsetx[:offsety] advance charwidth charheight texwidth texheight [spacewidth spaceheight scale texdir]");
603 supersample = iclamp(atoi(argv[3]), SUPERSAMPLE_MIN, SUPERSAMPLE_MAX);
604 sscanf(argv[4], "%f:%f:%f:%f", &border, &border2, &outline, &outline2);
605 radius = atoi(argv[5]);
606 pad = atoi(argv[6]);
607 sscanf(argv[7], "%f:%f", &offsetx, &offsety);
608 advance = atof(argv[8]);
609 w = atoi(argv[9]);
610 h = atoi(argv[10]);
611 tw = atoi(argv[11]);
612 th = atoi(argv[12]);
613 if(argc > 13) sw = atoi(argv[13]);
614 if(argc > 14) sh = atoi(argv[14]);
615 if(argc > 15) scale = atoi(argv[15]);
616 if(argc > 16) texdir = argv[16];
617 if(FT_Init_FreeType(&l))
618 fatal("tessfont: failed initing freetype");
619 if(FT_New_Face(l, argv[1], 0, &f) ||
620 FT_Set_Charmap(f, f->charmaps[0]) ||
621 FT_Set_Pixel_Sizes(f, w*supersample, h*supersample))
622 fatal("tessfont: failed loading font %s", argv[1]);
623 setbuf(stdout, NULL);
624 starttime = time(NULL);
625 for(c = 0; c < 256; c++) if(iscubeprint(c))
626 {
627 FT_Glyph p;
628 FT_BitmapGlyph b;
629 struct fontchar *dst = &chars[numchars];
630 dst->code = c;
631 dst->uni = cube2uni(c);
632 if(FT_Load_Char(f, dst->uni, FT_LOAD_TARGET_MONO))
633 fatal("tessfont: failed loading character %s", encodeutf8(dst->uni));
634 FT_Get_Glyph(f->glyph, &p);
635 FT_Glyph_To_Bitmap(&p, FT_RENDER_MODE_MONO, 0, 1);
636 b = (FT_BitmapGlyph)p;
637 dst->tex = -1;
638 dst->x = INT_MIN;
639 dst->y = INT_MIN;
640 dst->w = b->bitmap.width/(float)supersample;
641 dst->h = b->bitmap.rows/(float)supersample;
642 dst->left = b->left/(float)supersample;
643 dst->top = b->top/(float)supersample;
644 dst->advance = offsetx + p->advance.x/(float)(supersample<<16) + advance;
645 dst->glyph = b;
646 dst->sdfradius = radius;
647 dst->sdf = NULL;
648 dst->sdfpitch = 0;
649 dst->sdfx = 0;
650 dst->sdfy = 0;
651 dst->sdfw = 0;
652 dst->sdfh = 0;
653 order[numchars++] = dst;
654 if(!numgen) printf("tessfont: generating %d", dst->code);
655 else printf(" %d", dst->code);
656 numgen += dst->code >= 100 ? 4 : (dst->code >= 10 ? 3 : 2);
657 if(numgen > 50) { printf("\n"); numgen = 0; }
658 gensdf(dst);
659 }
660 if(numgen) printf("\n");
661 qsort(order, numchars, sizeof(order[0]), sortchars);
662 for(i = 0; i < numchars;)
663 {
664 struct fontchar *dst;
665 int j, k, trial0, prevscore, dstscore, fitscore;
666 for(trial0 = trial, prevscore = -1; (trial -= 2) >= trial0-512;)
667 {
668 int g, fw = rw, fh = rh, fy = ry, curscore = 0, reused = 0;
669 for(j = i; j < numchars; j++)
670 {
671 dst = order[j];
672 if(dst->tex >= 0 || dst->tex <= trial) continue;
673 g = groupchar(dst->uni);
674 dstscore = scorechar(dst, pad, tw, th, fw, fh, fy);
675 for(k = j; k < numchars; k++)
676 {
677 struct fontchar *fit = order[k];
678 if(fit->tex >= 0 || fit->tex <= trial) continue;
679 if(fit->tex >= trial0 && groupchar(fit->uni) != g) break;
680 fitscore = scorechar(fit, pad, tw, th, fw, fh, fy);
681 if(fitscore < dstscore || (fitscore == dstscore && fit->sdfh > dst->sdfh))
682 {
683 dst = fit;
684 dstscore = fitscore;
685 }
686 }
687 if(fw + dst->sdfw > tw)
688 {
689 fy += fh + pad;
690 fw = fh = 0;
691 }
692 if(fy + dst->sdfh > th)
693 {
694 fy = fw = fh = 0;
695 if(curscore > 0) break;
696 }
697 if(dst->tex >= trial+1 && dst->tex <= trial+2) { dst->tex = trial; reused++; }
698 else dst->tex = trial;
699 fw += dst->sdfw + pad;
700 fh = imax(fh, dst->sdfh);
701 if(dst != order[j]) --j;
702 curscore++;
703 }
704 if(reused < prevscore || curscore <= prevscore) break;
705 prevscore = curscore;
706 }
707 for(; i < numchars; i++)
708 {
709 dst = order[i];
710 if(dst->tex >= 0) continue;
711 dstscore = scorechar(dst, pad, tw, th, rw, rh, ry);
712 for(j = i; j < numchars; j++)
713 {
714 struct fontchar *fit = order[j];
715 if(fit->tex < trial || fit->tex > trial+2) continue;
716 fitscore = scorechar(fit, pad, tw, th, rw, rh, ry);
717 if(fitscore < dstscore || (fitscore == dstscore && fit->sdfh > dst->sdfh))
718 {
719 dst = fit;
720 dstscore = fitscore;
721 }
722 }
723 if(dst->tex < trial || dst->tex > trial+2) break;
724 if(rw + dst->sdfw > tw)
725 {
726 ry += rh + pad;
727 rw = rh = 0;
728 }
729 if(ry + dst->sdfh > th)
730 {
731 ry = rw = rh = 0;
732 numtex++;
733 }
734 dst->tex = numtex;
735 dst->x = rw;
736 dst->y = ry;
737 rw += dst->sdfw + pad;
738 rh = imax(rh, dst->sdfh);
739 y1 = fmin(y1, dst->top - dst->h);
740 y2 = fmax(y2, dst->top);
741 x1 = fmin(x1, dst->left);
742 x2 = fmax(x2, dst->left + dst->w);
743 w2 = fmax(w2, dst->w);
744 h2 = fmax(h2, dst->h);
745 if(dst != order[i]) --i;
746 }
747 }
748 if(rh > 0) numtex++;
749 if(sh <= 0) sh = (int)ceil(y2 - y1);
750 if(sw <= 0) sw = sh/3;
751 endtime = time(NULL);
752 writetexs(argv[2], chars, numchars, numtex, tw, th);
753 writecfg(argv[2], chars, numchars, x1, y1, x2, y2, sw, sh, argc, argv);
754 for(i = 0; i < numchars; i++)
755 {
756 struct fontchar *c = &chars[i];
757 FT_Done_Glyph((FT_Glyph)c->glyph);
758 if(c->sdf) free(c->sdf);
759 }
760 FT_Done_FreeType(l);
761 printf("tessfont: (%g, %g) .. (%g, %g) = (%g, %g) / (%g, %g), %d texs, %d secs\n", x1, y1, x2, y2, x2 - x1, y2 - y1, w2, h2, numtex, (int)(endtime - starttime));
762 return EXIT_SUCCESS;
763 }
764
765