1 /* sdltext.c: Font-rendering functions for SDL.
2 *
3 * Copyright (C) 2001-2006 by Brian Raiter, under the GNU General Public
4 * License. No warranty. See COPYING for details.
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include "SDL.h"
12 #include "sdlgen.h"
13 #include "../err.h"
14
15 /* Accept a bitmap as an 8-bit SDL surface and from it extract the
16 * glyphs of a font. (See the documentation included in the Tile World
17 * distribution for specifics regarding the bitmap layout.)
18 */
makefontfromsurface(fontinfo * pf,SDL_Surface * surface)19 static int makefontfromsurface(fontinfo *pf, SDL_Surface *surface)
20 {
21 char brk[267];
22 unsigned char *p;
23 unsigned char *dest;
24 Uint8 foregnd, bkgnd;
25 int pitch, wsum;
26 int count, ch;
27 int x, y, x0, y0, w;
28
29 if (surface->format->BytesPerPixel != 1)
30 return FALSE;
31
32 if (SDL_MUSTLOCK(surface))
33 SDL_LockSurface(surface);
34
35 pitch = surface->pitch;
36 p = surface->pixels;
37 foregnd = p[0];
38 bkgnd = p[pitch];
39 for (y = 1, p += pitch ; y < surface->h && *p == bkgnd ; ++y, p += pitch) ;
40 pf->h = y - 1;
41
42 wsum = 0;
43 ch = 32;
44 memset(pf->w, 0, sizeof pf->w);
45 memset(brk, 0, sizeof brk);
46 for (y = 0 ; y + pf->h < surface->h && ch < 256 ; y += pf->h + 1) {
47 p = surface->pixels;
48 p += y * pitch;
49 x0 = 1;
50 for (x = 1 ; x < surface->w ; ++x) {
51 if (p[x] == bkgnd)
52 continue;
53 w = x - x0;
54 x0 = x + 1;
55 pf->w[ch] = w;
56 wsum += w;
57 ++ch;
58 if (ch == 127)
59 ch = 144;
60 else if (ch == 154)
61 ch = 160;
62 else if (ch == 256)
63 break;
64 }
65 brk[ch] = 1;
66 }
67
68 count = ch;
69 if (!(pf->memory = calloc(wsum, pf->h)))
70 memerrexit();
71
72 x0 = 1;
73 y0 = 1;
74 dest = pf->memory;
75 for (ch = 0 ; ch < 256 ; ++ch) {
76 pf->bits[ch] = dest;
77 if (pf->w[ch] == 0)
78 continue;
79 if (brk[ch]) {
80 x0 = 1;
81 y0 += pf->h + 1;
82 }
83 p = surface->pixels;
84 p += y0 * pitch + x0;
85 for (y = 0 ; y < pf->h ; ++y, p += pitch)
86 for (x = 0 ; x < pf->w[ch] ; ++x, ++dest)
87 *dest = p[x] == bkgnd ? 0 : p[x] == foregnd ? 2 : 1;
88 x0 += pf->w[ch] + 1;
89 }
90
91 if (SDL_MUSTLOCK(surface))
92 SDL_UnlockSurface(surface);
93
94 return TRUE;
95 }
96
97 /* Given a text and a maximum horizontal space to occupy, return
98 * the amount of vertial space needed to render the entire text with
99 * word-wrapping.
100 */
measuremltext(unsigned char const * text,int len,int maxwidth)101 static int measuremltext(unsigned char const *text, int len, int maxwidth)
102 {
103 int brk, w, h, n;
104
105 if (len < 0)
106 len = strlen((char const*)text);
107 h = 0;
108 brk = 0;
109 for (n = 0, w = 0 ; n < len ; ++n) {
110 w += sdlg.font.w[text[n]];
111 if (isspace(text[n])) {
112 brk = w;
113 } else if (w > maxwidth) {
114 h += sdlg.font.h;
115 if (brk) {
116 w -= brk;
117 brk = 0;
118 } else {
119 w = sdlg.font.w[text[n]];
120 brk = 0;
121 }
122 }
123 }
124 if (w)
125 h += sdlg.font.h;
126 return h;
127 }
128
129 /*
130 * Render a single line of pixels of the given text to a locked
131 * surface at scanline. w specifies the total number of pixels to
132 * render. (Any pixels remaining after the last glyph has been
133 * rendered are set to the background color.) y specifies the vertical
134 * coordinate of the line to render relative to the font glyphs. A
135 * separate function is supplied for each possible surface depth.
136 */
137
drawtextscanline8(Uint8 * scanline,int w,int y,Uint32 * clr,unsigned char const * text,int len)138 static void *drawtextscanline8(Uint8 *scanline, int w, int y, Uint32 *clr,
139 unsigned char const *text, int len)
140 {
141 unsigned char const *glyph;
142 int n, x;
143
144 for (n = 0 ; n < len ; ++n) {
145 glyph = sdlg.font.bits[text[n]];
146 glyph += y * sdlg.font.w[text[n]];
147 for (x = 0 ; w && x < sdlg.font.w[text[n]] ; ++x, --w)
148 scanline[x] = (Uint8)clr[glyph[x]];
149 scanline += x;
150 }
151 while (w--)
152 *scanline++ = (Uint8)clr[0];
153 return scanline;
154 }
155
drawtextscanline16(Uint16 * scanline,int w,int y,Uint32 * clr,unsigned char const * text,int len)156 static void *drawtextscanline16(Uint16 *scanline, int w, int y, Uint32 *clr,
157 unsigned char const *text, int len)
158 {
159 unsigned char const *glyph;
160 int n, x;
161
162 for (n = 0 ; n < len ; ++n) {
163 glyph = sdlg.font.bits[text[n]];
164 glyph += y * sdlg.font.w[text[n]];
165 for (x = 0 ; w && x < sdlg.font.w[text[n]] ; ++x, --w)
166 scanline[x] = (Uint16)clr[glyph[x]];
167 scanline += x;
168 }
169 while (w--)
170 *scanline++ = (Uint16)clr[0];
171 return scanline;
172 }
173
drawtextscanline24(Uint8 * scanline,int w,int y,Uint32 * clr,unsigned char const * text,int len)174 static void *drawtextscanline24(Uint8 *scanline, int w, int y, Uint32 *clr,
175 unsigned char const *text, int len)
176 {
177 unsigned char const *glyph;
178 Uint32 c;
179 int n, x;
180
181 for (n = 0 ; n < len ; ++n) {
182 glyph = sdlg.font.bits[text[n]];
183 glyph += y * sdlg.font.w[text[n]];
184 for (x = 0 ; w && x < sdlg.font.w[text[n]] ; ++x, --w) {
185 c = clr[glyph[x]];
186 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
187 *scanline++ = (Uint8)(c >> 16);
188 *scanline++ = (Uint8)(c >> 8);
189 *scanline++ = (Uint8)c;
190 #else
191 *scanline++ = (Uint8)c;
192 *scanline++ = (Uint8)(c >> 8);
193 *scanline++ = (Uint8)(c >> 16);
194 #endif
195 }
196 }
197 c = clr[0];
198 while (w--) {
199 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
200 *scanline++ = (Uint8)(c >> 16);
201 *scanline++ = (Uint8)(c >> 8);
202 *scanline++ = (Uint8)c;
203 #else
204 *scanline++ = (Uint8)c;
205 *scanline++ = (Uint8)(c >> 8);
206 *scanline++ = (Uint8)(c >> 16);
207 #endif
208 }
209 return scanline;
210 }
211
drawtextscanline32(Uint32 * scanline,int w,int y,Uint32 * clr,unsigned char const * text,int len)212 static void *drawtextscanline32(Uint32 *scanline, int w, int y, Uint32 *clr,
213 unsigned char const *text, int len)
214 {
215 unsigned char const *glyph;
216 int n, x;
217
218 for (n = 0 ; n < len ; ++n) {
219 glyph = sdlg.font.bits[text[n]];
220 glyph += y * sdlg.font.w[text[n]];
221 for (x = 0 ; w && x < sdlg.font.w[text[n]] ; ++x, --w)
222 scanline[x] = clr[glyph[x]];
223 scanline += x;
224 }
225 while (w--)
226 *scanline++ = clr[0];
227 return scanline;
228 }
229
230 /*
231 * The main font-rendering functions.
232 */
233
234 /* Draw a single line of text to the screen at the position given by
235 * rect. The bitflags in the final argument control the placement of
236 * text within rect and what colors to use.
237 */
drawtext(SDL_Rect * rect,unsigned char const * text,int len,int flags)238 static void drawtext(SDL_Rect *rect, unsigned char const *text,
239 int len, int flags)
240 {
241 Uint32 *clr;
242 void *p;
243 void *q;
244 int l, r;
245 int pitch, bpp, n, w, y;
246
247 if (len < 0)
248 len = text ? strlen((char const*)text) : 0;
249
250 w = 0;
251 for (n = 0 ; n < len ; ++n)
252 w += sdlg.font.w[text[n]];
253 if (flags & PT_CALCSIZE) {
254 rect->h = sdlg.font.h;
255 rect->w = w;
256 return;
257 }
258 if (w >= rect->w) {
259 w = rect->w;
260 l = r = 0;
261 } else if (flags & PT_RIGHT) {
262 l = rect->w - w;
263 r = 0;
264 } else if (flags & PT_CENTER) {
265 l = (rect->w - w) / 2;
266 r = (rect->w - w) - l;
267 } else {
268 l = 0;
269 r = rect->w - w;
270 }
271
272 if (flags & PT_DIM)
273 clr = sdlg.dimtextclr.c;
274 else if (flags & PT_HILIGHT)
275 clr = sdlg.hilightclr.c;
276 else
277 clr = sdlg.textclr.c;
278
279 pitch = sdlg.screen->pitch;
280 bpp = sdlg.screen->format->BytesPerPixel;
281 p = (unsigned char*)sdlg.screen->pixels + rect->y * pitch + rect->x * bpp;
282 for (y = 0 ; y < sdlg.font.h && y < rect->h ; ++y) {
283 switch (bpp) {
284 case 1:
285 q = drawtextscanline8(p, l, y, clr, NULL, 0);
286 q = drawtextscanline8(q, w, y, clr, text, len);
287 q = drawtextscanline8(q, r, y, clr, NULL, 0);
288 break;
289 case 2:
290 q = drawtextscanline16(p, l, y, clr, NULL, 0);
291 q = drawtextscanline16(q, w, y, clr, text, len);
292 q = drawtextscanline16(q, r, y, clr, NULL, 0);
293 break;
294 case 3:
295 q = drawtextscanline24(p, l, y, clr, NULL, 0);
296 q = drawtextscanline24(q, w, y, clr, text, len);
297 q = drawtextscanline24(q, r, y, clr, NULL, 0);
298 break;
299 case 4:
300 q = drawtextscanline32(p, l, y, clr, NULL, 0);
301 q = drawtextscanline32(q, w, y, clr, text, len);
302 q = drawtextscanline32(q, r, y, clr, NULL, 0);
303 break;
304 }
305 p = (unsigned char*)p + pitch;
306 }
307
308 if (flags & PT_UPDATERECT) {
309 rect->y += y;
310 rect->h -= y;
311 }
312 }
313
314 /* Draw one or more lines of text to the screen at the position given by
315 * rect. The text is broken up on whitespace whenever possible.
316 */
drawmultilinetext(SDL_Rect * rect,unsigned char const * text,int len,int flags)317 static void drawmultilinetext(SDL_Rect *rect, unsigned char const *text,
318 int len, int flags)
319 {
320 SDL_Rect area;
321 int index, brkw, brkn;
322 int w, n;
323
324 if (flags & PT_CALCSIZE) {
325 rect->h = measuremltext(text, len, rect->w);
326 return;
327 }
328
329 if (len < 0)
330 len = strlen((char const*)text);
331
332 area = *rect;
333 brkw = brkn = 0;
334 index = 0;
335 for (n = 0, w = 0 ; n < len ; ++n) {
336 w += sdlg.font.w[text[n]];
337 if (isspace(text[n])) {
338 brkn = n;
339 brkw = w;
340 } else if (w > rect->w) {
341 if (brkw) {
342 drawtext(&area, text + index, brkn - index,
343 flags | PT_UPDATERECT);
344 index = brkn + 1;
345 w -= brkw;
346 } else {
347 drawtext(&area, text + index, n - index,
348 flags | PT_UPDATERECT);
349 index = n;
350 w = sdlg.font.w[text[n]];
351 }
352 brkw = 0;
353 }
354 }
355 if (w)
356 drawtext(&area, text + index, len - index, flags | PT_UPDATERECT);
357 if (flags & PT_UPDATERECT) {
358 *rect = area;
359 } else {
360 while (area.h)
361 drawtext(&area, NULL, 0, PT_UPDATERECT);
362 }
363 }
364
365 /*
366 * The exported functions.
367 */
368
369 /* Render a string of text.
370 */
_puttext(SDL_Rect * rect,char const * text,int len,int flags)371 static void _puttext(SDL_Rect *rect, char const *text, int len, int flags)
372 {
373 if (!sdlg.font.h)
374 die("no font available! (how did I get this far?)");
375
376 if (len < 0)
377 len = text ? strlen(text) : 0;
378
379 if (SDL_MUSTLOCK(sdlg.screen))
380 SDL_LockSurface(sdlg.screen);
381
382 if (flags & PT_MULTILINE)
383 drawmultilinetext(rect, (unsigned char const*)text, len, flags);
384 else
385 drawtext(rect, (unsigned char const*)text, len, flags);
386
387 if (SDL_MUSTLOCK(sdlg.screen))
388 SDL_UnlockSurface(sdlg.screen);
389 }
390
391 /* Lay out the columns of the given table so that the entire table
392 * fits within area (horizontally; no attempt is made to make it fit
393 * vertically). Return an array of rectangles, one per column. This
394 * function is essentially the same algorithm used within printtable()
395 * in tworld.c
396 */
_measuretable(SDL_Rect const * area,tablespec const * table)397 static SDL_Rect *_measuretable(SDL_Rect const *area, tablespec const *table)
398 {
399 SDL_Rect *colsizes;
400 unsigned char const *p;
401 int sep, mlindex, mlwidth, diff;
402 int i, j, n, i0, c, w, x;
403
404 if (!(colsizes = malloc(table->cols * sizeof *colsizes)))
405 memerrexit();
406 for (i = 0 ; i < table->cols ; ++i) {
407 colsizes[i].x = 0;
408 colsizes[i].y = area->y;
409 colsizes[i].w = 0;
410 colsizes[i].h = area->h;
411 }
412
413 mlindex = -1;
414 mlwidth = 0;
415 n = 0;
416 for (j = 0 ; j < table->rows ; ++j) {
417 for (i = 0 ; i < table->cols ; ++n) {
418 c = table->items[n][0] - '0';
419 if (c == 1) {
420 w = 0;
421 p = (unsigned char const*)table->items[n];
422 for (p += 2 ; *p ; ++p)
423 w += sdlg.font.w[*p];
424 if (table->items[n][1] == '!') {
425 if (w > mlwidth || mlindex != i)
426 mlwidth = w;
427 mlindex = i;
428 } else {
429 if (w > colsizes[i].w)
430 colsizes[i].w = w;
431 }
432 }
433 i += c;
434 }
435 }
436
437 sep = sdlg.font.w[' '] * table->sep;
438 w = -sep;
439 for (i = 0 ; i < table->cols ; ++i)
440 w += colsizes[i].w + sep;
441 diff = area->w - w;
442 if (diff < 0 && table->collapse >= 0) {
443 w = -diff;
444 if (colsizes[table->collapse].w < w)
445 w = colsizes[table->collapse].w - sdlg.font.w[' '];
446 colsizes[table->collapse].w -= w;
447 diff += w;
448 }
449
450 if (diff > 0) {
451 n = 0;
452 for (j = 0 ; j < table->rows && diff > 0 ; ++j) {
453 for (i = 0 ; i < table->cols ; ++n) {
454 c = table->items[n][0] - '0';
455 if (c > 1 && table->items[n][1] != '!') {
456 w = sep;
457 p = (unsigned char const*)table->items[n];
458 for (p += 2 ; *p ; ++p)
459 w += sdlg.font.w[*p];
460 for (i0 = i ; i0 < i + c ; ++i0)
461 w -= colsizes[i0].w + sep;
462 if (w > 0) {
463 if (table->collapse >= i && table->collapse < i + c)
464 i0 = table->collapse;
465 else if (mlindex >= i && mlindex < i + c)
466 i0 = mlindex;
467 else
468 i0 = i + c - 1;
469 if (w > diff)
470 w = diff;
471 colsizes[i0].w += w;
472 diff -= w;
473 if (diff == 0)
474 break;
475 }
476 }
477 i += c;
478 }
479 }
480 }
481 if (diff > 0 && mlindex >= 0 && colsizes[mlindex].w < mlwidth) {
482 mlwidth -= colsizes[mlindex].w;
483 w = mlwidth < diff ? mlwidth : diff;
484 colsizes[mlindex].w += w;
485 diff -= w;
486 }
487
488 x = 0;
489 for (i = 0 ; i < table->cols && x < area->w ; ++i) {
490 colsizes[i].x = area->x + x;
491 x += colsizes[i].w + sep;
492 if (x >= area->w)
493 colsizes[i].w = area->x + area->w - colsizes[i].x;
494 }
495 for ( ; i < table->cols ; ++i) {
496 colsizes[i].x = area->x + area->w;
497 colsizes[i].w = 0;
498 }
499
500 return colsizes;
501 }
502
503 /* Render a single row of a table to the screen, using cols to locate
504 * the entries in the individual columns.
505 */
_drawtablerow(tablespec const * table,SDL_Rect * cols,int * row,int flags)506 static int _drawtablerow(tablespec const *table, SDL_Rect *cols,
507 int *row, int flags)
508 {
509 SDL_Rect rect;
510 unsigned char const *p;
511 int c, f, n, i, y;
512
513 if (!cols) {
514 for (i = 0 ; i < table->cols ; i += table->items[(*row)++][0] - '0') ;
515 return TRUE;
516 }
517
518 if (SDL_MUSTLOCK(sdlg.screen))
519 SDL_LockSurface(sdlg.screen);
520
521 y = cols[0].y;
522 n = *row;
523 for (i = 0 ; i < table->cols ; ++n) {
524 p = (unsigned char const*)table->items[n];
525 c = p[0] - '0';
526 rect = cols[i];
527 i += c;
528 if (c > 1)
529 rect.w = cols[i - 1].x + cols[i - 1].w - rect.x;
530 f = flags | PT_UPDATERECT;
531 if (p[1] == '+')
532 f |= PT_RIGHT;
533 else if (p[1] == '.')
534 f |= PT_CENTER;
535 if (p[1] == '!')
536 drawmultilinetext(&rect, p + 2, -1, f);
537 else
538 drawtext(&rect, p + 2, -1, f);
539 if (rect.y > y)
540 y = rect.y;
541 }
542
543 if (SDL_MUSTLOCK(sdlg.screen))
544 SDL_UnlockSurface(sdlg.screen);
545
546 *row = n;
547 for (i = 0 ; i < table->cols ; ++i) {
548 cols[i].h -= y - cols[i].y;
549 cols[i].y = y;
550 }
551
552 return TRUE;
553 }
554
555 /* Free the resources associated with a font.
556 */
freefont(void)557 void freefont(void)
558 {
559 if (sdlg.font.h) {
560 free(sdlg.font.memory);
561 sdlg.font.memory = NULL;
562 sdlg.font.h = 0;
563 }
564 }
565
566 /* Load the font contained in the given bitmap file. Error messages
567 * will be displayed if complain is TRUE. The return value is TRUE if
568 * the font was successfully retrieved.
569 */
loadfontfromfile(char const * filename,int complain)570 int loadfontfromfile(char const *filename, int complain)
571 {
572 SDL_Surface *bmp;
573 fontinfo font;
574
575 bmp = SDL_LoadBMP(filename);
576 if (!bmp) {
577 if (complain)
578 errmsg(filename, "can't load font bitmap: %s", SDL_GetError());
579 return FALSE;
580 }
581 if (!makefontfromsurface(&font, bmp)) {
582 if (complain)
583 errmsg(filename, "invalid font file");
584 return FALSE;
585 }
586 SDL_FreeSurface(bmp);
587 freefont();
588 sdlg.font = font;
589 return TRUE;
590 }
591
592 /* Initialize the module.
593 */
_sdltextinitialize(void)594 int _sdltextinitialize(void)
595 {
596 sdlg.font.h = 0;
597 sdlg.puttextfunc = _puttext;
598 sdlg.measuretablefunc = _measuretable;
599 sdlg.drawtablerowfunc = _drawtablerow;
600 return TRUE;
601 }
602