5 #include "SDL/SDL_ttf.h"
6 #include "SDL/SDL_image.h"
8 void Image_WriteTGABGRA (const char *filename, int width, int height, const unsigned char *data)
11 unsigned char *buffer, *out;
12 const unsigned char *in, *end;
15 buffer = (unsigned char *)malloc(width*height*4 + 18);
17 memset (buffer, 0, 18);
18 buffer[2] = 2; // uncompressed type
19 buffer[12] = (width >> 0) & 0xFF;
20 buffer[13] = (width >> 8) & 0xFF;
21 buffer[14] = (height >> 0) & 0xFF;
22 buffer[15] = (height >> 8) & 0xFF;
24 for (y = 3;y < width*height*4;y += 4)
28 if (y < width*height*4)
30 // save the alpha channel
31 buffer[16] = 32; // pixel size
32 buffer[17] = 8; // 8 bits of alpha
36 for (y = height - 1;y >= 0;y--)
38 memcpy(out, data + y * width * 4, width * 4);
44 // save only the color channels
45 buffer[16] = 24; // pixel size
46 buffer[17] = 0; // 8 bits of alpha
48 // truncate bgra to bgr and flip upside down
50 for (y = height - 1;y >= 0;y--)
52 in = data + y * width * 4;
54 for (;in < end;in += 4)
63 f = fopen(filename, "wb");
66 if(fwrite(buffer, out - buffer, 1, f) != 1)
75 * Return the pixel value at (x, y)
76 * NOTE: The surface must be locked before calling this!
78 Uint32 getpixel(SDL_Surface *surface, int x, int y)
80 int bpp = surface->format->BytesPerPixel;
81 /* Here p is the address to the pixel we want to retrieve */
82 Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
92 if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
93 return p[0] << 16 | p[1] << 8 | p[2];
95 return p[0] | p[1] << 8 | p[2] << 16;
101 return 0; /* shouldn't happen, but avoids warnings */
106 * Set the pixel at (x, y) to the given value
107 * NOTE: The surface must be locked before calling this!
109 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
111 int bpp = surface->format->BytesPerPixel;
112 /* Here p is the address to the pixel we want to set */
113 Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
121 *(Uint16 *)p = pixel;
125 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
126 p[0] = (pixel >> 16) & 0xff;
127 p[1] = (pixel >> 8) & 0xff;
131 p[1] = (pixel >> 8) & 0xff;
132 p[2] = (pixel >> 16) & 0xff;
137 *(Uint32 *)p = pixel;
142 #define MIN(a,b) (((a)<(b))?(a):(b))
143 #define MAX(a,b) (((a)>(b))?(a):(b))
144 #define BOUND(a,b,c) MAX(a,MIN(b,c))
145 #define BLURFUNC(d,A,B) A-B*(d)
146 #define BLURFUNCIMAX(A,B) ceil(sqrt((A)/(B)))
148 Uint32 getpixelfilter(SDL_Surface *src, SDL_PixelFormat *fmt, int x, int y, double A, double B, double C)
150 double r, g, b, a, f;
151 Uint8 pr, pg, pb, pa;
153 int imax = BLURFUNCIMAX(A,B);
155 // 1. calculate blackened blurred image
157 for(i=-imax; i<=imax; ++i)
158 if(y+i >= 0 && y+i < src->h)
159 for(j=-imax; j<=imax; ++j)
160 if(x+j >= 0 && x+j < src->w)
162 SDL_GetRGBA(getpixel(src, x+j, y+i), src->format, &pr, &pg, &pb, &pa);
163 f = BLURFUNC(i*i+j*j, A, B);
175 // 2. overlap it with the actual image again
176 if(y >= 0 && y < src->h && x >= 0 && x < src->w)
178 SDL_GetRGBA(getpixel(src, x, y), src->format, &pr, &pg, &pb, &pa);
180 f = a + pa - (a * pa) / 255L;
197 r = g = b = MAX(0, 255 - C * (255 - a));
199 r = g = b = MAX(0, 255 + C * a);
201 return SDL_MapRGBA(fmt, r, g, b, a);
204 void blitfilter(SDL_Surface *src, SDL_Surface *dest, int x0, int y0, double A, double B, double C)
206 // note: x0, y0 is the origin of the UNFILTERED image; it is "transparently" expanded by a BLURFUNCIMAX.
209 d = BLURFUNCIMAX(A,B);
210 SDL_LockSurface(src);
211 SDL_LockSurface(dest);
212 for(y = -d; y < d + src->h; ++y)
213 for(x = -d; x < d + src->w; ++x)
214 putpixel(dest, x + x0, y + y0, getpixelfilter(src, dest->format, x, y, A, B, C));
215 SDL_UnlockSurface(dest);
216 SDL_UnlockSurface(src);
219 int mapFont(int d, char *c_)
221 unsigned char *c = (unsigned char *) c_;
223 return (*c >= 0x20 && *c <= 0x7E) || (*c >= 0xA0 && *c <= 0xFE) ? 0 : -1;
224 if(*c >= 0x20 && *c <= 0x7E)
226 if(*c >= 0xA0 && *c <= 0xAF)
231 if(*c >= 0xB0 && *c <= 0xB9)
236 if(*c >= 0xBA && *c <= 0xDF)
241 if(*c >= 0xE0 && *c <= 0xFE)
250 * @brief Blit a surface onto another and stretch it.
251 * With a 4.2 gcc you can use -fopenmp :)
252 * You might want to add some linear fading for scaling up?
254 * @param dst Destination surface
255 * @param src Source surface, if NULL, the destination surface is used
256 * @param drec The target area
257 * @param srec The source area, if NULL, then you suck :P
259 void StretchBlit(SDL_Surface *dst, SDL_Surface *src, SDL_Rect *drec, SDL_Rect *srec)
264 unsigned int freeSource = 0;
266 // To avoid copying copied pixels, that would suck :)
267 src = SDL_ConvertSurface(dst, dst->format, dst->flags);
272 drec = &dst->clip_rect;
274 srec = &src->clip_rect;
276 SDL_LockSurface(dst);
277 SDL_LockSurface(src);
279 double scaleX = (double)srec->w / (double)drec->w;
280 double scaleY = (double)srec->h / (double)drec->h;
284 for(y = drec->y; y < (drec->y + drec->h); ++y)
288 int dy = y - drec->y;
289 for(x = drec->x; x < (drec->x + drec->w); ++x)
293 // dx, dy relative to the drec start
294 int dx = x - drec->x;
296 double dfromX, dfromY, dtoX, dtoY;
297 int fromX, fromY, toX, toY;
298 // Get the pixel range which represents the current pixel
299 // When scaling down this should be a rectangle :)
300 // Otherwise it's just 1 pixel anyway, from==to then
301 dfromX = dx * scaleX;
302 dfromY = dy * scaleY;
303 dtoX = (dx+1) * scaleX;
304 dtoY = (dy+1) * scaleY;
305 // The first and last one usually aren't 100% within this space
306 fromX = (int)dfromX; dfromX = 1.0 - (dfromX - fromX); // invert the from percentage
307 fromY = (int)dfromY; dfromY = 1.0 - (dfromY - fromY);
308 toX = (int)dtoX; dtoX -= toX; // this one is ok
309 toY = (int)dtoY; dtoY -= toY;
311 /* Short explanation:
312 * FROM is where to START, so when it's 5.7, then 30% of the 5th pixel is to be used
313 * TO is where it ENDS, so if it's 8.4, then 40% of the 9th pixel is to be used!
316 // Now get all the pixels and merge them together...
318 unsigned int r, g, b, a, ar, ag, ab;
319 unsigned int count = 0;
320 r = g = b = a = ar = ag = ab = 0;
322 printf("%i %i - %f %f\n", fromX, toX, dfromX, dtoX);*/
324 // when going from one to the next there's usually one
325 // situation where the left pixel has a value of 0.1something and
326 // the right one of 0
327 // so adjust the values here
328 // otherwise we get lines in the image with the original color
330 if(toX - fromX == 1 && drec->w > srec->w) {
345 if(toY - fromY == 1 && drec->h > srec->h) {
360 for(j = fromY; j <= toY; ++j)
364 if((j+srec->y) >= src->h)
366 for(i = fromX; i <= toX; ++i)
368 Uint8 pr, pg, pb, pa;
369 Uint16 par, pag, pab;
373 if((i+srec->x) >= src->w)
376 SDL_GetRGBA(getpixel(src, i + srec->x, j + srec->y), src->format, &pr, &pg, &pb, &pa);
377 par = pa * (unsigned int)pr;
378 pag = pa * (unsigned int)pg;
379 pab = pa * (unsigned int)pb;
390 int iinc = inc * 256;
403 //printf("COLOR VALUE: %i, %i, %i, %i \t COUNT: %f\n", r, g, b, a, count);
419 putpixel(dst, x, y, SDL_MapRGBA(dst->format, (Uint8)r, (Uint8)g, (Uint8)b, (Uint8)a));
423 SDL_UnlockSurface(dst);
424 SDL_UnlockSurface(src);
427 SDL_FreeSurface(src);
430 void StretchDown(SDL_Surface *srfc, int x, int y, int w, int h, int wtarget)
432 // @"#$ SDL has no StretchBlit
433 // this one is slow, but at least I know how it works
435 unsigned int stretchedline[8 * wtarget]; // ra ga ba r g b a n
436 SDL_LockSurface(srfc);
438 for(r = y; r < y + h; ++r)
440 // each input pixel is wtarget pixels "worth"
441 //memset(stretchedline, sizeof(stretchedline), 0);
442 memset(stretchedline, 0, sizeof(stretchedline));
443 for(c = 0; c < w * wtarget; ++c)
445 Uint8 pr, pg, pb, pa;
446 unsigned int *p = &stretchedline[8 * (c / w)];
447 SDL_GetRGBA(getpixel(srfc, x + c / wtarget, r), srfc->format, &pr, &pg, &pb, &pa);
448 p[0] += (unsigned int) pr * (unsigned int) pa;
449 p[1] += (unsigned int) pg * (unsigned int) pa;
450 p[2] += (unsigned int) pb * (unsigned int) pa;
451 p[3] += (unsigned int) pr;
452 p[4] += (unsigned int) pg;
453 p[5] += (unsigned int) pb;
454 p[6] += (unsigned int) pa;
457 for(c = 0; c < wtarget; ++c)
459 unsigned int *p = &stretchedline[8 * c];
461 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]));
463 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]));
465 for(c = wtarget; c < w; ++c)
466 putpixel(srfc, x + c, r, SDL_MapRGBA(srfc->format, 0, 0, 0, 0));
469 SDL_UnlockSurface(srfc);
472 int GetBoundingBox(SDL_Surface *surf, const SDL_Rect *inbox, SDL_Rect *outbox)
474 int bx = -1, by = -1; // start
475 //int bw = 0, bh = 0;
476 int ex = -1, ey = -1; // end
478 for(cx = inbox->x; cx < inbox->x + inbox->w; ++cx)
480 for(cy = inbox->y; cy < inbox->y + inbox->h; ++cy)
482 Uint8 pr, pg, pb, pa;
483 SDL_GetRGBA(getpixel(surf, cx, cy), surf->format, &pr, &pg, &pb, &pa);
484 // include colors, or only care about pa?
494 if(cx < bx) // a pixel more on the left
496 /*if(cy < by) // a pixel more above... wait... this cannot happen actually
498 if(cx > ex) // a pixel on the right
500 if(cy > ey) // a pixel on the bottom :)
510 outbox->w = (ex - bx + 1);
511 outbox->h = (ey - by + 1);
515 int main(int argc, char **argv)
518 SDL_Surface *conchars, *conchars0;
521 SDL_Color white = {255, 255, 255, 255};
523 int maxAscent, maxDescent, maxWidth;
529 errx(1, "Usage: %s infile.tga topref bottomref cellheight outfile.tga font.ttf fontCOOL.ttf fontLCD.ttf blurA blurB blurColors\n", argv[0]);
531 const char *infilename = argv[1];
532 int referenceTop = atoi(argv[2]);
533 int referenceBottom = atoi(argv[3]);
534 int cell = atoi(argv[4]);
535 const char *outfilename = argv[5];
536 const char *font0 = argv[6];
537 const char *font1 = argv[7];
538 const char *font2 = argv[8];
539 double A = atof(argv[9]);
540 double B = atof(argv[10]);
541 double C = atof(argv[11]);
544 char widthfilename[512];
545 snprintf(widthfilename, sizeof(widthfilename), "%.*s.width", (int)strlen(outfilename) - 4, outfilename);
547 int border=BLURFUNCIMAX(A, B);
549 fprintf(stderr, "Using %d border pixels\n", border);
552 errx(1, "SDL_Init failed");
555 errx(1, "TTF_Init failed: %s", TTF_GetError());
557 conchars0 = IMG_Load(infilename);
559 errx(1, "IMG_Load failed: %s", IMG_GetError());
561 if(conchars0->w != conchars0->h)
562 errx(1, "conchars aren't square");
563 if(conchars0->w % 16)
564 errx(1, "conchars have bad width");
566 conchars = SDL_CreateRGBSurface(SDL_SWSURFACE, cell * 16, cell * 16, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
567 in.x = in.y = out.x = out.y = 0;
568 in.w = in.h = conchars0->w;
569 out.w = out.h = cell * 16;
570 StretchBlit(conchars, conchars0, &out, &in);
571 SDL_FreeSurface(conchars0);
573 for(currentSize = cell * 2; currentSize; --currentSize)
575 fonts[0] = TTF_OpenFont(font0, currentSize);
577 errx(1, "TTF_OpenFont failed: %s", TTF_GetError());
579 if(strcmp(font0, font1) || strcmp(font0, font2))
581 fonts[1] = TTF_OpenFont(font1, currentSize);
583 warnx("TTF_OpenFont failed: %s", TTF_GetError());
585 fonts[2] = TTF_OpenFont(font2, currentSize);
587 warnx("TTF_OpenFont failed: %s", TTF_GetError());
593 fonts[1] = fonts[2] = fonts[0];
597 //maxAscent = MAX(MAX(TTF_FontAscent(fonts[0]), fonts[1] ? TTF_FontAscent(fonts[1]) : 0), fonts[2] ? TTF_FontAscent(fonts[2]) : 0);
598 //maxDescent = -MIN(MIN(TTF_FontDescent(fonts[0]), fonts[1] ? TTF_FontDescent(fonts[1]) : 0), fonts[2] ? TTF_FontDescent(fonts[2]) : 0);
602 for(i = 0; i < 256; ++i)
604 char str[2]; str[0] = i; str[1] = 0;
605 int fntid = mapFont(differentFonts, &str[0]);
610 glyph = TTF_RenderText_Blended(fonts[fntid], str, white);
612 errx(1, "TTF_RenderText_Blended %d failed: %s", i, TTF_GetError());
614 maxWidth = MAX(maxWidth, glyph->w);
620 if(GetBoundingBox(glyph, &in, &out))
622 int baseline = TTF_FontAscent(fonts[fntid]);
623 int asc = baseline - out.y;
624 int desc = (out.y + out.h - 1) - baseline;
627 if(desc > maxDescent)
631 SDL_FreeSurface(glyph);
634 if(border + maxAscent + 1 + maxDescent + border <= cell)
635 if(border + maxWidth + border <= cell)
640 TTF_CloseFont(fonts[2]);
641 TTF_CloseFont(fonts[1]);
643 TTF_CloseFont(fonts[0]);
646 errx(1, "Sorry, no suitable size found.");
647 fprintf(stderr, "Using font size %d (%d + 1 + %d)\n", currentSize, maxAscent, maxDescent);
649 isfixed = TTF_FontFaceIsFixedWidth(fonts[0]);
650 if(getenv("FORCE_FIXED"))
653 // TODO convert conchars to BGRA (so the TGA writer can reliably use it)
655 transparent = SDL_MapRGBA(conchars->format, 255, 0, 255, 0);
657 FILE *widthfile = fopen(widthfilename, "w");
659 err(1, "fopen widthfile");
660 fprintf(widthfile, "extraspacing %f ", 0.0);
662 for(i = 0; i < 256; ++i)
667 char str[2]; str[0] = i; str[1] = 0;
670 fprintf(widthfile, "\n");
672 fntid = mapFont(differentFonts, &str[0]);
673 if(fntid < 0 || !fonts[fntid])
676 src.x = cell * (i % 16);
677 src.y = cell * (i / 16);
684 glyph = SDL_CreateRGBSurface(SDL_SWSURFACE, cell, cell, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
685 SDL_FillRect(glyph, &src2, transparent);
688 // referenceTop -> (cell - (maxAscent + 1 + maxDescent)) / 2
689 // referenceBottom -> (cell - (maxAscent + 1 + maxDescent)) / 2 + maxAscent
691 int destTop = (cell - (maxAscent + 1 + maxDescent)) / 2;
692 int destBottom = (cell - (maxAscent + 1 + maxDescent)) / 2 + maxAscent;
695 // x' = x / cell * h + y
697 // destTop = referenceTop / cell * h + y
698 // destBottom = referenceBottom / cell * h + y
701 dest.y = (destBottom * referenceTop - destTop * referenceBottom) / (double) (referenceTop - referenceBottom);
703 dest.h = cell * (destBottom - destTop) / (double) (referenceBottom - referenceTop);
708 if(dest.w > glyph->w)
710 if(dest.y + dest.h > glyph->h)
711 dest.h = glyph->h - dest.y;
715 dest.w = border + maxWidth + border;
716 StretchBlit(glyph, conchars, &dest, &src);
717 //SDL_FillRect(conchars, &src, transparent);
718 //SDL_BlitSurface(glyph, &src2, conchars, &src);
719 StretchBlit(conchars, glyph, &src, &src2);
720 SDL_FreeSurface(glyph);
721 fprintf(widthfile, "%f ", dest.w / (double) cell);
725 fprintf(stderr, "glyph %d...\n", i);
727 glyph = TTF_RenderText_Blended(fonts[fntid], str, white);
729 errx(1, "TTF_RenderText_Blended %d failed: %s", i, TTF_GetError());
731 w = border + glyph->w + border;
732 h = border + glyph->h + border;
734 warnx("sorry, this font contains a character that is too wide... output will be borked");
736 dest.x = cell * (i % 16);
737 dest.y = cell * (i / 16);
740 SDL_FillRect(conchars, &dest, transparent);
742 dest.x += border + (isfixed ? ((border + maxWidth + border - w) / 2) : 0);
743 dest.y += (cell - (maxAscent + 1 + maxDescent)) / 2 + (maxAscent - TTF_FontAscent(fonts[fntid]));
744 blitfilter(glyph, conchars, dest.x, dest.y, A, B, C);
746 SDL_FreeSurface(glyph);
748 if(isfixed && w > border + maxWidth + border)
750 StretchDown(conchars, cell * (i % 16), cell * (i / 16), w, cell, border + maxWidth + border);
751 fprintf(widthfile, "%f ", (border + maxWidth + border) / (double) cell);
754 fprintf(widthfile, "%f ", (isfixed ? border + maxWidth + border : w) / (double) cell);
757 fprintf(widthfile, "\n");
760 fprintf(stderr, "Writing...\n");
762 Image_WriteTGABGRA(outfilename, conchars->w, conchars->h, conchars->pixels);
764 SDL_FreeSurface(conchars);