2 * Renders antialiased fonts for mplayer using freetype library.
3 * Should work with TrueType, Type1 and any other font supported by libfreetype.
5 * Artur Zaprzala <zybi@fanthom.irc.pl>
7 * ported inside mplayer by Jindrich Makovicka
24 #include FT_FREETYPE_H
27 #ifdef HAVE_FONTCONFIG
28 #include <fontconfig/fontconfig.h>
31 #include "libavutil/common.h"
33 #include "font_load.h"
40 #if (FREETYPE_MAJOR > 2) || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 1)
41 #define HAVE_FREETYPE21
44 char *subtitle_font_encoding
= NULL
;
45 float text_font_scale_factor
= 5.0;
46 float osd_font_scale_factor
= 6.0;
47 float subtitle_font_radius
= 2.0;
48 float subtitle_font_thickness
= 2.0;
53 int subtitle_autoscale
= 3;
55 int vo_image_width
= 0;
56 int vo_image_height
= 0;
59 int using_freetype
= 0;
60 int font_fontconfig
= 0;
63 static unsigned int const colors
= 256;
64 static unsigned int const maxcolor
= 255;
65 static unsigned const base
= 256;
66 static unsigned const first_char
= 33;
67 #define MAX_CHARSET_SIZE 60000
69 static FT_Library library
;
71 #define OSD_CHARSET_SIZE 15
73 static FT_ULong osd_charset
[OSD_CHARSET_SIZE
] =
75 0xe001, 0xe002, 0xe003, 0xe004, 0xe005, 0xe006, 0xe007, 0xe008,
76 0xe009, 0xe00a, 0xe00b, 0xe010, 0xe011, 0xe012, 0xe013
79 static FT_ULong osd_charcodes
[OSD_CHARSET_SIZE
] =
81 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
82 0x09,0x0a,0x0b,0x10,0x11,0x12,0x13
85 #define f266ToInt(x) (((x)+32)>>6) // round fractional fixed point number to integer
86 // coordinates are in 26.6 pixels (i.e. 1/64th of pixels)
87 #define f266CeilToInt(x) (((x)+63)>>6) // ceiling
88 #define f266FloorToInt(x) ((x)>>6) // floor
89 #define f1616ToInt(x) (((x)+0x8000)>>16) // 16.16
90 #define floatTof266(x) ((int)((x)*(1<<6)+0.5))
92 #define ALIGN(x) (((x)+7)&~7) // 8 byte align
94 #define WARNING(msg, args...) mp_msg(MSGT_OSD, MSGL_WARN, msg "\n", ## args)
98 //static double ttime;
101 static void paste_bitmap(unsigned char *bbuffer
, FT_Bitmap
*bitmap
, int x
, int y
, int width
, int height
, int bwidth
) {
102 int drow
= x
+y
*width
;
105 if (bitmap
->pixel_mode
==ft_pixel_mode_mono
)
106 for (h
= bitmap
->rows
; h
>0 && height
> 0; --h
, height
--, drow
+=width
, srow
+=bitmap
->pitch
)
107 for (w
= bwidth
, sp
=dp
=0; w
>0; --w
, ++dp
, ++sp
)
108 bbuffer
[drow
+dp
] = (bitmap
->buffer
[srow
+sp
/8] & (0x80>>(sp
%8))) ? 255:0;
110 for (h
= bitmap
->rows
; h
>0 && height
> 0; --h
, height
--, drow
+=width
, srow
+=bitmap
->pitch
)
111 for (w
= bwidth
, sp
=dp
=0; w
>0; --w
, ++dp
, ++sp
)
112 bbuffer
[drow
+dp
] = bitmap
->buffer
[srow
+sp
];
116 static int check_font(font_desc_t
*desc
, float ppem
, int padding
, int pic_idx
,
117 int charset_size
, FT_ULong
*charset
, FT_ULong
*charcodes
,
120 FT_Face face
= desc
->faces
[pic_idx
];
121 int const load_flags
= FT_LOAD_DEFAULT
;
122 int ymin
= INT_MAX
, ymax
= INT_MIN
;
123 int space_advance
= 20;
125 unsigned char *bbuffer
;
126 int i
, uni_charmap
= 1;
128 error
= FT_Select_Charmap(face
, ft_encoding_unicode
);
129 // fprintf(stderr, "select unicode charmap: %d\n", error);
131 if (face
->charmap
==NULL
|| face
->charmap
->encoding
!=ft_encoding_unicode
) {
132 WARNING("Unicode charmap not available for this font. Very bad!");
134 error
= FT_Set_Charmap(face
, face
->charmaps
[0]);
135 if (error
) WARNING("No charmaps! Strange.");
139 if (FT_IS_SCALABLE(face
)) {
140 error
= FT_Set_Char_Size(face
, 0, floatTof266(ppem
), 0, 0);
141 if (error
) WARNING("FT_Set_Char_Size failed.");
144 int jppem
= face
->available_sizes
[0].height
;
145 /* find closest size */
146 for (i
= 0; i
<face
->num_fixed_sizes
; ++i
) {
147 if (fabs(face
->available_sizes
[i
].height
- ppem
) < abs(face
->available_sizes
[i
].height
- jppem
)) {
149 jppem
= face
->available_sizes
[i
].height
;
152 WARNING("Selected font is not scalable. Using ppem=%i.", face
->available_sizes
[j
].height
);
153 error
= FT_Set_Pixel_Sizes(face
, face
->available_sizes
[j
].width
, face
->available_sizes
[j
].height
);
154 if (error
) WARNING("FT_Set_Pixel_Sizes failed.");
157 if (FT_IS_FIXED_WIDTH(face
))
158 WARNING("Selected font is fixed-width.");
160 /* compute space advance */
161 error
= FT_Load_Char(face
, ' ', load_flags
);
162 if (error
) WARNING("spacewidth set to default.");
163 else space_advance
= f266ToInt(face
->glyph
->advance
.x
);
165 if (!desc
->spacewidth
) desc
->spacewidth
= 2*padding
+ space_advance
;
166 if (!desc
->charspace
) desc
->charspace
= -2*padding
;
167 if (!desc
->height
) desc
->height
= f266ToInt(face
->size
->metrics
.height
);
170 for (i
= 0; i
<charset_size
; ++i
) {
171 FT_ULong character
, code
;
174 character
= charset
[i
];
176 desc
->font
[unicode
?character
:code
] = pic_idx
;
181 glyph_index
= FT_Get_Char_Index(face
, uni_charmap
? character
:code
);
182 if (glyph_index
==0) {
183 WARNING("Glyph for char 0x%02lx|U+%04lX|%c not found.", code
, character
,
184 code
<' '||code
>255 ? '.':(char)code
);
185 desc
->font
[unicode
?character
:code
] = -1;
189 desc
->glyph_index
[unicode
?character
:code
] = glyph_index
;
191 // fprintf(stderr, "font height: %lf\n", (double)(face->bbox.yMax-face->bbox.yMin)/(double)face->units_per_EM*ppem);
192 // fprintf(stderr, "font width: %lf\n", (double)(face->bbox.xMax-face->bbox.xMin)/(double)face->units_per_EM*ppem);
194 ymax
= (double)(face
->bbox
.yMax
)/(double)face
->units_per_EM
*ppem
+1;
195 ymin
= (double)(face
->bbox
.yMin
)/(double)face
->units_per_EM
*ppem
-1;
197 width
= ppem
*(face
->bbox
.xMax
-face
->bbox
.xMin
)/face
->units_per_EM
+3+2*padding
;
198 if (desc
->max_width
< width
) desc
->max_width
= width
;
199 width
= ALIGN(width
);
200 desc
->pic_b
[pic_idx
]->charwidth
= width
;
203 mp_msg(MSGT_OSD
, MSGL_ERR
, "Wrong bounding box, width <= 0 !\n");
208 mp_msg(MSGT_OSD
, MSGL_ERR
, "Something went wrong. Use the source!\n");
212 height
= ymax
- ymin
+ 2*padding
;
214 mp_msg(MSGT_OSD
, MSGL_ERR
, "Wrong bounding box, height <= 0 !\n");
218 if (desc
->max_height
< height
) desc
->max_height
= height
;
219 desc
->pic_b
[pic_idx
]->charheight
= height
;
221 // fprintf(stderr, "font height2: %d\n", height);
222 desc
->pic_b
[pic_idx
]->baseline
= ymax
+ padding
;
223 desc
->pic_b
[pic_idx
]->padding
= padding
;
224 desc
->pic_b
[pic_idx
]->current_alloc
= 0;
225 desc
->pic_b
[pic_idx
]->current_count
= 0;
229 desc
->pic_b
[pic_idx
]->w
= width
;
230 desc
->pic_b
[pic_idx
]->h
= height
;
231 desc
->pic_b
[pic_idx
]->c
= colors
;
232 desc
->pic_b
[pic_idx
]->bmp
= bbuffer
;
233 desc
->pic_b
[pic_idx
]->pen
= 0;
251 for (y
= 0; y
<height
; y
++) {
252 for (x
= 0; x
<width
; x
++) {
256 const int x1
=(x
<r
) ? r
-x
: 0;
257 const int y1
=(y
<r
) ? r
-y
: 0;
258 const int x2
=(x
+r
>=width
) ? r
+width
-x
: 2*r
+1;
259 const int y2
=(y
+r
>=height
) ? r
+height
-y
: 2*r
+1;
260 register unsigned char *dstp
= t
+ (y1
+y
-r
)* stride
+ x
-r
;
261 //register int *mp = m + y1 *mwidth;
262 register unsigned char *mp
= m
+ msize
*src
+ y1
*mwidth
;
265 for(my
= y1
; my
<y2
; my
++){
267 for(mx
= x1
; mx
<x2
; mx
++){
268 if(dstp
[mx
] < mp
[mx
]) dstp
[mx
]= mp
[mx
];
281 static void outline1(
289 int skip
= stride
-width
;
291 for (x
= 0; x
<width
; ++x
, ++s
, ++t
) *t
= *s
;
294 for (y
= 1; y
<height
-1; ++y
) {
296 for (x
= 1; x
<width
-1; ++x
, ++s
, ++t
) {
309 *t
= v
>maxcolor
? maxcolor
: v
;
315 for (x
= 0; x
<width
; ++x
, ++s
, ++t
) *t
= *s
;
319 static void outline0(
326 for (y
= 0; y
<height
; ++y
) {
335 unsigned char *buffer
,
336 unsigned short *tmp2
,
346 unsigned char *s
= buffer
;
347 unsigned short *t
= tmp2
+1;
348 for(y
=0; y
<height
; y
++){
349 memset(t
-1, 0, (width
+1)*sizeof(short));
354 register unsigned short *dstp
= t
+ x
-r
;
356 unsigned *m3
= m2
+ src
*mwidth
;
357 for(mx
=r
-x
; mx
<mwidth
; mx
++){
363 for(; x
<width
-r
; x
++){
366 register unsigned short *dstp
= t
+ x
-r
;
368 unsigned *m3
= m2
+ src
*mwidth
;
369 for(mx
=0; mx
<mwidth
; mx
++){
378 register unsigned short *dstp
= t
+ x
-r
;
380 const int x2
= r
+width
-x
;
381 unsigned *m3
= m2
+ src
*mwidth
;
382 for(mx
=0; mx
<x2
; mx
++){
393 for(x
=0; x
<width
; x
++){
395 unsigned short *srcp
= t
+ y
*(width
+1) + 1;
398 register unsigned short *dstp
= srcp
- 1 + width
+1;
399 const int src2
= (src
+ 128)>>8;
400 unsigned *m3
= m2
+ src2
*mwidth
;
404 for(mx
=r
-1; mx
<mwidth
; mx
++){
410 for(; y
<height
-r
; y
++){
411 unsigned short *srcp
= t
+ y
*(width
+1) + 1;
414 register unsigned short *dstp
= srcp
- 1 - r
*(width
+1);
415 const int src2
= (src
+ 128)>>8;
416 unsigned *m3
= m2
+ src2
*mwidth
;
420 for(mx
=0; mx
<mwidth
; mx
++){
426 for(; y
<height
; y
++){
427 unsigned short *srcp
= t
+ y
*(width
+1) + 1;
430 const int y2
=r
+height
-y
;
431 register unsigned short *dstp
= srcp
- 1 - r
*(width
+1);
432 const int src2
= (src
+ 128)>>8;
433 unsigned *m3
= m2
+ src2
*mwidth
;
437 for(mx
=0; mx
<y2
; mx
++){
448 for(y
=0; y
<height
; y
++){
449 for(x
=0; x
<width
; x
++){
457 static void resample_alpha(unsigned char *abuf
, unsigned char *bbuf
, int width
, int height
, int stride
, float factor
)
461 for (i
= 0; i
< height
; i
++) {
462 unsigned char *a
= abuf
+i
*stride
;
463 unsigned char *b
= bbuf
+i
*stride
;
464 for(j
=0;j
<width
;j
++,a
++,b
++){
467 x
=255-((x
*f
)>>8); // scale
468 if (x
+y
>255) x
=255-y
; // to avoid overflows
469 if (x
<1) x
=1; else if (x
>=252) x
=0;
475 #define ALLOC_INCR 32
476 void render_one_glyph(font_desc_t
*desc
, int c
)
480 FT_BitmapGlyph glyph
;
481 int width
, height
, stride
, maxw
, off
;
482 unsigned char *abuffer
, *bbuffer
;
484 int const load_flags
= FT_LOAD_DEFAULT
;
486 int font
= desc
->font
[c
];
489 // fprintf(stderr, "render_one_glyph %d\n", c);
491 if (!desc
->dynamic
) return;
492 if (desc
->width
[c
] != -1) return;
493 if (desc
->font
[c
] == -1) return;
495 glyph_index
= desc
->glyph_index
[c
];
498 error
= FT_Load_Glyph(desc
->faces
[font
], glyph_index
, load_flags
);
500 WARNING("FT_Load_Glyph 0x%02x (char 0x%04x) failed.", glyph_index
, c
);
504 slot
= desc
->faces
[font
]->glyph
;
507 if (slot
->format
!= ft_glyph_format_bitmap
) {
508 error
= FT_Render_Glyph(slot
, ft_render_mode_normal
);
510 WARNING("FT_Render_Glyph 0x%04x (char 0x%04x) failed.", glyph_index
, c
);
516 // extract glyph image
517 error
= FT_Get_Glyph(slot
, (FT_Glyph
*)&glyph
);
519 WARNING("FT_Get_Glyph 0x%04x (char 0x%04x) failed.", glyph_index
, c
);
524 // fprintf(stderr, "glyph generated\n");
526 maxw
= desc
->pic_b
[font
]->charwidth
;
528 if (glyph
->bitmap
.width
> maxw
) {
529 fprintf(stderr
, "glyph too wide!\n");
532 // allocate new memory, if needed
533 // fprintf(stderr, "\n%d %d %d\n", desc->pic_b[font]->charwidth, desc->pic_b[font]->charheight, desc->pic_b[font]->current_alloc);
534 if (desc
->pic_b
[font
]->current_count
>= desc
->pic_b
[font
]->current_alloc
) {
535 int newsize
= desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
*(desc
->pic_b
[font
]->current_alloc
+ALLOC_INCR
);
536 int increment
= desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
*ALLOC_INCR
;
537 desc
->pic_b
[font
]->current_alloc
+= ALLOC_INCR
;
539 // fprintf(stderr, "\nns = %d inc = %d\n", newsize, increment);
541 desc
->pic_b
[font
]->bmp
= realloc(desc
->pic_b
[font
]->bmp
, newsize
);
542 desc
->pic_a
[font
]->bmp
= realloc(desc
->pic_a
[font
]->bmp
, newsize
);
544 off
= desc
->pic_b
[font
]->current_count
*desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
;
545 memset(desc
->pic_b
[font
]->bmp
+off
, 0, increment
);
546 memset(desc
->pic_a
[font
]->bmp
+off
, 0, increment
);
549 abuffer
= desc
->pic_a
[font
]->bmp
;
550 bbuffer
= desc
->pic_b
[font
]->bmp
;
552 off
= desc
->pic_b
[font
]->current_count
*desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
;
554 paste_bitmap(bbuffer
+off
,
556 desc
->pic_b
[font
]->padding
+ glyph
->left
,
557 desc
->pic_b
[font
]->baseline
- glyph
->top
,
558 desc
->pic_b
[font
]->charwidth
, desc
->pic_b
[font
]->charheight
,
559 glyph
->bitmap
.width
<= maxw
? glyph
->bitmap
.width
: maxw
);
561 // fprintf(stderr, "glyph pasted\n");
562 FT_Done_Glyph((FT_Glyph
)glyph
);
565 pen_xa
= f266ToInt(slot
->advance
.x
) + 2*desc
->pic_b
[font
]->padding
;
566 if (pen_xa
> maxw
) pen_xa
= maxw
;
568 desc
->start
[c
] = off
;
569 width
= desc
->width
[c
] = pen_xa
;
570 height
= desc
->pic_b
[font
]->charheight
;
571 stride
= desc
->pic_b
[font
]->w
;
573 if (desc
->tables
.o_r
== 0) {
574 outline0(bbuffer
+off
, abuffer
+off
, width
, height
, stride
);
575 } else if (desc
->tables
.o_r
== 1) {
576 outline1(bbuffer
+off
, abuffer
+off
, width
, height
, stride
);
578 outline(bbuffer
+off
, abuffer
+off
, width
, height
, stride
,
579 desc
->tables
.omt
, desc
->tables
.o_r
, desc
->tables
.o_w
,
580 desc
->tables
.o_size
);
582 // fprintf(stderr, "fg: outline t = %lf\n", GetTimer()-t);
584 if (desc
->tables
.g_r
) {
585 blur(abuffer
+off
, desc
->tables
.tmp
, width
, height
, stride
,
586 desc
->tables
.gt2
, desc
->tables
.g_r
,
588 // fprintf(stderr, "fg: blur t = %lf\n", GetTimer()-t);
591 resample_alpha(abuffer
+off
, bbuffer
+off
, width
, height
, stride
, font_factor
);
593 desc
->pic_b
[font
]->current_count
++;
597 static int prepare_font(font_desc_t
*desc
, FT_Face face
, float ppem
, int pic_idx
,
598 int charset_size
, FT_ULong
*charset
, FT_ULong
*charcodes
, int unicode
,
599 double thickness
, double radius
)
602 int padding
= ceil(radius
) + ceil(thickness
);
604 desc
->faces
[pic_idx
] = face
;
606 desc
->pic_a
[pic_idx
] = malloc(sizeof(raw_file
));
607 if (!desc
->pic_a
[pic_idx
]) return -1;
608 desc
->pic_b
[pic_idx
] = malloc(sizeof(raw_file
));
609 if (!desc
->pic_b
[pic_idx
]) return -1;
611 desc
->pic_a
[pic_idx
]->bmp
= NULL
;
612 desc
->pic_a
[pic_idx
]->pal
= NULL
;
613 desc
->pic_b
[pic_idx
]->bmp
= NULL
;
614 desc
->pic_b
[pic_idx
]->pal
= NULL
;
616 desc
->pic_a
[pic_idx
]->pal
= malloc(sizeof(unsigned char)*256*3);
617 if (!desc
->pic_a
[pic_idx
]->pal
) return -1;
618 for (i
= 0; i
<768; ++i
) desc
->pic_a
[pic_idx
]->pal
[i
] = i
/3;
620 desc
->pic_b
[pic_idx
]->pal
= malloc(sizeof(unsigned char)*256*3);
621 if (!desc
->pic_b
[pic_idx
]->pal
) return -1;
622 for (i
= 0; i
<768; ++i
) desc
->pic_b
[pic_idx
]->pal
[i
] = i
/3;
624 // ttime = GetTimer();
625 err
= check_font(desc
, ppem
, padding
, pic_idx
, charset_size
, charset
, charcodes
, unicode
);
626 // ttime=GetTimer()-ttime;
627 // printf("render: %7lf us\n",ttime);
629 // fprintf(stderr, "fg: render t = %lf\n", GetTimer()-t);
631 desc
->pic_a
[pic_idx
]->w
= desc
->pic_b
[pic_idx
]->w
;
632 desc
->pic_a
[pic_idx
]->h
= desc
->pic_b
[pic_idx
]->h
;
633 desc
->pic_a
[pic_idx
]->c
= colors
;
635 desc
->pic_a
[pic_idx
]->bmp
= NULL
;
637 // fprintf(stderr, "fg: w = %d, h = %d\n", desc->pic_a[pic_idx]->w, desc->pic_a[pic_idx]->h);
642 static int generate_tables(font_desc_t
*desc
, double thickness
, double radius
)
644 int width
= desc
->max_height
;
645 int height
= desc
->max_width
;
647 double A
= log(1.0/base
)/(radius
*radius
*2);
649 double volume_diff
, volume_factor
= 0;
652 desc
->tables
.g_r
= ceil(radius
);
653 desc
->tables
.o_r
= ceil(thickness
);
654 desc
->tables
.g_w
= 2*desc
->tables
.g_r
+1;
655 desc
->tables
.o_w
= 2*desc
->tables
.o_r
+1;
656 desc
->tables
.o_size
= desc
->tables
.o_w
* desc
->tables
.o_w
;
658 // fprintf(stderr, "o_r = %d\n", desc->tables.o_r);
660 if (desc
->tables
.g_r
) {
661 desc
->tables
.g
= malloc(desc
->tables
.g_w
* sizeof(unsigned));
662 desc
->tables
.gt2
= malloc(256 * desc
->tables
.g_w
* sizeof(unsigned));
663 if (desc
->tables
.g
==NULL
|| desc
->tables
.gt2
==NULL
) {
667 desc
->tables
.om
= malloc(desc
->tables
.o_w
*desc
->tables
.o_w
* sizeof(unsigned));
668 desc
->tables
.omt
= malloc(desc
->tables
.o_size
*256);
670 omtp
= desc
->tables
.omt
;
671 desc
->tables
.tmp
= malloc((width
+1)*height
*sizeof(short));
673 if (desc
->tables
.om
==NULL
|| desc
->tables
.omt
==NULL
|| desc
->tables
.tmp
==NULL
) {
677 if (desc
->tables
.g_r
) {
678 // gaussian curve with volume = 256
679 for (volume_diff
=10000000; volume_diff
>0.0000001; volume_diff
*=0.5){
680 volume_factor
+= volume_diff
;
681 desc
->tables
.volume
=0;
682 for (i
= 0; i
<desc
->tables
.g_w
; ++i
) {
683 desc
->tables
.g
[i
] = (unsigned)(exp(A
* (i
-desc
->tables
.g_r
)*(i
-desc
->tables
.g_r
)) * volume_factor
+ .5);
684 desc
->tables
.volume
+= desc
->tables
.g
[i
];
686 if(desc
->tables
.volume
>256) volume_factor
-= volume_diff
;
688 desc
->tables
.volume
=0;
689 for (i
= 0; i
<desc
->tables
.g_w
; ++i
) {
690 desc
->tables
.g
[i
] = (unsigned)(exp(A
* (i
-desc
->tables
.g_r
)*(i
-desc
->tables
.g_r
)) * volume_factor
+ .5);
691 desc
->tables
.volume
+= desc
->tables
.g
[i
];
695 for(mx
=0;mx
<desc
->tables
.g_w
;mx
++){
697 desc
->tables
.gt2
[mx
+i
*desc
->tables
.g_w
] = i
*desc
->tables
.g
[mx
];
703 for (my
= 0; my
<desc
->tables
.o_w
; ++my
) {
704 for (mx
= 0; mx
<desc
->tables
.o_w
; ++mx
) {
705 // antialiased circle would be perfect here, but this one is good enough
706 double d
= thickness
+ 1 - sqrt((mx
-desc
->tables
.o_r
)*(mx
-desc
->tables
.o_r
)+(my
-desc
->tables
.o_r
)*(my
-desc
->tables
.o_r
));
707 desc
->tables
.om
[mx
+my
*desc
->tables
.o_w
] = d
>=1 ? base
: d
<=0 ? 0 : (d
*base
+ .5);
713 for(mx
=0;mx
<desc
->tables
.o_size
;mx
++) *(omtp
++) = (i
*desc
->tables
.om
[mx
] + (base
/2))/base
;
720 /* decode from 'encoding' to unicode */
721 static FT_ULong
decode_char(iconv_t
*cd
, char c
) {
724 char *outbuf
= (char*)&o
;
725 size_t inbytesleft
= 1;
726 size_t outbytesleft
= sizeof(FT_ULong
);
728 iconv(*cd
, &inbuf
, &inbytesleft
, &outbuf
, &outbytesleft
);
730 /* convert unicode BigEndian -> MachineEndian */
733 // if (count==-1) o = 0; // not OK, at least my iconv() returns E2BIG for all
734 if (outbytesleft
!=0) o
= 0;
736 /* we don't want control characters */
737 if (o
>=0x7f && o
<0xa0) o
= 0;
741 static int prepare_charset(char *charmap
, char *encoding
, FT_ULong
*charset
, FT_ULong
*charcodes
) {
747 // check if ucs-4 is available
748 cd
= iconv_open(charmap
, charmap
);
749 if (cd
==(iconv_t
)-1) {
750 mp_msg(MSGT_OSD
, MSGL_ERR
, "iconv doesn't know %s encoding. Use the source!\n", charmap
);
756 cd
= iconv_open(charmap
, encoding
);
757 if (cd
==(iconv_t
)-1) {
758 mp_msg(MSGT_OSD
, MSGL_ERR
, "Unsupported encoding `%s', use iconv --list to list character sets known on your system.\n", encoding
);
762 charset_size
= 256 - first_char
;
763 for (i
= 0; i
<charset_size
; ++i
) {
764 charcodes
[count
] = i
+first_char
;
765 charset
[count
] = decode_char(&cd
, i
+first_char
);
766 if (charset
[count
]!=0) ++count
;
768 charcodes
[count
] = charset
[count
] = 0; ++count
;
769 charset_size
= count
;
772 if (charset_size
==0) {
773 mp_msg(MSGT_OSD
, MSGL_ERR
, "No characters to render!\n");
780 static int prepare_charset_unicode(FT_Face face
, FT_ULong
*charset
, FT_ULong
*charcodes
) {
781 #ifdef HAVE_FREETYPE21
789 if (face
->charmap
==NULL
|| face
->charmap
->encoding
!=ft_encoding_unicode
) {
790 WARNING("Unicode charmap not available for this font. Very bad!");
793 #ifdef HAVE_FREETYPE21
795 charcode
= FT_Get_First_Char( face
, &gindex
);
796 while (gindex
!= 0) {
797 if (charcode
< 65536 && charcode
>= 33) { // sanity check
798 charset
[i
] = charcode
;
802 charcode
= FT_Get_Next_Char( face
, charcode
, &gindex
);
805 // for FT < 2.1 we have to use brute force enumeration
807 for (j
= 33; j
< 65536; j
++) {
808 gindex
= FT_Get_Char_Index(face
, j
);
816 mp_msg(MSGT_OSD
, MSGL_V
, "Unicode font: %d glyphs.\n", i
);
822 static font_desc_t
* init_font_desc(void)
827 desc
= malloc(sizeof(font_desc_t
));
828 if(!desc
) return NULL
;
829 memset(desc
,0,sizeof(font_desc_t
));
833 /* setup sane defaults */
839 desc
->spacewidth
= 0;
842 desc
->max_height
= 0;
844 desc
->tables
.g
= NULL
;
845 desc
->tables
.gt2
= NULL
;
846 desc
->tables
.om
= NULL
;
847 desc
->tables
.omt
= NULL
;
848 desc
->tables
.tmp
= NULL
;
850 for(i
= 0; i
< 65536; i
++)
851 desc
->start
[i
] = desc
->width
[i
] = desc
->font
[i
] = -1;
852 for(i
= 0; i
< 16; i
++)
853 desc
->pic_a
[i
] = desc
->pic_b
[i
] = NULL
;
858 void free_font_desc(font_desc_t
*desc
)
864 // if (!desc->dynamic) return; // some vo_aa crap, better leaking than crashing
866 if (desc
->name
) free(desc
->name
);
867 if (desc
->fpath
) free(desc
->fpath
);
869 for(i
= 0; i
< 16; i
++) {
870 if (desc
->pic_a
[i
]) {
871 if (desc
->pic_a
[i
]->bmp
) free(desc
->pic_a
[i
]->bmp
);
872 if (desc
->pic_a
[i
]->pal
) free(desc
->pic_a
[i
]->pal
);
873 free (desc
->pic_a
[i
]);
875 if (desc
->pic_b
[i
]) {
876 if (desc
->pic_b
[i
]->bmp
) free(desc
->pic_b
[i
]->bmp
);
877 if (desc
->pic_b
[i
]->pal
) free(desc
->pic_b
[i
]->pal
);
878 free (desc
->pic_b
[i
]);
882 if (desc
->tables
.g
) free(desc
->tables
.g
);
883 if (desc
->tables
.gt2
) free(desc
->tables
.gt2
);
884 if (desc
->tables
.om
) free(desc
->tables
.om
);
885 if (desc
->tables
.omt
) free(desc
->tables
.omt
);
886 if (desc
->tables
.tmp
) free(desc
->tables
.tmp
);
888 for(i
= 0; i
< desc
->face_cnt
; i
++) {
889 FT_Done_Face(desc
->faces
[i
]);
895 static int load_sub_face(const char *name
, FT_Face
*face
)
899 if (name
) err
= FT_New_Face(library
, name
, 0, face
);
902 char *font_file
= get_path("subfont.ttf");
903 err
= FT_New_Face(library
, font_file
, 0, face
);
906 err
= FT_New_Face(library
, MPLAYER_DATADIR
"/subfont.ttf", 0, face
);
908 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_NewFaceFailed
);
916 static int load_osd_face(FT_Face
*face
)
918 if ( FT_New_Memory_Face(library
, osd_font_pfb
, sizeof(osd_font_pfb
), 0, face
) ) {
919 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_NewMemoryFaceFailed
);
925 int kerning(font_desc_t
*desc
, int prevc
, int c
)
929 if (!desc
->dynamic
) return 0;
930 if (prevc
< 0 || c
< 0) return 0;
931 if (desc
->font
[prevc
] != desc
->font
[c
]) return 0;
932 if (desc
->font
[prevc
] == -1 || desc
->font
[c
] == -1) return 0;
933 FT_Get_Kerning(desc
->faces
[desc
->font
[c
]],
934 desc
->glyph_index
[prevc
], desc
->glyph_index
[c
],
935 ft_kerning_default
, &kern
);
937 // fprintf(stderr, "kern: %c %c %d\n", prevc, c, f266ToInt(kern.x));
939 return f266ToInt(kern
.x
);
942 font_desc_t
* read_font_desc_ft(const char *fname
, int movie_width
, int movie_height
)
944 font_desc_t
*desc
= NULL
;
948 FT_ULong
*my_charset
= malloc(MAX_CHARSET_SIZE
* sizeof(FT_ULong
)); /* characters we want to render; Unicode */
949 FT_ULong
*my_charcodes
= malloc(MAX_CHARSET_SIZE
* sizeof(FT_ULong
)); /* character codes in 'encoding' */
951 char *charmap
= "ucs-4";
959 float subtitle_font_ppem
;
962 if (my_charset
== NULL
|| my_charcodes
== NULL
) {
963 mp_msg(MSGT_OSD
, MSGL_ERR
, "subtitle font: malloc failed.\n");
967 switch (subtitle_autoscale
) {
969 movie_size
= movie_height
;
972 movie_size
= movie_width
;
975 movie_size
= sqrt(movie_height
*movie_height
+movie_width
*movie_width
);
982 subtitle_font_ppem
= movie_size
*text_font_scale_factor
/100.0;
983 osd_font_ppem
= movie_size
*osd_font_scale_factor
/100.0;
985 if (subtitle_font_ppem
< 5) subtitle_font_ppem
= 5;
986 if (osd_font_ppem
< 5) osd_font_ppem
= 5;
988 if (subtitle_font_ppem
> 128) subtitle_font_ppem
= 128;
989 if (osd_font_ppem
> 128) osd_font_ppem
= 128;
991 if ((subtitle_font_encoding
== NULL
)
992 || (strcasecmp(subtitle_font_encoding
, "unicode") == 0)) {
998 desc
= init_font_desc();
999 if(!desc
) goto err_out
;
1003 /* generate the subtitle font */
1004 err
= load_sub_face(fname
, &face
);
1006 mp_msg(MSGT_OSD
, MSGL_WARN
, MSGTR_LIBVO_FONT_LOAD_FT_SubFaceFailed
);
1013 charset_size
= prepare_charset_unicode(face
, my_charset
, my_charcodes
);
1015 if (subtitle_font_encoding
) {
1016 charset_size
= prepare_charset(charmap
, subtitle_font_encoding
, my_charset
, my_charcodes
);
1018 charset_size
= prepare_charset(charmap
, "iso-8859-1", my_charset
, my_charcodes
);
1022 if (charset_size
< 0) {
1023 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_SubFontCharsetFailed
);
1030 // fprintf(stderr, "fg: prepare t = %lf\n", GetTimer()-t);
1032 err
= prepare_font(desc
, face
, subtitle_font_ppem
, desc
->face_cnt
-1,
1033 charset_size
, my_charset
, my_charcodes
, unicode
,
1034 subtitle_font_thickness
, subtitle_font_radius
);
1037 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_CannotPrepareSubtitleFont
);
1043 /* generate the OSD font */
1044 err
= load_osd_face(&face
);
1050 err
= prepare_font(desc
, face
, osd_font_ppem
, desc
->face_cnt
-1,
1051 OSD_CHARSET_SIZE
, osd_charset
, osd_charcodes
, 0,
1052 subtitle_font_thickness
, subtitle_font_radius
);
1055 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_CannotPrepareOSDFont
);
1059 err
= generate_tables(desc
, subtitle_font_thickness
, subtitle_font_radius
);
1062 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_CannotGenerateTables
);
1068 desc
->width
[' ']=desc
->spacewidth
;
1071 if (desc
->font
[j
] < 0) j
= '?';
1072 if (desc
->font
[j
] < 0) j
= ' ';
1073 render_one_glyph(desc
, j
);
1074 for(i
= 0; i
< 65536; i
++) {
1075 if (desc
->font
[i
] < 0 && i
!= ' ') {
1076 desc
->start
[i
] = desc
->start
[j
];
1077 desc
->width
[i
] = desc
->width
[j
];
1078 desc
->font
[i
] = desc
->font
[j
];
1087 free_font_desc(desc
);
1093 int init_freetype(void)
1097 /* initialize freetype */
1098 err
= FT_Init_FreeType(&library
);
1100 mp_msg(MSGT_OSD
, MSGL_ERR
, "Init_FreeType failed.\n");
1103 mp_msg(MSGT_OSD
, MSGL_V
, "init_freetype\n");
1108 int done_freetype(void)
1112 if (!using_freetype
)
1115 err
= FT_Done_FreeType(library
);
1117 mp_msg(MSGT_OSD
, MSGL_ERR
, MSGTR_LIBVO_FONT_LOAD_FT_DoneFreeTypeFailed
);
1124 void load_font_ft(int width
, int height
, font_desc_t
** fontp
, const char *font_name
)
1126 #ifdef HAVE_FONTCONFIG
1127 FcPattern
*fc_pattern
;
1128 FcPattern
*fc_pattern2
;
1132 font_desc_t
*vo_font
= *fontp
;
1133 vo_image_width
= width
;
1134 vo_image_height
= height
;
1136 // protection against vo_aa font hacks
1137 if (vo_font
&& !vo_font
->dynamic
) return;
1139 if (vo_font
) free_font_desc(vo_font
);
1141 #ifdef HAVE_FONTCONFIG
1142 if (font_fontconfig
> 0)
1145 font_name
= strdup("sans-serif");
1147 fc_pattern
= FcNameParse(font_name
);
1148 FcConfigSubstitute(0, fc_pattern
, FcMatchPattern
);
1149 FcDefaultSubstitute(fc_pattern
);
1150 fc_pattern2
= fc_pattern
;
1151 fc_pattern
= FcFontMatch(0, fc_pattern
, 0);
1152 FcPatternDestroy(fc_pattern2
);
1153 FcPatternGetBool(fc_pattern
, FC_SCALABLE
, 0, &scalable
);
1154 if (scalable
!= FcTrue
) {
1155 FcPatternDestroy(fc_pattern
);
1156 fc_pattern
= FcNameParse("sans-serif");
1157 FcConfigSubstitute(0, fc_pattern
, FcMatchPattern
);
1158 FcDefaultSubstitute(fc_pattern
);
1159 fc_pattern2
= fc_pattern
;
1160 fc_pattern
= FcFontMatch(0, fc_pattern
, 0);
1161 FcPatternDestroy(fc_pattern2
);
1163 // s doesn't need to be freed according to fontconfig docs
1164 FcPatternGetString(fc_pattern
, FC_FILE
, 0, &s
);
1165 *fontp
=read_font_desc_ft(s
, width
, height
);
1166 FcPatternDestroy(fc_pattern
);
1170 *fontp
=read_font_desc_ft(font_name
, width
, height
);