core: add support for the autoverbose feature
[fbsplash.git] / core / src / ttf.c
blobe8cefaa8d954a6ac427cf6e2a1064c1919b521ac
1 /*
2 * tty.c - support for True Type fonts
4 * Splashutils adaptations:
5 * Copyright (C) 2004-2005 Michal Januszewski <spock@gentoo.org>
7 * Fbtruetype code:
8 * (w) by stepan@suse.de
10 * Original code comes from SDL_ttf.
12 * This file is subject to the terms and conditions of the GNU General Public
13 * License v2. See the file COPYING in the main directory of this archive for
14 * more details.
18 #ifndef TARGET_KERNEL
19 #include <math.h>
20 #endif
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <assert.h>
27 #include <ft2build.h>
28 #include <freetype/ftoutln.h>
29 #include <freetype/ttnameid.h>
31 #include "common.h"
32 #include "render.h"
34 int boot_msg_width = 0;
36 #define DEFAULT_PTSIZE 18
37 #define NUM_GRAYS 256
39 #ifdef TARGET_KERNEL
40 int ceil(float a)
42 int h = (int)a;
43 if (a - h >= 0.5)
44 return h+1;
45 else
46 return h;
48 #endif
50 static void Flush_Glyph(c_glyph* glyph)
52 glyph->stored = 0;
53 glyph->index = 0;
54 if(glyph->bitmap.buffer) {
55 free(glyph->bitmap.buffer);
56 glyph->bitmap.buffer = 0;
58 if(glyph->pixmap.buffer) {
59 free(glyph->pixmap.buffer);
60 glyph->pixmap.buffer = 0;
62 glyph->cached = 0;
65 static void Flush_Cache(TTF_Font* font)
67 int i;
68 int size = sizeof(font->cache) / sizeof(font->cache[0]);
70 for(i = 0; i < size; ++i) {
71 if(font->cache[i].cached) {
72 Flush_Glyph(&font->cache[i]);
76 if(font->scratch.cached) {
77 Flush_Glyph(&font->scratch);
81 static FT_Error Load_Glyph(TTF_Font* font, unsigned short ch, c_glyph* cached, int want)
83 FT_Face face;
84 FT_Error error;
85 FT_GlyphSlot glyph;
86 FT_Glyph_Metrics* metrics;
87 FT_Outline* outline;
89 assert(font);
90 assert(font->face);
92 face = font->face;
94 /* Load the glyph */
95 if (! cached->index) {
96 cached->index = FT_Get_Char_Index(face, ch);
98 error = FT_Load_Glyph(face, cached->index, FT_LOAD_DEFAULT);
99 if(error) {
100 return error;
102 /* Get our glyph shortcuts */
103 glyph = face->glyph;
104 metrics = &glyph->metrics;
105 outline = &glyph->outline;
107 /* Get the glyph metrics if desired */
108 if ((want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS)) {
109 /* Get the bounding box */
110 cached->minx = FT_FLOOR(metrics->horiBearingX);
111 cached->maxx = cached->minx + FT_CEIL(metrics->width);
112 cached->maxy = FT_FLOOR(metrics->horiBearingY);
113 cached->miny = cached->maxy - FT_CEIL(metrics->height);
114 cached->yoffset = font->ascent - cached->maxy;
115 cached->advance = FT_CEIL(metrics->horiAdvance);
117 /* Adjust for bold and italic text */
118 if(font->style & TTF_STYLE_BOLD) {
119 cached->maxx += font->glyph_overhang;
121 if(font->style & TTF_STYLE_ITALIC) {
122 cached->maxx += (int)ceil(font->glyph_italics);
124 cached->stored |= CACHED_METRICS;
127 if (((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) ||
128 ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP))) {
129 int mono = (want & CACHED_BITMAP);
130 int i;
131 FT_Bitmap* src;
132 FT_Bitmap* dst;
134 /* Handle the italic style */
135 if(font->style & TTF_STYLE_ITALIC) {
136 FT_Matrix shear;
138 shear.xx = 1 << 16;
139 shear.xy = (int) (font->glyph_italics * (1 << 16))/ font->height;
140 shear.yx = 0;
141 shear.yy = 1 << 16;
143 FT_Outline_Transform(outline, &shear);
146 /* Render the glyph */
147 if (mono) {
148 error = FT_Render_Glyph(glyph, ft_render_mode_mono);
149 } else {
150 error = FT_Render_Glyph(glyph, ft_render_mode_normal);
152 if(error) {
153 return error;
156 /* Copy over information to cache */
157 src = &glyph->bitmap;
158 if (mono) {
159 dst = &cached->bitmap;
160 } else {
161 dst = &cached->pixmap;
163 memcpy(dst, src, sizeof(*dst));
164 if (mono) {
165 dst->pitch *= 8;
168 /* Adjust for bold and italic text */
169 if(font->style & TTF_STYLE_BOLD) {
170 int bump = font->glyph_overhang;
171 dst->pitch += bump;
172 dst->width += bump;
174 if(font->style & TTF_STYLE_ITALIC) {
175 int bump = (int)ceil(font->glyph_italics);
176 dst->pitch += bump;
177 dst->width += bump;
180 if (dst->rows != 0) {
181 dst->buffer = malloc(dst->pitch * dst->rows);
182 if(!dst->buffer) {
183 return FT_Err_Out_Of_Memory;
185 memset(dst->buffer, 0, dst->pitch * dst->rows);
187 for(i = 0; i < src->rows; i++) {
188 int soffset = i * src->pitch;
189 int doffset = i * dst->pitch;
190 if (mono) {
191 unsigned char *srcp = src->buffer + soffset;
192 unsigned char *dstp = dst->buffer + doffset;
193 int j;
194 for (j = 0; j < src->width; j += 8) {
195 unsigned char ch = *srcp++;
196 *dstp++ = (ch&0x80) >> 7;
197 ch <<= 1;
198 *dstp++ = (ch&0x80) >> 7;
199 ch <<= 1;
200 *dstp++ = (ch&0x80) >> 7;
201 ch <<= 1;
202 *dstp++ = (ch&0x80) >> 7;
203 ch <<= 1;
204 *dstp++ = (ch&0x80) >> 7;
205 ch <<= 1;
206 *dstp++ = (ch&0x80) >> 7;
207 ch <<= 1;
208 *dstp++ = (ch&0x80) >> 7;
209 ch <<= 1;
210 *dstp++ = (ch&0x80) >> 7;
212 } else {
213 memcpy(dst->buffer+doffset,
214 src->buffer+soffset,src->pitch);
219 /* Handle the bold style */
220 if (font->style & TTF_STYLE_BOLD) {
221 int row;
222 int col;
223 int offset;
224 int pixel;
225 unsigned char* pixmap;
227 /* The pixmap is a little hard, we have to add and clamp */
228 for(row = dst->rows - 1; row >= 0; --row) {
229 pixmap = (unsigned char*) dst->buffer + row * dst->pitch;
230 for(offset=1; offset <= font->glyph_overhang; ++offset) {
231 for(col = dst->width - 1; col > 0; --col) {
232 pixel = (pixmap[col] + pixmap[col-1]);
233 if(pixel > NUM_GRAYS - 1) {
234 pixel = NUM_GRAYS - 1;
236 pixmap[col] = (unsigned char) pixel;
242 /* Mark that we rendered this format */
243 if (mono) {
244 cached->stored |= CACHED_BITMAP;
245 } else {
246 cached->stored |= CACHED_PIXMAP;
250 /* We're done, mark this glyph cached */
251 cached->cached = ch;
253 return 0;
256 static FT_Error Find_Glyph(TTF_Font* font, unsigned short ch, int want)
258 int retval = 0;
260 if(ch < 256) {
261 font->current = &font->cache[ch];
262 } else {
263 if (font->scratch.cached != ch) {
264 Flush_Glyph(&font->scratch);
266 font->current = &font->scratch;
268 if ((font->current->stored & want) != want) {
269 retval = Load_Glyph(font, ch, font->current, want);
271 return retval;
274 static unsigned short *UTF8_to_UNICODE(unsigned short *unicode, const char *utf8, int len)
276 int i, j;
277 unsigned short ch;
279 for (i=0, j=0; i < len; ++i, ++j) {
280 ch = ((const unsigned char *)utf8)[i];
281 if (ch >= 0xF0) {
282 ch = (unsigned short)(utf8[i]&0x07) << 18;
283 ch |= (unsigned short)(utf8[++i]&0x3F) << 12;
284 ch |= (unsigned short)(utf8[++i]&0x3F) << 6;
285 ch |= (unsigned short)(utf8[++i]&0x3F);
286 } else
287 if (ch >= 0xE0) {
288 ch = (unsigned short)(utf8[i]&0x3F) << 12;
289 ch |= (unsigned short)(utf8[++i]&0x3F) << 6;
290 ch |= (unsigned short)(utf8[++i]&0x3F);
291 } else
292 if (ch >= 0xC0) {
293 ch = (unsigned short)(utf8[i]&0x3F) << 6;
294 ch |= (unsigned short)(utf8[++i]&0x3F);
296 unicode[j] = ch;
298 unicode[j] = 0;
300 return unicode;
303 /* TTF stuff */
305 static FT_Library library;
306 static int TTF_initialized = 0;
308 int TTF_Init(void)
310 int status;
311 FT_Error error;
313 status = 0;
314 error = FT_Init_FreeType(&library);
315 if (error) {
316 iprint(MSG_ERROR, "Couldn't init FreeType engine %d\n", error);
317 status = -1;
318 } else {
319 TTF_initialized = 1;
321 return status;
324 void TTF_Quit(void)
326 if (TTF_initialized) {
327 FT_Done_FreeType(library);
329 TTF_initialized = 0;
332 static int TTF_SizeUNICODE(TTF_Font *font, const unsigned short *text, int *w, int *h)
334 int status;
335 const unsigned short *ch;
336 int x, z;
337 int minx, maxx;
338 int miny, maxy;
339 c_glyph *glyph;
340 FT_Error error;
342 /* Initialize everything to 0 */
343 if (! TTF_initialized) {
344 return -1;
346 status = 0;
347 minx = maxx = 0;
348 miny = maxy = 0;
350 /* Load each character and sum it's bounding box */
351 x= 0;
352 for (ch=text; *ch; ++ch) {
353 error = Find_Glyph(font, *ch, CACHED_METRICS);
354 if (error) {
355 return -1;
357 glyph = font->current;
359 z = x + glyph->minx;
360 if (minx > z) {
361 minx = z;
363 if (font->style & TTF_STYLE_BOLD) {
364 x += font->glyph_overhang;
366 if (glyph->advance > glyph->maxx) {
367 z = x + glyph->advance;
368 } else {
369 z = x + glyph->maxx;
371 if (maxx < z) {
372 maxx = z;
374 x += glyph->advance;
376 if (glyph->miny < miny) {
377 miny = glyph->miny;
379 if (glyph->maxy > maxy) {
380 maxy = glyph->maxy;
384 /* Fill the bounds rectangle */
385 if (w) {
386 *w = (maxx - minx);
388 if (h)
389 *h = font->height;
391 return status;
394 static void TTF_RenderUNICODE_Shaded(stheme_t *theme, u8 *target, const unsigned short *text,
395 TTF_Font* font, int x, int y, color fcol, rect *re)
397 int xstart, width, height, i, j, row_underline;
398 unsigned int val;
399 const unsigned short* ch;
400 unsigned char* src;
401 unsigned char* dst;
402 int row, col;
403 c_glyph *glyph;
404 FT_Error error;
406 /* Get the dimensions of the text surface */
407 if ((TTF_SizeUNICODE(font, text, &width, NULL) < 0) || !width) {
408 iprint(MSG_ERROR, "Text has zero width.\n");
409 return;
411 height = font->height;
414 * The underline stuff below is a little hackish. The characters
415 * that are being rendered do not form a continuous rectangle, and
416 * we want for the underline to be continuous and span below the
417 * whole text. To achieve that, while rendering each character,
418 * we have to not only paint the part of the underline that is
419 * directly below it, but also the part that 'links' it to the next
420 * character. Thus all the (font->style & TTF_STYLE_UNDERLINE) ? .. : ..
421 * code.
423 row_underline = font->ascent - font->underline_offset - 1;
424 if (row_underline >= height) {
425 row_underline = (height-1) - font->underline_height;
428 /* Load and render each character */
429 xstart = 0;
430 for (ch = text; *ch; ++ch) {
431 FT_Bitmap* current;
432 rect tre;
434 error = Find_Glyph(font, *ch, CACHED_METRICS|CACHED_PIXMAP);
435 if (error)
436 return;
437 glyph = font->current;
439 tre.x1 = xstart + x;
440 tre.x2 = tre.x1 + glyph->advance - 1;
441 if (font->style & TTF_STYLE_BOLD) {
442 tre.x2 += font->glyph_overhang;
444 tre.y1 = y;
445 tre.y2 = y + height - 1;
447 if (tre.x1 < 0)
448 goto next_glyph;
450 /* TODO: optimize rect-based rendering (?) */
451 if (!rect_intersect(re, &tre))
452 goto next_glyph;
454 current = &glyph->pixmap;
455 for (row = 0; row < ((font->style & TTF_STYLE_UNDERLINE) ? height - glyph->yoffset : current->rows); ++row) {
456 int add;
457 u8 *memlimit = target + theme->xres * theme->yres * fbd.bytespp;
459 /* Sanity checks.. */
460 i = y + row + glyph->yoffset;
461 j = tre.x1;
463 if (i < re->y1 || i > re->y2)
464 continue;
466 if (j >= theme->xres)
467 continue;
469 if (font->style & TTF_STYLE_UNDERLINE && glyph->minx > 0) {
470 j -= glyph->minx;
473 dst = (unsigned char *)target + (i * theme->xres + j) * fbd.bytespp;
474 src = current->buffer + row*current->pitch;
476 add = x & 1;
477 add ^= (add ^ (row+y)) & 1 ? 1 : 3;
479 for (col = ((font->style & TTF_STYLE_UNDERLINE && glyph->minx > 0) ? -glyph->minx : 0);
480 col < ((font->style & TTF_STYLE_UNDERLINE && *(ch+1)) ?
481 current->width + glyph->advance : current->width); col++, j++) {
483 if (j < re->x1 || j > re->x2)
484 continue;
486 if (j >= theme->xres)
487 continue;
489 if (dst + fbd.bytespp-1 > memlimit)
490 break;
492 if (row < current->rows && col < current->width && col >= 0)
493 val = *src++;
494 else
495 val = 0;
497 /* Handle underline */
498 if (font->style & TTF_STYLE_UNDERLINE && row+glyph->yoffset >= row_underline &&
499 row+glyph->yoffset < row_underline + font->underline_height) {
500 val = NUM_GRAYS-1;
503 put_pixel(fcol.a * val / 255, fcol.r, fcol.g, fcol.b, dst, dst, add);
504 dst += fbd.bytespp;
505 add ^= 3;
509 next_glyph:
511 xstart += glyph->advance;
512 if (font->style & TTF_STYLE_BOLD) {
513 xstart += font->glyph_overhang;
516 return;
519 static void TTF_CloseFont(TTF_Font* font)
521 Flush_Cache(font);
522 FT_Done_Face(font->face);
523 free(font);
526 static void TTF_SetFontStyle(TTF_Font* font, int style)
528 font->style = style;
529 Flush_Cache(font);
532 static TTF_Font* TTF_OpenFontIndex(const char *file, int ptsize, long index)
534 TTF_Font* font;
535 FT_Error error;
536 FT_Face face;
537 FT_Fixed scale;
539 font = (TTF_Font*) malloc(sizeof *font);
540 if (font == NULL) {
541 iprint(MSG_ERROR, "Out of memory\n");
542 return NULL;
544 memset(font, 0, sizeof(*font));
546 /* Open the font and create ancillary data */
547 error = FT_New_Face(library, file, 0, &font->face);
549 if (error)
550 error = FT_New_Face(library, TTF_DEFAULT, 0, &font->face);
552 // if (error && !strict_font)
553 // error=FT_New_Memory_Face(library, (const FT_Byte*)luxisri_ttf, LUXISRI_SIZE, 0, &font->face);
555 if (error) {
556 iprint(MSG_ERROR, "Couldn't load font file\n");
557 free(font);
558 return NULL;
561 if (index != 0) {
562 if (font->face->num_faces > index) {
563 FT_Done_Face(font->face);
564 error = FT_New_Face(library, file, index, &font->face);
565 if (error) {
566 iprint(MSG_ERROR, "Couldn't get font face\n");
567 free(font);
568 return NULL;
570 } else {
571 iprint(MSG_ERROR, "No such font face\n");
572 free(font);
573 return NULL;
576 face = font->face;
578 /* Make sure that our font face is scalable (global metrics) */
579 if (! FT_IS_SCALABLE(face)) {
580 iprint(MSG_ERROR, "Font face is not scalable\n");
581 TTF_CloseFont(font);
582 return NULL;
585 /* Set the character size and use default DPI (72) */
586 error = FT_Set_Char_Size(font->face, 0, ptsize * 64, 0, 0);
587 if (error) {
588 iprint(MSG_ERROR, "Couldn't set font size\n");
589 TTF_CloseFont(font);
590 return NULL;
593 /* Get the scalable font metrics for this font */
594 scale = face->size->metrics.y_scale;
595 font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale));
596 font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale));
597 font->height = font->ascent - font->descent + /* baseline */ 1;
598 font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
599 font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale));
600 font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale));
601 if (font->underline_height < 1) {
602 font->underline_height = 1;
605 /* Set the default font style */
606 font->style = TTF_STYLE_NORMAL;
607 font->glyph_overhang = face->size->metrics.y_ppem / 10;
608 /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
609 font->glyph_italics = 0.207f;
610 font->glyph_italics *= font->height;
612 return font;
615 static TTF_Font* TTF_OpenFont(const char *file, int ptsize)
617 TTF_Font *a;
619 a = TTF_OpenFontIndex(file, ptsize, 0);
621 if (a == NULL) {
622 iprint(MSG_ERROR, "Couldn't load %d pt font from %s\n", ptsize, file);
625 return a;
628 int load_fonts(stheme_t *theme)
630 item *i;
632 for (i = theme->fonts.head; i != NULL; i = i->next) {
633 font_e *fe = (font_e*) i->p;
634 if (!fe->font) {
635 fe->font = TTF_OpenFont(fe->file, fe->size);
639 return 0;
642 int free_fonts(stheme_t *theme)
644 item *i, *j;
646 for (i = theme->fonts.head; i != NULL;) {
647 font_e *fe = (font_e*) i->p;
648 j = i->next;
650 if (fe->font)
651 TTF_CloseFont(fe->font);
653 if (fe->file)
654 free(fe->file);
656 free(fe);
657 free(i);
658 i = j;
661 return 0;
664 static char *text_get_output(char *prg)
666 char *buf = malloc(1024);
667 fd_set rfds;
668 struct timeval tv;
669 int pfds[2];
670 pid_t pid;
671 int i;
673 if (!buf)
674 return NULL;
676 pipe(pfds);
677 pid = fork();
678 buf[0] = 0;
680 if (pid == 0) {
681 #ifndef TARGET_KERNEL
682 /* Only play with stdout if we are NOT the kernel helper.
683 * Otherwise, things will break horribly and we'll end up
684 * with a deadlock. */
685 close(1);
686 #endif
687 dup(pfds[1]);
688 close(pfds[0]);
689 execlp("sh", "sh", "-c", prg, NULL);
690 } else {
691 FD_ZERO(&rfds);
692 FD_SET(pfds[0], &rfds);
693 tv.tv_sec = 0;
694 tv.tv_usec = 250000;
695 i = select(pfds[0]+1, &rfds, NULL, NULL, &tv);
696 if (i != -1 && i != 0) {
697 i = read(pfds[0], buf, 1024);
698 if (i > 0)
699 buf[i] = 0;
702 close(pfds[0]);
703 close(pfds[1]);
706 return buf;
709 static char *text_eval(char *txt)
711 char *p, *t, *ret, *d;
712 int len, i;
714 i = len = strlen(txt);
715 p = txt;
717 while ((t = strstr(p, "$progress")) != NULL) {
718 len += 3;
719 p = t+1;
722 ret = malloc(len+1);
724 p = txt;
725 d = ret;
727 while (*p != 0) {
728 if (*p == '\\') {
729 /* to allow literal "$progress" i.e. \$progress */
730 p++;
732 /* might have reached end of string */
733 if (*p == 0)
734 break;
736 if (*p == 'n')
737 *d = '\n';
738 else
739 *d = *p;
740 p++;
741 d++;
742 continue;
745 *d = *p;
747 if (!strncmp(p, "$progress", 9)) {
748 d += sprintf(d, "%d", config.progress * 100 / FBSPL_PROGRESS_MAX);
749 p += 9;
750 } else {
751 p++;
752 d++;
756 *d = 0; /* NULL-terminate */
758 return ret;
761 void text_render(stheme_t *theme, text *ct, rect *re, u8 *target)
763 u16 *t, *p;
764 obj *o = container_of(ct);
765 color col;
766 int x, y;
768 if (!target || !ct || !ct->font || !ct->font->font)
769 return;
771 x = o->bnd.x1;
772 y = o->bnd.y1;
774 TTF_SetFontStyle(ct->font->font, ct->style);
776 memcpy(&col, &ct->col, sizeof(col));
777 col.a *= o->opacity / 255;
779 /* Render the unicode text line by line. */
780 for (t = p = ct->cache; *p != 0; p++) {
781 if (*p == '\n') {
782 *p = 0;
783 if (p > t)
784 TTF_RenderUNICODE_Shaded(theme, target, t, ct->font->font, x, y, col, re);
785 y += ct->font->font->height;
786 *p = '\n';
787 t = p+1;
791 if (*t != 0) {
792 TTF_RenderUNICODE_Shaded(theme, target, t, ct->font->font, x, y, col, re);
796 void text_bnd(stheme_t *theme, text *ct, rect *bnd)
798 obj *o;
799 char *txt = NULL, *txt2;
800 u16 *p;
801 int unicode_len, t;
802 int lines = 1;
804 o = container_of(ct);
806 if (!ct->font || !ct->font->font)
807 return;
809 if (ct->cache)
810 free(ct->cache);
812 if (ct->flags & F_TXT_EXEC) {
813 txt = text_get_output(ct->val);
816 if (ct->flags & F_TXT_EVAL) {
817 if (txt) {
818 txt2 = text_eval(txt);
819 free(txt);
820 txt = txt2;
821 } else {
822 txt = text_eval(ct->val);
826 if (ct->flags & F_TXT_MSGLOG) {
827 item *i;
828 int len = 1;
830 ct->log_last = theme->log_cnt;
832 for (i = theme->msglog.head; i; i = i->next) {
833 len += strlen(i->p) + 1;
836 /* Return an invalid bounding box if there is not
837 * data in the fbsplash log. */
838 if (len == 1) {
839 bnd->x1 = ct->x;
840 bnd->y1 = ct->y;
841 bnd->x2 = ct->x - 1;
842 bnd->y2 = ct->y - 1;
843 return;
846 txt = malloc(sizeof(char) * len);
847 txt[0] = 0;
849 for (i = theme->msglog.head; i; i = i->next) {
850 strcat(txt, i->p);
851 strcat(txt, "\n");
855 if (!txt)
856 txt = ct->val;
858 if (ct->curr_progress >= 0)
859 ct->curr_progress = config.progress;
861 /* Copy the Latin-1 text to a UNICODE text buffer */
862 unicode_len = strlen(txt);
863 ct->cache = (u16 *)malloc((unicode_len+1) * sizeof(*ct->cache));
865 if (ct->cache == NULL) {
866 iprint(MSG_ERROR, "Out of memory.\n");
867 return;
870 UTF8_to_UNICODE(ct->cache, txt, unicode_len);
871 if (txt != ct->val)
872 free(txt);
873 TTF_SetFontStyle(ct->font->font, ct->style);
875 /* Get the dimensions of the text surface */
876 if ((TTF_SizeUNICODE(ct->font->font, ct->cache, &bnd->x2, NULL) < 0) || !bnd->x2) {
877 iprint(MSG_ERROR, "Text has zero width.\n");
878 return;
881 for (p = ct->cache; *p; p++) {
882 if (*p == '\n')
883 lines++;
886 /* Calculate the position of the text object. */
887 t = ct->hotspot & F_HS_HORIZ_MASK;
888 if (t == F_HS_HMIDDLE) {
889 bnd->x1 = ct->x - bnd->x2/2;
890 bnd->x2 = ct->x + ((bnd->x2 % 2 == 0) ? (bnd->x2/2 - 1) : (bnd->x2/2));
891 } else if (t == F_HS_RIGHT) {
892 bnd->x1 = ct->x - bnd->x2 + 1;
893 bnd->x2 = ct->x;
894 } else {
895 bnd->x1 = ct->x;
896 bnd->x2 += ct->x - 1;
899 t = ct->hotspot & F_HS_VERT_MASK;
900 if (t == F_HS_VMIDDLE) {
901 bnd->y1 = ct->y - (ct->font->font->height * lines)/2;
902 bnd->y2 = ct->y + (((ct->font->font->height * lines) % 2 == 0) ?
903 ((ct->font->font->height * lines)/2 - 1) :
904 ((ct->font->font->height * lines)/2));
905 } else if (t == F_HS_BOTTOM) {
906 bnd->y1 = ct->y - (ct->font->font->height * lines) + 1;
907 bnd->y2 = ct->y;
908 } else {
909 bnd->y1 = ct->y;
910 bnd->y2 = ct->y - 1 + ct->font->font->height * lines;
913 rect_sanitize(theme, bnd);
916 void text_prerender(stheme_t *theme, text *ct, bool force)
918 rect bnd;
919 obj *o = container_of(ct);
921 if (ct->curr_progress == config.progress && !force)
922 return;
924 /* Don't do anything if the message log hasn't been updated since
925 * the last time we were called. */
926 if (ct->flags & F_TXT_MSGLOG && theme->log_cnt == ct->log_last && !force)
927 return;
929 text_bnd(theme, ct, &bnd);
930 if (bnd.x1 > bnd.x2)
931 return;
933 /* New bounding rectangle. */
934 blit_add(theme, &bnd);
935 render_add(theme, o, &bnd);
937 /* Old bounding rectangle. */
938 blit_add(theme, &o->bnd);
939 render_add(theme, o, &o->bnd);
941 /* TODO: compare w/ the old bounding rectangle here, reallocate
942 * the background buffer if necessary */
943 memcpy(&o->bnd, &bnd, sizeof(rect));