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