]> git.xonotic.org Git - xonotic/xonotic.git/blob - misc/tools/ttf2conchars/ttf2conchars.c
Merge branch 'master' of git://git.xonotic.org/xonotic/xonotic
[xonotic/xonotic.git] / misc / tools / ttf2conchars / ttf2conchars.c
1 #include <stdio.h>
2 #include <errno.h>
3 #include <stdarg.h>
4 #include <math.h>
5 #include "SDL/SDL.h" 
6 #include "SDL/SDL_ttf.h" 
7 #include "SDL/SDL_image.h" 
8
9 #ifdef _MSC_VER
10 #define snprintf _snprintf
11 #pragma message("You are using a broken and outdated compiler. Do not expect this to work.")
12 #endif
13
14 void warn(const char *fmt, ...)
15 {
16         va_list list;
17         int e = errno;
18         va_start(list, fmt);
19         vfprintf(stderr, fmt, list);
20         fputs(": ", stderr);
21         fputs(strerror(e), stderr);
22         fputs("\n", stderr);
23 }
24
25 void warnx(const char *fmt, ...)
26 {
27         va_list list;
28         va_start(list, fmt);
29         vfprintf(stderr, fmt, list);
30         fputs("\n", stderr);
31 }
32
33 void err(int ex, const char *fmt, ...)
34 {
35         va_list list;
36         int e = errno;
37         va_start(list, fmt);
38         vfprintf(stderr, fmt, list);
39         fputs(": ", stderr);
40         fputs(strerror(e), stderr);
41         fputs("\n", stderr);
42         exit(ex);
43 }
44
45 void errx(int ex, const char *fmt, ...)
46 {
47         va_list list;
48         va_start(list, fmt);
49         vfprintf(stderr, fmt, list);
50         fputs("\n", stderr);
51         exit(ex);
52 }
53
54 void Image_WriteTGABGRA (const char *filename, int width, int height, const unsigned char *data)
55 {
56     int y;
57     unsigned char *buffer, *out;
58     const unsigned char *in, *end;
59         FILE *f;
60
61     buffer = (unsigned char *)malloc(width*height*4 + 18);
62
63     memset (buffer, 0, 18);
64     buffer[2] = 2;      /*  uncompressed type */
65     buffer[12] = (width >> 0) & 0xFF;
66     buffer[13] = (width >> 8) & 0xFF;
67     buffer[14] = (height >> 0) & 0xFF;
68     buffer[15] = (height >> 8) & 0xFF;
69
70     for (y = 3;y < width*height*4;y += 4)
71         if (data[y] < 255)
72             break;
73
74     if (y < width*height*4)
75     {   
76         /*  save the alpha channel */
77         buffer[16] = 32;    /*  pixel size */
78         buffer[17] = 8; /*  8 bits of alpha */
79
80         /*  flip upside down */
81         out = buffer + 18;
82         for (y = height - 1;y >= 0;y--)
83         {   
84             memcpy(out, data + y * width * 4, width * 4);
85             out += width*4;
86         }
87     }
88     else
89     {   
90         /*  save only the color channels */
91         buffer[16] = 24;    /*  pixel size */
92         buffer[17] = 0; /*  8 bits of alpha */
93
94         /*  truncate bgra to bgr and flip upside down */
95         out = buffer + 18;
96         for (y = height - 1;y >= 0;y--)
97         {
98             in = data + y * width * 4;
99             end = in + width * 4;
100             for (;in < end;in += 4)
101             {
102                 *out++ = in[0];
103                 *out++ = in[1];
104                 *out++ = in[2];
105             }
106         }
107     }
108
109         f = fopen(filename, "wb");
110         if(!f)
111                 err(1, "WriteTGA");
112         if(fwrite(buffer, out - buffer, 1, f) != 1)
113                 err(1, "WriteTGA");
114         if(fclose(f))
115                 err(1, "WriteTGA");
116
117     free(buffer);
118 }
119
120 /*
121  * Return the pixel value at (x, y)
122  * NOTE: The surface must be locked before calling this!
123  */
124 Uint32 getpixel(SDL_Surface *surface, int x, int y)
125 {
126     int bpp = surface->format->BytesPerPixel;
127     /* Here p is the address to the pixel we want to retrieve */
128     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
129
130     switch(bpp) {
131     case 1:
132         return *p;
133
134     case 2:
135         return *(Uint16 *)p;
136
137     case 3:
138         if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
139             return p[0] << 16 | p[1] << 8 | p[2];
140         else
141             return p[0] | p[1] << 8 | p[2] << 16;
142
143     case 4:
144         return *(Uint32 *)p;
145
146     default:
147         return 0;       /* shouldn't happen, but avoids warnings */
148     }
149 }
150
151 /*
152  * Set the pixel at (x, y) to the given value
153  * NOTE: The surface must be locked before calling this!
154  */
155 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
156 {
157     int bpp = surface->format->BytesPerPixel;
158     /* Here p is the address to the pixel we want to set */
159     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
160
161     switch(bpp) {
162     case 1:
163         *p = pixel;
164         break;
165
166     case 2:
167         *(Uint16 *)p = pixel;
168         break;
169
170     case 3:
171         if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
172             p[0] = (pixel >> 16) & 0xff;
173             p[1] = (pixel >> 8) & 0xff;
174             p[2] = pixel & 0xff;
175         } else {
176             p[0] = pixel & 0xff;
177             p[1] = (pixel >> 8) & 0xff;
178             p[2] = (pixel >> 16) & 0xff;
179         }
180         break;
181
182     case 4:
183         *(Uint32 *)p = pixel;
184         break;
185     }
186 }
187
188 #define MIN(a,b) (((a)<(b))?(a):(b))
189 #define MAX(a,b) (((a)>(b))?(a):(b))
190 #define BOUND(a,b,c) MAX(a,MIN(b,c))
191 #define BLURFUNC(d,A,B) A-B*(d)
192 #define BLURFUNCIMAX(A,B) ceil(sqrt((A)/(B)))
193
194 Uint32 getpixelfilter(SDL_Surface *src, SDL_PixelFormat *fmt, int x, int y, double A, double B, double C)
195 {
196         double r, g, b, a, f;
197         Uint8 pr, pg, pb, pa;
198         int i, j;
199         int imax = (int) BLURFUNCIMAX(A,B);
200
201         /*  1. calculate blackened blurred image */
202         a = 0;
203         for(i=-imax; i<=imax; ++i)
204                 if(y+i >= 0 && y+i < src->h)
205                         for(j=-imax; j<=imax; ++j)
206                                 if(x+j >= 0 && x+j < src->w)
207                                 {
208                                         SDL_GetRGBA(getpixel(src, x+j, y+i), src->format, &pr, &pg, &pb, &pa);
209                                         f = BLURFUNC(i*i+j*j, A, B);
210                                         f = MAX(0, f);
211
212                                         if(C == 0)
213                                                 a = MAX(a, pa * f);
214                                         else
215                                                 a = a + pa * f;
216                                 }
217         a = MIN(a, 255);
218
219         if(C == 0)
220         {
221                 /*  2. overlap it with the actual image again */
222                 if(y >= 0 && y < src->h && x >= 0 && x < src->w)
223                 {
224                         SDL_GetRGBA(getpixel(src, x, y), src->format, &pr, &pg, &pb, &pa);
225
226                         f = a + pa - (a * pa) / 255L;
227
228                         r = pr * pa / f;
229                         g = pg * pa / f;
230                         b = pb * pa / f;
231
232                         a = f;
233                 }
234                 else
235                 {
236                         r = 0;
237                         g = 0;
238                         b = 0;
239                         a = a;
240                 }
241         }
242         else if(C > 0)
243                 r = g = b = MAX(0, 255 - C * (255 - a));
244         else if(C < 0)
245                 r = g = b = MAX(0, 255 + C * a);
246
247         return SDL_MapRGBA(fmt, (unsigned char) r, (unsigned char) g, (unsigned char) b, (unsigned char) a);
248 }
249
250 void blitfilter(SDL_Surface *src, SDL_Surface *dest, int x0, int y0, double A, double B, double C)
251 {
252         /*  note: x0, y0 is the origin of the UNFILTERED image; it is "transparently" expanded by a BLURFUNCIMAX. */
253         int x, y, d, xa, ya, xb, yb;
254         d = (int) BLURFUNCIMAX(A, B);
255
256         SDL_LockSurface(src);
257         SDL_LockSurface(dest);
258
259         xa = x0 - d;
260         ya = y0 - d;
261         xb = x0 + src->w + d;
262         yb = y0 + src->h + d;
263
264         if(xa < 0) xa = 0;
265         if(ya < 0) ya = 0;
266         if(xa >= dest->w) xa = dest->w - 1;
267         if(ya >= dest->h) ya = dest->h - 1;
268         for(y = ya; y <= yb; ++y)
269                 for(x = xa; x <= xb; ++x)
270                         putpixel(dest, x, y, getpixelfilter(src, dest->format, x - x0, y - y0, A, B, C));
271         SDL_UnlockSurface(dest);
272         SDL_UnlockSurface(src);
273 }
274
275 int mapFont(int d, char *c_)
276 {
277         unsigned char *c = (unsigned char *) c_;
278         if(!d)
279                 return ((*c >= 0x20 && *c <= 0x7E) || (*c >= 0xA0 && *c <= 0xFE)) ? 0 : -1;
280         if(*c >= 0x20 && *c <= 0x7E)
281                 return 0;
282         if(*c >= 0xA0 && *c <= 0xAF)
283         {
284                 *c &= 0x7F;
285                 return 1;
286         }
287         if(*c >= 0xB0 && *c <= 0xB9)
288         {
289                 *c &= 0x7F;
290                 return 2;
291         }
292         if(*c >= 0xBA && *c <= 0xDF)
293         {
294                 *c &= 0x7F;
295                 return 1; /*  cool */
296         }
297         if(*c >= 0xE0 && *c <= 0xFE)
298         {
299                 *c &= 0x5F;
300                 return 2; /*  lcd */
301         }
302         return -1;
303 }
304
305 /**
306  * @brief Blit a surface onto another and stretch it.
307  * With a 4.2 gcc you can use -fopenmp :)
308  * You might want to add some linear fading for scaling up?
309  *
310  * @param dst Destination surface
311  * @param src Source surface, if NULL, the destination surface is used
312  * @param drec The target area
313  * @param srec The source area, if NULL, then you suck :P
314  */
315 void StretchBlit(SDL_Surface *dst, SDL_Surface *src, SDL_Rect *drec, SDL_Rect *srec)
316 {
317         unsigned int freeSource;
318         int x, y;
319         double scaleX;
320         double scaleY;
321         
322
323         if(!src)
324                 src = dst;
325
326         freeSource = 0;
327         if(src == dst) {
328                 /*  To avoid copying copied pixels, that would suck :) */
329                 src = SDL_ConvertSurface(dst, dst->format, dst->flags);
330                 freeSource = 1;
331         }
332
333         if(!drec)
334                 drec = &dst->clip_rect;
335         if(!srec)
336                 srec = &src->clip_rect;
337
338         SDL_LockSurface(dst);
339         SDL_LockSurface(src);
340
341         scaleX = (double)srec->w / (double)drec->w;
342         scaleY = (double)srec->h / (double)drec->h;
343         
344         for(y = drec->y; y < (drec->y + drec->h); ++y)
345         {
346                 int dy;
347                 if(y >= dst->h)
348                         break;
349                 dy = y - drec->y;
350                 for(x = drec->x; x < (drec->x + drec->w); ++x)
351                 {
352                         int dx;
353                         double dfromX, dfromY, dtoX, dtoY;
354                         int fromX, fromY, toX, toY;
355                         int i, j;
356                         unsigned int r, g, b, a, ar, ag, ab;
357                         unsigned int count;
358
359                         if(x >= dst->w)
360                                 break;
361                         /*  dx, dy relative to the drec start */
362                         dx = x - drec->x;
363
364                         /*  Get the pixel range which represents the current pixel */
365                         /*  When scaling down this should be a rectangle :) */
366                         /*  Otherwise it's just 1 pixel anyway, from==to then */
367                         dfromX = dx * scaleX;
368                         dfromY = dy * scaleY;
369                         dtoX = (dx+1) * scaleX;
370                         dtoY = (dy+1) * scaleY;
371                         /*  The first and last one usually aren't 100% within this space */
372                         fromX = (int)dfromX; dfromX = 1.0 - (dfromX - fromX); /*  invert the from percentage */
373                         fromY = (int)dfromY; dfromY = 1.0 - (dfromY - fromY);
374                         toX = (int)dtoX; dtoX -= toX; /*  this one is ok */
375                         toY = (int)dtoY; dtoY -= toY;
376                                                 
377                         /* Short explanation:
378                          * FROM is where to START, so when it's 5.7, then 30% of the 5th pixel is to be used
379                          * TO is where it ENDS, so if it's 8.4, then 40% of the 9th pixel is to be used!
380                          */
381                                                 
382                         /*  Now get all the pixels and merge them together... */
383                         count = 0;
384                         r = g = b = a = ar = ag = ab = 0;
385                         /*if(drec->w > 1024)
386                           printf("%i %i - %f %f\n", fromX, toX, dfromX, dtoX);*/
387
388                         /*  when going from one to the next there's usually one */
389                         /*  situation where the left pixel has a value of 0.1something and */
390                         /*  the right one of 0 */
391                         /*  so adjust the values here */
392                         /*  otherwise we get lines in the image with the original color */
393                         /*  of the left pixel */
394                         if(toX - fromX == 1 && drec->w > srec->w) {
395                                 dfromX = 1.0 - dtoX;
396                                 ++fromX;
397                         }
398                         if(fromX == toX) {
399                                 dfromX -= 0.5;
400                                 if(dfromX > 0.0) {
401                                         --fromX;
402                                         dtoX = 1.0-dfromX;
403                                 } else {
404                                         ++toX;
405                                         dtoX = -dfromX;
406                                         dfromX = 1.0-dtoX;
407                                 }
408                         }
409                         if(toY - fromY == 1 && drec->h > srec->h) {
410                                 dfromY = 1.0 - dtoY;
411                                 ++fromY;
412                         }
413                         if(fromY == toY) {
414                                 dfromY -= 0.5;
415                                 if(dfromY > 0.0) {
416                                         --fromY;
417                                         dtoY = 1.0-dfromY;
418                                 } else {
419                                         ++toY;
420                                         dtoY = -dfromY;
421                                         dfromY = 1.0-dtoY;
422                                 }
423                         }
424                         for(j = fromY; j <= toY; ++j)
425                         {
426                                 if(j < 0)
427                                         continue;
428                                 if((j+srec->y) >= src->h)
429                                         break;
430                                 for(i = fromX; i <= toX; ++i)
431                                 {
432                                         Uint8 pr, pg, pb, pa;
433                                         Uint16 par, pag, pab;
434                                         double inc = 1;
435                                         int iinc;
436                                         if(x < 0)
437                                                 continue;
438                                         if((i+srec->x) >= src->w)
439                                                 break;
440
441                                         SDL_GetRGBA(getpixel(src, i + srec->x, j + srec->y), src->format, &pr, &pg, &pb, &pa);
442                                         par = pa * (unsigned int)pr;
443                                         pag = pa * (unsigned int)pg;
444                                         pab = pa * (unsigned int)pb;
445
446                                         if(i == fromX)
447                                                 inc *= dfromX;
448                                         if(j == fromY)
449                                                 inc *= dfromY;
450                                         if(i == (toX))
451                                                 inc *= dtoX;
452                                         if(j == (toY))
453                                                 inc *= dtoY;
454
455                                         iinc = (int) (inc * 256);
456
457                                         r += (pr * iinc);
458                                         g += (pg * iinc);
459                                         b += (pb * iinc);
460                                         ar += (par * iinc);
461                                         ag += (pag * iinc);
462                                         ab += (pab * iinc);
463                                         a += (pa * iinc);
464                                         /* ++count; */
465                                         count += iinc;
466                                 }
467                         }
468                         /* printf("COLOR VALUE: %i, %i, %i, %i \t COUNT: %f\n", r, g, b, a, count); */
469                         if(a)
470                         {
471                                 r = ar / a;
472                                 g = ag / a;
473                                 b = ab / a;
474                                 a /= count;
475                         }
476                         else
477                         {
478                                 r /= count;
479                                 g /= count;
480                                 b /= count;
481                                 a /= count;
482                         }
483
484                         putpixel(dst, x, y, SDL_MapRGBA(dst->format, (Uint8)r, (Uint8)g, (Uint8)b, (Uint8)a));
485                 }
486         }
487         
488         SDL_UnlockSurface(dst);
489         SDL_UnlockSurface(src);
490
491         if(freeSource)
492                 SDL_FreeSurface(src);
493 }
494
495 void StretchDown(SDL_Surface *srfc, int x, int y, int w, int h, int wtarget)
496 {
497         /*  @"#$ SDL has no StretchBlit */
498         /*  this one is slow, but at least I know how it works */
499         int r, c;
500         unsigned int *stretchedline = (unsigned int *) alloca(8 * wtarget * sizeof(unsigned int)); /*  ra ga ba r g b a n */
501         SDL_LockSurface(srfc);
502
503         for(r = y; r < y + h; ++r)
504         {
505                 /*  each input pixel is wtarget pixels "worth" */
506                 /* memset(stretchedline, sizeof(stretchedline), 0); */
507                 memset(stretchedline, 0, 8 * wtarget * sizeof(unsigned int));
508                 for(c = 0; c < w * wtarget; ++c)
509                 {
510                         Uint8 pr, pg, pb, pa;
511                         unsigned int *p = &stretchedline[8 * (c / w)];
512                         SDL_GetRGBA(getpixel(srfc, x + c / wtarget, r), srfc->format, &pr, &pg, &pb, &pa);
513                         p[0] += (unsigned int) pr * (unsigned int) pa;
514                         p[1] += (unsigned int) pg * (unsigned int) pa;
515                         p[2] += (unsigned int) pb * (unsigned int) pa;
516                         p[3] += (unsigned int) pr;
517                         p[4] += (unsigned int) pg;
518                         p[5] += (unsigned int) pb;
519                         p[6] += (unsigned int) pa;
520                         p[7] += 1;
521                 }
522                 for(c = 0; c < wtarget; ++c)
523                 {
524                         unsigned int *p = &stretchedline[8 * c];
525                         if(p[6])
526                                 putpixel(srfc, x + c, r, SDL_MapRGBA(srfc->format, p[0] / p[6], p[1] / p[6], p[2] / p[6], p[6] / p[7]));
527                         else
528                                 putpixel(srfc, x + c, r, SDL_MapRGBA(srfc->format, p[3] / p[7], p[4] / p[7], p[5] / p[7], p[6] / p[7]));
529                 }
530                 for(c = wtarget; c < w; ++c)
531                         putpixel(srfc, x + c, r, SDL_MapRGBA(srfc->format, 0, 0, 0, 0));
532         }
533
534         SDL_UnlockSurface(srfc);
535 }
536
537 int GetBoundingBox(SDL_Surface *surf, const SDL_Rect *inbox, SDL_Rect *outbox)
538 {
539         int bx = -1, by = -1; /*  start */
540         /* int bw = 0, bh = 0; */
541         int ex = -1, ey = -1; /*  end */
542         int cx, cy;
543         for(cx = inbox->x; cx < inbox->x + inbox->w; ++cx)
544         {
545                 for(cy = inbox->y; cy < inbox->y + inbox->h; ++cy)
546                 {
547                         Uint8 pr, pg, pb, pa;
548                         SDL_GetRGBA(getpixel(surf, cx, cy), surf->format, &pr, &pg, &pb, &pa);
549                         /*  include colors, or only care about pa? */
550                         if(!pa)
551                                 continue;
552
553                         if(bx < 0) {
554                                 bx = ex = cx;
555                                 by = ey = cy;
556                                 continue;
557                         }
558                         
559                         if(cx < bx) /*  a pixel more on the left */
560                                 bx = cx;
561                         if(cy < by) /*  a pixel more above... */
562                             by = cy;
563                         if(cx > ex) /*  a pixel on the right */
564                                 ex = cx;
565                         if(cy > ey) /*  a pixel on the bottom :) */
566                                 ey = cy;
567                 }
568         }
569
570         if(ex < 0)
571                 return 0;
572
573         outbox->x = bx;
574         outbox->y = by;
575         outbox->w = (ex - bx + 1);
576         outbox->h = (ey - by + 1);
577         return 1;
578 }
579
580 int main(int argc, char **argv)
581 {
582         SDL_Rect in, out;
583         SDL_Surface *conchars, *conchars0;
584         SDL_Surface *glyph;
585         TTF_Font *fonts[3];
586         SDL_Color white = {255, 255, 255, 255};
587         Uint32 transparent;
588         int maxAscent, maxDescent, maxWidth;
589         int i, j;
590         int currentSize;
591         int isfixed;
592         const char *infilename;
593         int referenceTop;
594         int referenceBottom;
595         int cell;
596         const char *outfilename;
597         const char *font0;
598         const char *font1;
599         const char *font2;
600         double A;
601         double B;
602         double C;
603         int differentFonts;
604         int d;
605         double f = 0, a = 0;
606         char widthfilename[512];
607         int border;
608         FILE *widthfile;
609
610         if(argc != 12)
611                 errx(1, "Usage: %s infile.tga topref bottomref cellheight outfile.tga font.ttf fontCOOL.ttf fontLCD.ttf blurA blurB blurColors\n", argv[0]);
612
613         infilename = argv[1];
614         referenceTop = atoi(argv[2]);
615         referenceBottom = atoi(argv[3]);
616         cell = atoi(argv[4]);
617         outfilename = argv[5];
618         font0 = argv[6];
619         font1 = argv[7];
620         font2 = argv[8];
621         A = atof(argv[9]);
622         B = atof(argv[10]);
623         C = atof(argv[11]);
624
625         d = (int) BLURFUNCIMAX(1, B);
626         fprintf(stderr, "Translating parameters:\nA=%f B=%f (using %d pixels)\n", A, B, (int) BLURFUNCIMAX(1, B));
627         if(C == 0)
628         {
629                 B = A * B;
630                 A = A * 1;
631         }
632         else
633         {
634                 for(i=-d; i<=d; ++i)
635                         for(j=-d; j<=d; ++j)
636                         {
637                                 f = BLURFUNC(i*i+j*j, 1, B);
638                                 f = MAX(0, f);
639
640                                 if(C == 0)
641                                         a = MAX(a, f);
642                                 else
643                                         a = a + f;
644                         }
645                 B = A/a * B;
646                 A = A/a * 1;
647         }
648         fprintf(stderr, "A=%f B=%f (using %d pixels)\n", A, B, (int) BLURFUNCIMAX(A, B));
649
650         snprintf(widthfilename, sizeof(widthfilename), "%.*s.width", (int)strlen(outfilename) - 4, outfilename);
651
652         border=(int) BLURFUNCIMAX(A, B);
653
654         if(SDL_Init(0) < 0)
655                 errx(1, "SDL_Init failed");
656
657         if(TTF_Init() < 0)
658                 errx(1, "TTF_Init failed: %s", TTF_GetError());
659
660         conchars0 = IMG_Load(infilename);
661         if(!conchars0)
662                 errx(1, "IMG_Load failed: %s", IMG_GetError());
663
664         if(conchars0->w != conchars0->h)
665                 errx(1, "conchars aren't square");
666         if(conchars0->w % 16)
667                 errx(1, "conchars have bad width");
668         
669         conchars = SDL_CreateRGBSurface(SDL_SWSURFACE, cell * 16, cell * 16, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
670         in.x = in.y = out.x = out.y = 0;
671         in.w = in.h = conchars0->w;
672         out.w = out.h = cell * 16;
673         StretchBlit(conchars, conchars0, &out, &in);
674         SDL_FreeSurface(conchars0);
675
676         for(currentSize = cell * 2; currentSize; --currentSize)
677         {
678                 fonts[0] = TTF_OpenFont(font0, currentSize);
679                 if(!fonts[0])
680                         errx(1, "TTF_OpenFont %s failed: %s", font0, TTF_GetError());
681
682                 if(strcmp(font0, font1) || strcmp(font0, font2))
683                 {
684                         if(*font1)
685                         {
686                                 fonts[1] = TTF_OpenFont(font1, currentSize);
687                                 if(!fonts[1])
688                                         warnx("TTF_OpenFont %s failed: %s", font1, TTF_GetError());
689                         }
690                         else
691                                 fonts[1] = NULL;
692
693                         if(*font2)
694                         {
695                                 fonts[2] = TTF_OpenFont(font2, currentSize);
696                                 if(!fonts[2])
697                                         warnx("TTF_OpenFont %s failed: %s", font2, TTF_GetError());
698                         }
699                         else
700                                 fonts[2] = NULL;
701
702                         differentFonts = 1;
703                 }
704                 else
705                 {
706                         fonts[1] = fonts[2] = fonts[0];
707                         differentFonts = 0;
708                 }
709
710                 /* maxAscent = MAX(MAX(TTF_FontAscent(fonts[0]), fonts[1] ? TTF_FontAscent(fonts[1]) : 0), fonts[2] ? TTF_FontAscent(fonts[2]) : 0); */
711                 /* maxDescent = -MIN(MIN(TTF_FontDescent(fonts[0]), fonts[1] ? TTF_FontDescent(fonts[1]) : 0), fonts[2] ? TTF_FontDescent(fonts[2]) : 0); */
712                 maxAscent = 0;
713                 maxDescent = 0;
714                 maxWidth = 0;
715                 for(i = 0; i < 256; ++i)
716                 {
717                         char str[2];
718                         int fntid = mapFont(differentFonts, &str[0]);
719                         str[0] = i; str[1] = 0;
720                         if(fntid < 0)
721                                 continue;
722                         if(!fonts[fntid])
723                                 continue;
724                         glyph = TTF_RenderText_Blended(fonts[fntid], str, white);
725                         if(!glyph)
726                                 errx(1, "TTF_RenderText_Blended %d failed: %s", i, TTF_GetError());
727                         if(fntid == 0)
728                                 maxWidth = MAX(maxWidth, glyph->w);
729
730                         in.x = 0;
731                         in.y = 0;
732                         in.w = glyph->w;
733                         in.h = glyph->h;
734                         if(GetBoundingBox(glyph, &in, &out))
735                         {
736                                 int baseline = TTF_FontAscent(fonts[fntid]);
737                                 int asc = baseline - out.y;
738                                 int desc = (out.y + out.h - 1) - baseline;
739                                 //fprintf(stderr, "%c: rect %d %d %d %d baseline %d\n", (int)i, out.x, out.y, out.w, out.h, baseline);
740                                 //fprintf(stderr, "%c: ascent %d descent %d\n", (int)i, asc, desc);
741                                 if(asc > maxAscent)
742                                         maxAscent = asc;
743                                 if(desc > maxDescent)
744                                         maxDescent = desc;
745                         }
746
747                         SDL_FreeSurface(glyph);
748                 }
749
750                 maxAscent += 10;
751                 maxDescent += 10;
752
753                 if(border + maxAscent + 1 + maxDescent + border <= cell)
754                         if(border + maxWidth + border <= cell)
755                                 break; /*  YEAH */
756
757                 if(differentFonts)
758                 {
759                         if(fonts[2])
760                                 TTF_CloseFont(fonts[2]);
761                         if(fonts[1])
762                                 TTF_CloseFont(fonts[1]);
763                 }
764                 TTF_CloseFont(fonts[0]);
765         }
766         if(!currentSize)
767                 errx(1, "Sorry, no suitable size found.");
768         fprintf(stderr, "Using font size %d (%d + 1 + %d)\n", currentSize, maxAscent, maxDescent);
769
770         isfixed = TTF_FontFaceIsFixedWidth(fonts[0]);
771         if(getenv("FORCE_FIXED"))
772                 isfixed = 1;
773
774         /*  TODO convert conchars to BGRA (so the TGA writer can reliably use it) */
775
776         transparent = SDL_MapRGBA(conchars->format, 255, 0, 255, 0);
777
778         widthfile = fopen(widthfilename, "w");
779         if(!widthfile)
780                 err(1, "fopen widthfile");
781         fprintf(widthfile, "extraspacing %f\n", 0.0);
782         fprintf(widthfile, "scale %f\n", 1.0);
783
784         for(i = 0; i < 256; ++i)
785         {
786                 int w, h;
787                 int fntid;
788                 SDL_Rect dest;
789                 char str[2]; str[0] = i; str[1] = 0;
790
791                 if(i && !(i % 16))
792                         fprintf(widthfile, "\n");
793
794                 fntid = mapFont(differentFonts, &str[0]);
795                 if(fntid < 0 || !fonts[fntid])
796                 {
797                         SDL_Rect src, src2;
798                         int destTop, destBottom;
799
800                         src.x = cell * (i % 16);
801                         src.y = cell * (i / 16);
802                         src.w = cell;
803                         src.h = cell;
804                         src2.x = 0;
805                         src2.y = 0;
806                         src2.w = cell;
807                         src2.h = cell;
808                         glyph = SDL_CreateRGBSurface(SDL_SWSURFACE, cell, cell, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
809                         SDL_FillRect(glyph, &src2, transparent);
810
811                         /*  map: */
812                         /*    referenceTop    -> (cell - (maxAscent + 1 + maxDescent)) / 2 */
813                         /*    referenceBottom -> (cell - (maxAscent + 1 + maxDescent)) / 2 + maxAscent */
814
815                         destTop = (cell - (maxAscent + 1 + maxDescent)) / 2;
816                         destBottom = (cell - (maxAscent + 1 + maxDescent)) / 2 + maxAscent;
817
818                         /*  map is: */
819                         /*    x' = x / cell * h + y */
820                         /*  solve: */
821                         /*    destTop = referenceTop / cell * h + y */
822                         /*    destBottom = referenceBottom / cell * h + y */
823
824                         dest.x = 0;
825                         dest.y = (int) ((double) (destBottom * referenceTop - destTop * referenceBottom) / (double) (referenceTop - referenceBottom));
826                         dest.h = (int) (cell * (double) (destBottom - destTop) / (double) (referenceBottom - referenceTop));
827                         dest.w = dest.h;
828
829                         /*
830                         if(dest.y < 0)
831                                 dest.y = 0;
832                         if(dest.w > glyph->w)
833                                 dest.w = glyph->w;
834                         if(dest.y + dest.h > glyph->h)
835                                 dest.h = glyph->h - dest.y;
836                         */
837
838                         if(isfixed)
839                                 dest.w = border + maxWidth + border;
840                         StretchBlit(glyph, conchars, &dest, &src);
841                         /* SDL_FillRect(conchars, &src, transparent); */
842                         /* SDL_BlitSurface(glyph, &src2, conchars, &src); */
843                         StretchBlit(conchars, glyph, &src, &src2);
844                         SDL_FreeSurface(glyph);
845                         fprintf(widthfile, "%f ", dest.w / (double) cell);
846                         continue;
847                 }
848
849                 fprintf(stderr, "glyph %d...\n", i);
850
851                 glyph = TTF_RenderText_Blended(fonts[fntid], str, white);
852                 if(!glyph)
853                         errx(1, "TTF_RenderText_Blended %d failed: %s", i, TTF_GetError());
854
855                 w = border + glyph->w + border;
856                 h = border + glyph->h + border;
857                 if(w > cell)
858                         warnx("sorry, this font contains a character that is too wide... output will be borked");
859
860                 dest.x = cell * (i % 16);
861                 dest.y = cell * (i / 16);
862                 dest.w = cell;
863                 dest.h = cell;
864                 SDL_FillRect(conchars, &dest, transparent);
865
866                 dest.x += border + (isfixed ? ((border + maxWidth + border - w) / 2) : 0);
867                 dest.y += (cell - (maxAscent + 1 + maxDescent)) / 2 + (maxAscent - TTF_FontAscent(fonts[fntid]));
868                 blitfilter(glyph, conchars, dest.x, dest.y, A, B, C);
869
870                 SDL_FreeSurface(glyph);
871
872                 if(isfixed && w > border + maxWidth + border)
873                 {
874                         StretchDown(conchars, cell * (i % 16), cell * (i / 16), w, cell, border + maxWidth + border);
875                         fprintf(widthfile, "%f ", (border + maxWidth + border) / (double) cell);
876                 }
877                 else
878                         fprintf(widthfile, "%f ", (isfixed ? border + maxWidth + border : w) / (double) cell);
879         }
880
881         fprintf(widthfile, "\n");
882         fclose(widthfile);
883
884         fprintf(stderr, "Writing...\n");
885
886         Image_WriteTGABGRA(outfilename, conchars->w, conchars->h, (unsigned char *) conchars->pixels);
887
888         SDL_FreeSurface(conchars);
889
890         SDL_Quit();
891
892         return 0;
893 }