2 * tty.c - support for True Type fonts
4 * Splashutils adaptations:
5 * Copyright (C) 2004-2005 Michal Januszewski <spock@gentoo.org>
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
28 #include <freetype/ftoutln.h>
29 #include <freetype/ttnameid.h>
34 int boot_msg_width
= 0;
36 #define DEFAULT_PTSIZE 18
50 static void Flush_Glyph(c_glyph
* glyph
)
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;
65 static void Flush_Cache(TTF_Font
* font
)
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
)
86 FT_Glyph_Metrics
* metrics
;
95 if (! cached
->index
) {
96 cached
->index
= FT_Get_Char_Index(face
, ch
);
98 error
= FT_Load_Glyph(face
, cached
->index
, FT_LOAD_DEFAULT
);
102 /* Get our glyph shortcuts */
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
);
134 /* Handle the italic style */
135 if(font
->style
& TTF_STYLE_ITALIC
) {
139 shear
.xy
= (int) (font
->glyph_italics
* (1 << 16))/ font
->height
;
143 FT_Outline_Transform(outline
, &shear
);
146 /* Render the glyph */
148 error
= FT_Render_Glyph(glyph
, ft_render_mode_mono
);
150 error
= FT_Render_Glyph(glyph
, ft_render_mode_normal
);
156 /* Copy over information to cache */
157 src
= &glyph
->bitmap
;
159 dst
= &cached
->bitmap
;
161 dst
= &cached
->pixmap
;
163 memcpy(dst
, src
, sizeof(*dst
));
168 /* Adjust for bold and italic text */
169 if(font
->style
& TTF_STYLE_BOLD
) {
170 int bump
= font
->glyph_overhang
;
174 if(font
->style
& TTF_STYLE_ITALIC
) {
175 int bump
= (int)ceil(font
->glyph_italics
);
180 if (dst
->rows
!= 0) {
181 dst
->buffer
= malloc(dst
->pitch
* dst
->rows
);
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
;
191 unsigned char *srcp
= src
->buffer
+ soffset
;
192 unsigned char *dstp
= dst
->buffer
+ doffset
;
194 for (j
= 0; j
< src
->width
; j
+= 8) {
195 unsigned char ch
= *srcp
++;
196 *dstp
++ = (ch
&0x80) >> 7;
198 *dstp
++ = (ch
&0x80) >> 7;
200 *dstp
++ = (ch
&0x80) >> 7;
202 *dstp
++ = (ch
&0x80) >> 7;
204 *dstp
++ = (ch
&0x80) >> 7;
206 *dstp
++ = (ch
&0x80) >> 7;
208 *dstp
++ = (ch
&0x80) >> 7;
210 *dstp
++ = (ch
&0x80) >> 7;
213 memcpy(dst
->buffer
+doffset
,
214 src
->buffer
+soffset
,src
->pitch
);
219 /* Handle the bold style */
220 if (font
->style
& TTF_STYLE_BOLD
) {
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 */
244 cached
->stored
|= CACHED_BITMAP
;
246 cached
->stored
|= CACHED_PIXMAP
;
250 /* We're done, mark this glyph cached */
256 static FT_Error
Find_Glyph(TTF_Font
* font
, unsigned short ch
, int want
)
261 font
->current
= &font
->cache
[ch
];
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
);
274 static unsigned short *UTF8_to_UNICODE(unsigned short *unicode
, const char *utf8
, int len
)
279 for (i
=0, j
=0; i
< len
; ++i
, ++j
) {
280 ch
= ((const unsigned char *)utf8
)[i
];
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);
288 ch
= (unsigned short)(utf8
[i
]&0x3F) << 12;
289 ch
|= (unsigned short)(utf8
[++i
]&0x3F) << 6;
290 ch
|= (unsigned short)(utf8
[++i
]&0x3F);
293 ch
= (unsigned short)(utf8
[i
]&0x3F) << 6;
294 ch
|= (unsigned short)(utf8
[++i
]&0x3F);
305 static FT_Library library
;
306 static int TTF_initialized
= 0;
314 error
= FT_Init_FreeType(&library
);
316 iprint(MSG_ERROR
, "Couldn't init FreeType engine %d\n", error
);
326 if (TTF_initialized
) {
327 FT_Done_FreeType(library
);
332 static int TTF_SizeUNICODE(TTF_Font
*font
, const unsigned short *text
, int *w
, int *h
)
335 const unsigned short *ch
;
342 /* Initialize everything to 0 */
343 if (! TTF_initialized
) {
350 /* Load each character and sum it's bounding box */
352 for (ch
=text
; *ch
; ++ch
) {
353 error
= Find_Glyph(font
, *ch
, CACHED_METRICS
);
357 glyph
= font
->current
;
363 if (font
->style
& TTF_STYLE_BOLD
) {
364 x
+= font
->glyph_overhang
;
366 if (glyph
->advance
> glyph
->maxx
) {
367 z
= x
+ glyph
->advance
;
376 if (glyph
->miny
< miny
) {
379 if (glyph
->maxy
> maxy
) {
384 /* Fill the bounds rectangle */
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
;
399 const unsigned short* ch
;
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");
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) ? .. : ..
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 */
430 for (ch
= text
; *ch
; ++ch
) {
434 error
= Find_Glyph(font
, *ch
, CACHED_METRICS
|CACHED_PIXMAP
);
437 glyph
= font
->current
;
440 tre
.x2
= tre
.x1
+ glyph
->advance
- 1;
441 if (font
->style
& TTF_STYLE_BOLD
) {
442 tre
.x2
+= font
->glyph_overhang
;
445 tre
.y2
= y
+ height
- 1;
450 /* TODO: optimize rect-based rendering (?) */
451 if (!rect_intersect(re
, &tre
))
454 current
= &glyph
->pixmap
;
455 for (row
= 0; row
< ((font
->style
& TTF_STYLE_UNDERLINE
) ? height
- glyph
->yoffset
: current
->rows
); ++row
) {
457 u8
*memlimit
= target
+ theme
->xres
* theme
->yres
* fbd
.bytespp
;
459 /* Sanity checks.. */
460 i
= y
+ row
+ glyph
->yoffset
;
463 if (i
< re
->y1
|| i
> re
->y2
)
466 if (j
>= theme
->xres
)
469 if (font
->style
& TTF_STYLE_UNDERLINE
&& glyph
->minx
> 0) {
473 dst
= (unsigned char *)target
+ (i
* theme
->xres
+ j
) * fbd
.bytespp
;
474 src
= current
->buffer
+ row
*current
->pitch
;
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
)
486 if (j
>= theme
->xres
)
489 if (dst
+ fbd
.bytespp
-1 > memlimit
)
492 if (row
< current
->rows
&& col
< current
->width
&& col
>= 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
) {
503 put_pixel(fcol
.a
* val
/ 255, fcol
.r
, fcol
.g
, fcol
.b
, dst
, dst
, add
);
511 xstart
+= glyph
->advance
;
512 if (font
->style
& TTF_STYLE_BOLD
) {
513 xstart
+= font
->glyph_overhang
;
519 static void TTF_CloseFont(TTF_Font
* font
)
522 FT_Done_Face(font
->face
);
526 static void TTF_SetFontStyle(TTF_Font
* font
, int style
)
532 static TTF_Font
* TTF_OpenFontIndex(const char *file
, int ptsize
, long index
)
539 font
= (TTF_Font
*) malloc(sizeof *font
);
541 iprint(MSG_ERROR
, "Out of memory\n");
544 memset(font
, 0, sizeof(*font
));
546 /* Open the font and create ancillary data */
547 error
= FT_New_Face(library
, file
, 0, &font
->face
);
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);
556 iprint(MSG_ERROR
, "Couldn't load font file\n");
562 if (font
->face
->num_faces
> index
) {
563 FT_Done_Face(font
->face
);
564 error
= FT_New_Face(library
, file
, index
, &font
->face
);
566 iprint(MSG_ERROR
, "Couldn't get font face\n");
571 iprint(MSG_ERROR
, "No such font face\n");
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");
585 /* Set the character size and use default DPI (72) */
586 error
= FT_Set_Char_Size(font
->face
, 0, ptsize
* 64, 0, 0);
588 iprint(MSG_ERROR
, "Couldn't set font size\n");
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
;
615 static TTF_Font
* TTF_OpenFont(const char *file
, int ptsize
)
619 a
= TTF_OpenFontIndex(file
, ptsize
, 0);
622 iprint(MSG_ERROR
, "Couldn't load %d pt font from %s\n", ptsize
, file
);
628 int load_fonts(stheme_t
*theme
)
632 for (i
= theme
->fonts
.head
; i
!= NULL
; i
= i
->next
) {
633 font_e
*fe
= (font_e
*) i
->p
;
635 fe
->font
= TTF_OpenFont(fe
->file
, fe
->size
);
642 int free_fonts(stheme_t
*theme
)
646 for (i
= theme
->fonts
.head
; i
!= NULL
;) {
647 font_e
*fe
= (font_e
*) i
->p
;
651 TTF_CloseFont(fe
->font
);
664 static char *text_get_output(char *prg
)
666 char *buf
= malloc(1024);
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. */
689 execlp("sh", "sh", "-c", prg
, NULL
);
692 FD_SET(pfds
[0], &rfds
);
695 i
= select(pfds
[0]+1, &rfds
, NULL
, NULL
, &tv
);
696 if (i
!= -1 && i
!= 0) {
697 i
= read(pfds
[0], buf
, 1024);
709 static char *text_eval(char *txt
)
711 char *p
, *t
, *ret
, *d
;
714 i
= len
= strlen(txt
);
717 while ((t
= strstr(p
, "$progress")) != NULL
) {
729 /* to allow literal "$progress" i.e. \$progress */
732 /* might have reached end of string */
747 if (!strncmp(p
, "$progress", 9)) {
748 d
+= sprintf(d
, "%d", config
.progress
* 100 / FBSPL_PROGRESS_MAX
);
756 *d
= 0; /* NULL-terminate */
761 void text_render(stheme_t
*theme
, text
*ct
, rect
*re
, u8
*target
)
764 obj
*o
= container_of(ct
);
768 if (!target
|| !ct
|| !ct
->font
|| !ct
->font
->font
)
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
++) {
784 TTF_RenderUNICODE_Shaded(theme
, target
, t
, ct
->font
->font
, x
, y
, col
, re
);
785 y
+= ct
->font
->font
->height
;
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
)
799 char *txt
= NULL
, *txt2
;
804 o
= container_of(ct
);
806 if (!ct
->font
|| !ct
->font
->font
)
812 if (ct
->flags
& F_TXT_EXEC
) {
813 txt
= text_get_output(ct
->val
);
816 if (ct
->flags
& F_TXT_EVAL
) {
818 txt2
= text_eval(txt
);
822 txt
= text_eval(ct
->val
);
826 if (ct
->flags
& F_TXT_MSGLOG
) {
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. */
846 txt
= malloc(sizeof(char) * len
);
849 for (i
= theme
->msglog
.head
; i
; i
= i
->next
) {
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");
870 UTF8_to_UNICODE(ct
->cache
, txt
, unicode_len
);
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");
881 for (p
= ct
->cache
; *p
; p
++) {
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;
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;
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
)
919 obj
*o
= container_of(ct
);
921 if (ct
->curr_progress
== config
.progress
&& !force
)
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
)
929 text_bnd(theme
, ct
, &bnd
);
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
));