#include #include #include #include "blursk.h" static int textheight; /* height of the tallest character, plus 1 */ static int frame; /* frame counter, used for color-cycling */ static int textbg; /* background color for text */ /* this is a map of the shapes for 0-9, A-Z, and a few punctuation characters. * A space char indicates that the pixel should be unchanged, a period that it * should be set to white, and and 'x' that it should be set to black. White * is used to outline the black symbols, so they can be read even on dark * backgrounds. Single-character strings mark the start of each character, * and NULL marks the end of the list. After run-time initialization, the * single-character strings are converted to NULLs. */ static char *shapes[] = { "_", " ", " ", " ", "0", " ..... ", " .xxxxx. ", ".xx...xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx...xx.", " .xxxxx. ", " ..... ", "1", " ... ", " .xxx. ", " .xxxx. ", " ..xx. ", " .xx. ", " .xx. ", " .xx. ", " ...xx.. ", ".xxxxxxx.", " ....... ", "2", " ..... ", " .xxxxx. ", ".xx...xx.", " .. .xx.", " ...xx. ", " .xxxx. ", ".xx... ", ".xx..... ", ".xxxxxxx.", " ....... ", "3", " ..... ", " .xxxxx. ", ".xx...xx.", " .. ..xx.", " .xxx. ", " ..xx.", " .. .xx.", ".xx...xx.", " .xxxxx. ", " ..... ", "4", " ... ", " .xxx. ", " .xxxx. ", " .xx.xx. ", ".xx..xx. ", ".xx..xx. ", ".xxxxxxx.", " ....xx. ", " .xx. ", " .. ", "5", " ....... ", ".xxxxxxx.", ".xx..... ", ".xx.... ", ".xxxxxx. ", "......xx.", " .. .xx.", ".xx...xx.", " .xxxxx. ", " ..... ", "6", " .. ", " .xx. ", " .xx. ", " .xx. ", ".xx.... ", ".xxxxxx. ", ".xx...xx.", ".xx...xx.", " .xxxxx. ", " ..... ", "7", " ....... ", ".xxxxxxx.", ".x....xx.", " . .xx. ", " .xx. ", " .xx. ", " .xx. ", " .xx. ", " .xx. ", " .. ", "8", " ..... ", " .xxxxx. ", ".xx...xx.", ".xx...xx.", " .xxxxx. ", ".xx...xx.", ".xx. .xx.", ".xx...xx.", " .xxxxx. ", " ..... ", "9", " ..... ", " .xxxxx. ", ".xx...xx.", ".xx...xx.", " .xxxxxx.", " ....xx.", " .xx. ", " .xx. ", " .xx. ", " .. ", "a", " ", " ", " ..... ", " .xxxxx. ", " ....xx.", " .xxxxxx.", ".xx. .xx.", ".xx. .xx.", " .xxxxxx.", " ...... ", "b", " .. ", ".xx. ", ".xx..... ", ".xx.xxxx. ", ".xxx...xx.", ".xx. .xx.", ".xx. .xx.", ".xxx...xx.", ".xx.xxxx. ", " .. .... ", "c", " ", " ", " ..... ", " .xxxxx. ", ".xx...xx.", ".xx. .. ", ".xx. ", ".xx..... ", " .xxxxxx.", " ..... ", "d", " .. ", " .xx.", " .....xx.", " .xxxx.xx.", ".xx...xxx.", ".xx. .xx.", ".xx. .xx.", ".xx...xxx.", " .xxxx.xx.", " .... .. ", "e", " ", " ", " ..... ", " .xxxxx. ", ".xx. .xx.", ".xx. .xx.", ".xxxxxx. ", ".xx.... ", " .xxxxx. ", " ..... ", "f", " .... ", " .xxxx. ", " .xx..xx.", " .xx. .. ", " .xx. ", ".xxxx. ", " .xx. ", " .xx. ", " .xx. ", " .. ", "g", " ", " ", " .. .... ", " .xxx.xx.", ".xx..xxx.", ".xx. .xx.", ".xx. .xx.", ".xx..xxx.", " .xxx.xx.", " ....xx.", " .xxxxx. ", " ..... ", "h", " .. ", ".xx. ", ".xx.... ", ".xx.xxx. ", ".xxx..xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", " .. .. ", "i", " .. ", ".xx.", " .. ", ".xx.", ".xx.", ".xx.", ".xx.", ".xx.", ".xx.", " .. ", "j", " .. ", " .xx.", " .. ", " .xx.", " .xx.", " .xx.", " .xx.", " .xx.", " ..xx.", ".xxx. ", " ... ", "k", " .. ", ".xx. ", ".xx. .. ", ".xx. .xx.", ".xx..xx. ", ".xxxxx. ", ".xx..xx. ", ".xx. .xx.", ".xx. .xx.", " .. .. ", "l", " ... ", ".xxx. ", " .xx. ", " .xx. ", " .xx. ", " .xx. ", " .xx. ", " .xx. ", ".xxxx.", " .... ", "m", " ", " ", " .. ... .... ", ".xx.xxx.xxxx. ", ".xxx..xxx..xx.", ".xx. .xx. .xx.", ".xx. .xx. .xx.", ".xx. .xx. .xx.", ".xx. .xx. .xx.", " .. .. .. ", "n", " ", " ", " .. .... ", ".xx.xxxx. ", ".xxx...xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", " .. .. ", "o", " ", " ", " ..... ", " .xxxxx. ", ".xx...xx.", ".xx. .xx.", ".xx. .xx.", ".xx...xx.", " .xxxxx. ", " ..... ", "p", " ", " ", " .. .... ", ".xx.xxxx. ", ".xxx...xx.", ".xx. .xx.", ".xx. .xx.", ".xxx...xx.", ".xx.xxxx. ", ".xx..... ", ".xx. ", " .. ", "q", " ", " ", " .... .. ", " .xxxx.xx.", ".xx...xxx.", ".xx. .xx.", ".xx. .xx.", ".xx...xxx.", " .xxxx.xx.", " .....xx.", " .xx.", " .. ", "r", " ", " ", " .. ... ", ".xx.xxx.", ".xxxx.. ", ".xxx. ", ".xx. ", ".xx. ", ".xx. ", " .. ", "s", " ", " ", " ..... ", " .xxxxx. ", ".xx...xx.", " .xxx.. ", " ..xxx. ", ".xx...xx.", " .xxxxx. ", " ..... ", "t", " .. ", " .xx. ", " ..xx... ", ".xxxxxxx.", " ..xx... ", " .xx. ", " .xx. ", " .xx. ", " .xx. ", " .. ", "u", " ", " ", " .. .. ", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx....xx.", " .xxxxxx. ", " ...... ", "v", " ", " ", " .. .. ", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", " .xx.xx. ", " .xx.xx. ", " .xxx. ", " ... ", "w", " ", " ", " .. .. ", ".xx. .xx.", ".xx. . .xx.", ".xx. .x. .xx.", " .xx.xxx.xx. ", " .xx.xxx.xx. ", " .xxx.xxx. ", " ... ... ", "x", " ", " ", " .. .. ", ".xx. .xx.", " .xx.xx. ", " .xxx. ", " .xxx. ", " .xx.xx. ", ".xx. .xx.", " .. .. ", "y", " ", " ", " .. .. ", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx. .xx.", ".xx..xxx.", " .xxx.xx.", " ....xx.", " .xxxxx. ", " ..... ", "z", " ", " ", " ....... ", ".xxxxxxx.", " ....xx. ", " .xx. ", " .xx. ", " .xx.... ", ".xxxxxxx.", " ....... ", "-", " ", " ", " ", " ...... ", ".xxxxxx.", " ...... ", "~", " ", " ", " ", " .... ", " .xx.x.", ".x.xx. ", " .... ", ".", " ", " ", " ", " ", " ", " ", " .. ", ".xx.", ".xx.", " .. ", "!", " .. ", ".xx.", ".xx.", ".xx.", ".xx.", ".xx.", " .. ", " .. ", ".xx.", ".xx.", " .. ", ":", " ", " .. ", ".xx.", ".xx.", " .. ", " ", " .. ", ".xx.", ".xx.", " .. ", "#", " . . ", " .x..x. ", " ..x..x. ", " .xxxxxxx.", " .x..x.. ", " ..x..x. ", ".xxxxxxx. ", " .x..x.. ", " .x..x. ", " . . ", "[", " .... ", ".xxxx.", ".xx. ", ".xx. ", ".xx. ", ".xx. ", ".xx. ", ".xx. ", ".xxxx.", " .... ", "]", " .... ", ".xxxx.", " .xx.", " .xx.", " .xx.", " .xx.", " .xx.", " .xx.", ".xxxx.", " .... ", "(", " .. ", " .xx.", " .xx. ", ".xx. ", ".xx. ", ".xx. ", ".xx. ", " .xx. ", " .xx.", " .. ", ")", " .. ", ".xx. ", " .xx. ", " .xx.", " .xx.", " .xx.", " .xx.", " .xx. ", ".xx. ", " .. ", "'", " .. ", " .xx.", " .xx.", ".xx. ", " .. ", ",", " ", " ", " ", " ", " .. ", " .xx. ", ".xxxx.", " .xx. ", ".xx. ", " .. ", "%", " ", " .. ", ".xx. .. ", " .. .xx.", " .xx. ", " .xx. ", " .xx. ", ".xx. .. ", " .. .xx.", " .. ", "/", " ", " ", " .. ", " .xx.", " .xx. ", " .xx. ", " .xx. ", ".xx. ", " .. ", ">", " ", " ... ", ".xxx.. ", " ..xxx. ", " ..xx.", " ..xxx. ", ".xxx.. ", " ... ", "<", " ", " ... ", " ..xxx.", " .xxx.. ", ".xx.. ", " .xxx.. ", " ..xxx.", " ... ", NULL }; static char **chmap[127]; static int row; static gboolean big; static void textinit() { int i, h; /* if already initialized, then do nothing */ if (!shapes[0]) return; /* scan shapes for the characters defined there */ for (h = i = 0; shapes[i]; i++, h++) { /* start of new char? */ if (!shapes[i][1]) { /* was previous char the tallest? */ if (h >= textheight) textheight = h; h = 0; /* remember the shape */ chmap[shapes[i][0]] = &shapes[i + 1]; shapes[i] = NULL; } } } static guchar *normaltext(guchar *cursor, int bpl, char **shape, int color) { int x, y; for (x = 0; shape[0][x]; x++, cursor++) for (y = 0; shape[y]; y++) switch (shape[y][x]) { case '.': cursor[y * bpl] = textbg; break; case 'x': cursor[y * bpl] = color; break; } return cursor; } static guchar *bigtext(guchar *cursor, int bpl, char **shape, int color) { int x, y; char *raster; for (x = 0; shape[0][x]; x++, cursor += 2) for (y = 0, raster = cursor; shape[y]; y++, raster += 2 * bpl) switch (shape[y][x]) { case '.': raster[0] = raster[1] = raster[bpl] = raster[bpl + 1] = textbg; break; case 'x': raster[0] = raster[1] = raster[bpl] = raster[bpl + 1] = color; break; } return cursor; } /* Find the number of chars in words that fit */ static int fitwords(int maxwidth, char *text, int *width) { int scale = big ? 2 : 1; int i, last, w, lastw, ch; int fudgefactor = 0; for (i = last = w = 0; text[i] && w < maxwidth - fudgefactor; i++) { ch = tolower(text[i]); switch (ch) { case '{': scale = 2; break; case '}': scale = 1; break; case '\n': *width = w; return i; default: /* ignore if it can't be shown */ if (ch < 0 || ch > 126 || !chmap[ch]) break; /* if end of word, remember it */ if (ch == ' ') { last = i; lastw = w; } /* prefer to break lines before certain punctuation */ if (strchr("-([,", ch)) fudgefactor = 100; else fudgefactor = 0; /* add the width of this character */ w += scale * strlen(chmap[ch][0]); } } if (!text[i]) { *width = w; return i; } if (last > 0) { *width = lastw; return last; } *width = maxwidth; return i - 1; } static void textdrawrow(guchar *img, int height, int bpl, char *side, char *text, int max, int rowwidth) { int big = FALSE; guchar *cursor; int bpt = bpl * (textheight / 2); int hasbig; int rowheight; int i, ch, color; /* determine whether this involves any big chars */ if (big) hasbig = TRUE; else { for (i = 0; i < max && text[i] && text[i] != '{'; i++) { } hasbig = (i < max && text[i] == '{'); } /* if too high, then skip it */ rowheight = (hasbig ? textheight * 2 : textheight); if (row + rowheight >= height) return; /* choose the starting position for this row */ cursor = img + row * bpl; switch (*side) { case 'R': cursor += (bpl - 3 - rowwidth); break; case 'L': cursor += 1; break; default: cursor += ((bpl - 3 - rowwidth)/2); } /* if big text, then start the base line a little lower to make room */ if (hasbig) cursor += bpt; /* for each text character... */ for (i = 0; i < max && text[i]; i++) { /* handle special or unsupported characters */ ch = tolower(text[i]); if (ch == '{') { big = TRUE; continue; } if (ch == '}') { big = FALSE; continue; } if (ch < 0 || ch > 126 || !chmap[ch]) continue; /* choose a color */ color = (( frame - i) * 3) & 0xff; if (color < 128) color = color ^ 0xff; /* draw the character */ if (big) { cursor -= bpt; cursor = bigtext(cursor, bpl, chmap[ch], color); cursor += bpt; } else { cursor = normaltext(cursor, bpl, chmap[ch], color); } } /* increment row by the height of this text row */ row += (hasbig ? textheight * 2 : textheight); } /* Draw text into an image. */ void textdraw(guchar *img, int height, int bpl, char *side, char *text) { int i, max, twidth; /* parse the font table, if necessary */ textinit(); /* initialize */ frame++; row = 0; big = FALSE; textbg = (*config.overall_effect == 'B' ? 0x80 : 0); /* for each row of text... */ for (i = 0; *text; ) { /* find out how much can fit on this row */ max = fitwords(bpl - 3, text, &twidth); /* draw it */ textdrawrow(img, height, bpl, side, text, max, twidth); /* move to start of next row */ text += max; while (isspace(*text)) text++; } }