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