6 #include "SDL/SDL_ttf.h"
7 #include "SDL/SDL_image.h"
10 #define snprintf _snprintf
11 #pragma message("You are using a broken and outdated compiler. Do not expect this to work.")
14 void warn(const char *fmt, ...)
19 vfprintf(stderr, fmt, list);
21 fputs(strerror(e), stderr);
25 void warnx(const char *fmt, ...)
29 vfprintf(stderr, fmt, list);
33 void err(int ex, const char *fmt, ...)
38 vfprintf(stderr, fmt, list);
40 fputs(strerror(e), stderr);
45 void errx(int ex, const char *fmt, ...)
49 vfprintf(stderr, fmt, list);
54 void Image_WriteTGABGRA (const char *filename, int width, int height, const unsigned char *data)
57 unsigned char *buffer, *out;
58 const unsigned char *in, *end;
61 buffer = (unsigned char *)malloc(width*height*4 + 18);
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;
70 for (y = 3;y < width*height*4;y += 4)
74 if (y < width*height*4)
76 /* save the alpha channel */
77 buffer[16] = 32; /* pixel size */
78 buffer[17] = 8; /* 8 bits of alpha */
80 /* flip upside down */
82 for (y = height - 1;y >= 0;y--)
84 memcpy(out, data + y * width * 4, width * 4);
90 /* save only the color channels */
91 buffer[16] = 24; /* pixel size */
92 buffer[17] = 0; /* 8 bits of alpha */
94 /* truncate bgra to bgr and flip upside down */
96 for (y = height - 1;y >= 0;y--)
98 in = data + y * width * 4;
100 for (;in < end;in += 4)
109 f = fopen(filename, "wb");
112 if(fwrite(buffer, out - buffer, 1, f) != 1)
121 * Return the pixel value at (x, y)
122 * NOTE: The surface must be locked before calling this!
124 Uint32 getpixel(SDL_Surface *surface, int x, int y)
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;
138 if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
139 return p[0] << 16 | p[1] << 8 | p[2];
141 return p[0] | p[1] << 8 | p[2] << 16;
147 return 0; /* shouldn't happen, but avoids warnings */
152 * Set the pixel at (x, y) to the given value
153 * NOTE: The surface must be locked before calling this!
155 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
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;
167 *(Uint16 *)p = pixel;
171 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
172 p[0] = (pixel >> 16) & 0xff;
173 p[1] = (pixel >> 8) & 0xff;
177 p[1] = (pixel >> 8) & 0xff;
178 p[2] = (pixel >> 16) & 0xff;
183 *(Uint32 *)p = pixel;
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)))
194 Uint32 getpixelfilter(SDL_Surface *src, SDL_PixelFormat *fmt, int x, int y, double A, double B, double C)
196 double r, g, b, a, f;
197 Uint8 pr, pg, pb, pa;
199 int imax = (int) BLURFUNCIMAX(A,B);
201 /* 1. calculate blackened blurred image */
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)
208 SDL_GetRGBA(getpixel(src, x+j, y+i), src->format, &pr, &pg, &pb, &pa);
209 f = BLURFUNC(i*i+j*j, A, B);
221 /* 2. overlap it with the actual image again */
222 if(y >= 0 && y < src->h && x >= 0 && x < src->w)
224 SDL_GetRGBA(getpixel(src, x, y), src->format, &pr, &pg, &pb, &pa);
226 f = a + pa - (a * pa) / 255L;
243 r = g = b = MAX(0, 255 - C * (255 - a));
245 r = g = b = MAX(0, 255 + C * a);
247 return SDL_MapRGBA(fmt, (unsigned char) r, (unsigned char) g, (unsigned char) b, (unsigned char) a);
250 void blitfilter(SDL_Surface *src, SDL_Surface *dest, int x0, int y0, double A, double B, double C)
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);
256 SDL_LockSurface(src);
257 SDL_LockSurface(dest);
261 xb = x0 + src->w + d;
262 yb = y0 + src->h + d;
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);
275 int mapFont(int d, char *c_)
277 unsigned char *c = (unsigned char *) c_;
279 return ((*c >= 0x20 && *c <= 0x7E) || (*c >= 0xA0 && *c <= 0xFE)) ? 0 : -1;
280 if(*c >= 0x20 && *c <= 0x7E)
282 if(*c >= 0xA0 && *c <= 0xAF)
287 if(*c >= 0xB0 && *c <= 0xB9)
292 if(*c >= 0xBA && *c <= 0xDF)
297 if(*c >= 0xE0 && *c <= 0xFE)
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?
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
315 void StretchBlit(SDL_Surface *dst, SDL_Surface *src, SDL_Rect *drec, SDL_Rect *srec)
317 unsigned int freeSource;
328 /* To avoid copying copied pixels, that would suck :) */
329 src = SDL_ConvertSurface(dst, dst->format, dst->flags);
334 drec = &dst->clip_rect;
336 srec = &src->clip_rect;
338 SDL_LockSurface(dst);
339 SDL_LockSurface(src);
341 scaleX = (double)srec->w / (double)drec->w;
342 scaleY = (double)srec->h / (double)drec->h;
344 for(y = drec->y; y < (drec->y + drec->h); ++y)
350 for(x = drec->x; x < (drec->x + drec->w); ++x)
353 double dfromX, dfromY, dtoX, dtoY;
354 int fromX, fromY, toX, toY;
356 unsigned int r, g, b, a, ar, ag, ab;
361 /* dx, dy relative to the drec start */
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;
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!
382 /* Now get all the pixels and merge them together... */
384 r = g = b = a = ar = ag = ab = 0;
386 printf("%i %i - %f %f\n", fromX, toX, dfromX, dtoX);*/
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) {
409 if(toY - fromY == 1 && drec->h > srec->h) {
424 for(j = fromY; j <= toY; ++j)
428 if((j+srec->y) >= src->h)
430 for(i = fromX; i <= toX; ++i)
432 Uint8 pr, pg, pb, pa;
433 Uint16 par, pag, pab;
438 if((i+srec->x) >= src->w)
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;
455 iinc = (int) (inc * 256);
468 /* printf("COLOR VALUE: %i, %i, %i, %i \t COUNT: %f\n", r, g, b, a, count); */
484 putpixel(dst, x, y, SDL_MapRGBA(dst->format, (Uint8)r, (Uint8)g, (Uint8)b, (Uint8)a));
488 SDL_UnlockSurface(dst);
489 SDL_UnlockSurface(src);
492 SDL_FreeSurface(src);
495 void StretchDown(SDL_Surface *srfc, int x, int y, int w, int h, int wtarget)
497 /* @"#$ SDL has no StretchBlit */
498 /* this one is slow, but at least I know how it works */
500 unsigned int *stretchedline = (unsigned int *) alloca(8 * wtarget * sizeof(unsigned int)); /* ra ga ba r g b a n */
501 SDL_LockSurface(srfc);
503 for(r = y; r < y + h; ++r)
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)
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;
522 for(c = 0; c < wtarget; ++c)
524 unsigned int *p = &stretchedline[8 * c];
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]));
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]));
530 for(c = wtarget; c < w; ++c)
531 putpixel(srfc, x + c, r, SDL_MapRGBA(srfc->format, 0, 0, 0, 0));
534 SDL_UnlockSurface(srfc);
537 int GetBoundingBox(SDL_Surface *surf, const SDL_Rect *inbox, SDL_Rect *outbox)
539 int bx = -1, by = -1; /* start */
540 /* int bw = 0, bh = 0; */
541 int ex = -1, ey = -1; /* end */
543 for(cx = inbox->x; cx < inbox->x + inbox->w; ++cx)
545 for(cy = inbox->y; cy < inbox->y + inbox->h; ++cy)
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? */
559 if(cx < bx) /* a pixel more on the left */
561 if(cy < by) /* a pixel more above... */
563 if(cx > ex) /* a pixel on the right */
565 if(cy > ey) /* a pixel on the bottom :) */
575 outbox->w = (ex - bx + 1);
576 outbox->h = (ey - by + 1);
580 int main(int argc, char **argv)
583 SDL_Surface *conchars, *conchars0;
586 SDL_Color white = {255, 255, 255, 255};
588 int maxAscent, maxDescent, maxWidth;
592 const char *infilename;
596 const char *outfilename;
606 char widthfilename[512];
611 errx(1, "Usage: %s infile.tga topref bottomref cellheight outfile.tga font.ttf fontCOOL.ttf fontLCD.ttf blurA blurB blurColors\n", argv[0]);
613 infilename = argv[1];
614 referenceTop = atoi(argv[2]);
615 referenceBottom = atoi(argv[3]);
616 cell = atoi(argv[4]);
617 outfilename = argv[5];
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));
637 f = BLURFUNC(i*i+j*j, 1, B);
648 fprintf(stderr, "A=%f B=%f (using %d pixels)\n", A, B, (int) BLURFUNCIMAX(A, B));
650 snprintf(widthfilename, sizeof(widthfilename), "%.*s.width", (int)strlen(outfilename) - 4, outfilename);
652 border=(int) BLURFUNCIMAX(A, B);
655 errx(1, "SDL_Init failed");
658 errx(1, "TTF_Init failed: %s", TTF_GetError());
660 conchars0 = IMG_Load(infilename);
662 errx(1, "IMG_Load failed: %s", IMG_GetError());
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");
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);
676 for(currentSize = cell * 2; currentSize; --currentSize)
678 fonts[0] = TTF_OpenFont(font0, currentSize);
680 errx(1, "TTF_OpenFont %s failed: %s", font0, TTF_GetError());
682 if(strcmp(font0, font1) || strcmp(font0, font2))
686 fonts[1] = TTF_OpenFont(font1, currentSize);
688 warnx("TTF_OpenFont %s failed: %s", font1, TTF_GetError());
695 fonts[2] = TTF_OpenFont(font2, currentSize);
697 warnx("TTF_OpenFont %s failed: %s", font2, TTF_GetError());
706 fonts[1] = fonts[2] = fonts[0];
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); */
715 for(i = 0; i < 256; ++i)
718 int fntid = mapFont(differentFonts, &str[0]);
719 str[0] = i; str[1] = 0;
724 glyph = TTF_RenderText_Blended(fonts[fntid], str, white);
726 errx(1, "TTF_RenderText_Blended %d failed: %s", i, TTF_GetError());
728 maxWidth = MAX(maxWidth, glyph->w);
734 if(GetBoundingBox(glyph, &in, &out))
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);
743 if(desc > maxDescent)
747 SDL_FreeSurface(glyph);
753 if(border + maxAscent + 1 + maxDescent + border <= cell)
754 if(border + maxWidth + border <= cell)
760 TTF_CloseFont(fonts[2]);
762 TTF_CloseFont(fonts[1]);
764 TTF_CloseFont(fonts[0]);
767 errx(1, "Sorry, no suitable size found.");
768 fprintf(stderr, "Using font size %d (%d + 1 + %d)\n", currentSize, maxAscent, maxDescent);
770 isfixed = TTF_FontFaceIsFixedWidth(fonts[0]);
771 if(getenv("FORCE_FIXED"))
774 /* TODO convert conchars to BGRA (so the TGA writer can reliably use it) */
776 transparent = SDL_MapRGBA(conchars->format, 255, 0, 255, 0);
778 widthfile = fopen(widthfilename, "w");
780 err(1, "fopen widthfile");
781 fprintf(widthfile, "extraspacing %f\n", 0.0);
782 fprintf(widthfile, "scale %f\n", 1.0);
784 for(i = 0; i < 256; ++i)
789 char str[2]; str[0] = i; str[1] = 0;
792 fprintf(widthfile, "\n");
794 fntid = mapFont(differentFonts, &str[0]);
795 if(fntid < 0 || !fonts[fntid])
798 int destTop, destBottom;
800 src.x = cell * (i % 16);
801 src.y = cell * (i / 16);
808 glyph = SDL_CreateRGBSurface(SDL_SWSURFACE, cell, cell, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
809 SDL_FillRect(glyph, &src2, transparent);
812 /* referenceTop -> (cell - (maxAscent + 1 + maxDescent)) / 2 */
813 /* referenceBottom -> (cell - (maxAscent + 1 + maxDescent)) / 2 + maxAscent */
815 destTop = (cell - (maxAscent + 1 + maxDescent)) / 2;
816 destBottom = (cell - (maxAscent + 1 + maxDescent)) / 2 + maxAscent;
819 /* x' = x / cell * h + y */
821 /* destTop = referenceTop / cell * h + y */
822 /* destBottom = referenceBottom / cell * h + y */
825 dest.y = (int) ((double) (destBottom * referenceTop - destTop * referenceBottom) / (double) (referenceTop - referenceBottom));
826 dest.h = (int) (cell * (double) (destBottom - destTop) / (double) (referenceBottom - referenceTop));
832 if(dest.w > glyph->w)
834 if(dest.y + dest.h > glyph->h)
835 dest.h = glyph->h - dest.y;
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);
849 fprintf(stderr, "glyph %d...\n", i);
851 glyph = TTF_RenderText_Blended(fonts[fntid], str, white);
853 errx(1, "TTF_RenderText_Blended %d failed: %s", i, TTF_GetError());
855 w = border + glyph->w + border;
856 h = border + glyph->h + border;
858 warnx("sorry, this font contains a character that is too wide... output will be borked");
860 dest.x = cell * (i % 16);
861 dest.y = cell * (i / 16);
864 SDL_FillRect(conchars, &dest, transparent);
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);
870 SDL_FreeSurface(glyph);
872 if(isfixed && w > border + maxWidth + border)
874 StretchDown(conchars, cell * (i % 16), cell * (i / 16), w, cell, border + maxWidth + border);
875 fprintf(widthfile, "%f ", (border + maxWidth + border) / (double) cell);
878 fprintf(widthfile, "%f ", (isfixed ? border + maxWidth + border : w) / (double) cell);
881 fprintf(widthfile, "\n");
884 fprintf(stderr, "Writing...\n");
886 Image_WriteTGABGRA(outfilename, conchars->w, conchars->h, (unsigned char *) conchars->pixels);
888 SDL_FreeSurface(conchars);