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 <makovick@gmail.com>
9 * This file is part of MPlayer.
11 * MPlayer is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * MPlayer is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
38 #include FT_FREETYPE_H
41 #ifdef CONFIG_FONTCONFIG
42 #include <fontconfig/fontconfig.h>
45 #include "libavutil/common.h"
47 #include "font_load.h"
53 #if (FREETYPE_MAJOR > 2) || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 1)
54 #define HAVE_FREETYPE21
57 char *subtitle_font_encoding
= NULL
;
58 float text_font_scale_factor
= 3.5;
59 float osd_font_scale_factor
= 4.0;
60 float subtitle_font_radius
= 2.0;
61 float subtitle_font_thickness
= 2.0;
66 int subtitle_autoscale
= 3;
68 int vo_image_width
= 0;
69 int vo_image_height
= 0;
72 int using_freetype
= 0;
73 #ifdef CONFIG_FONTCONFIG
74 int font_fontconfig
= 1;
76 int font_fontconfig
= -1;
80 static unsigned int const colors
= 256;
81 static unsigned int const maxcolor
= 255;
82 static unsigned const base
= 256;
83 static unsigned const first_char
= 33;
84 #define MAX_CHARSET_SIZE 60000
86 static FT_Library library
;
88 #define OSD_CHARSET_SIZE 15
90 static const FT_ULong osd_charset
[OSD_CHARSET_SIZE
] =
92 0xe001, 0xe002, 0xe003, 0xe004, 0xe005, 0xe006, 0xe007, 0xe008,
93 0xe009, 0xe00a, 0xe00b, 0xe010, 0xe011, 0xe012, 0xe013
96 static const FT_ULong osd_charcodes
[OSD_CHARSET_SIZE
] =
98 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
99 0x09,0x0a,0x0b,0x10,0x11,0x12,0x13
102 #define f266ToInt(x) (((x)+32)>>6) // round fractional fixed point number to integer
103 // coordinates are in 26.6 pixels (i.e. 1/64th of pixels)
104 #define f266CeilToInt(x) (((x)+63)>>6) // ceiling
105 #define f266FloorToInt(x) ((x)>>6) // floor
106 #define f1616ToInt(x) (((x)+0x8000)>>16) // 16.16
107 #define floatTof266(x) ((int)((x)*(1<<6)+0.5))
109 #define ALIGN(x) (((x)+7)&~7) // 8 byte align
111 #define WARNING(msg, args...) mp_msg(MSGT_OSD, MSGL_WARN, msg "\n", ## args)
115 //static double ttime;
118 static void paste_bitmap(unsigned char *bbuffer
, FT_Bitmap
*bitmap
, int x
, int y
, int width
, int height
, int bwidth
) {
119 int drow
= x
+y
*width
;
122 if (bitmap
->pixel_mode
==ft_pixel_mode_mono
)
123 for (h
= bitmap
->rows
; h
>0 && height
> 0; --h
, height
--, drow
+=width
, srow
+=bitmap
->pitch
)
124 for (w
= bwidth
, sp
=dp
=0; w
>0; --w
, ++dp
, ++sp
)
125 bbuffer
[drow
+dp
] = (bitmap
->buffer
[srow
+sp
/8] & (0x80>>(sp
%8))) ? 255:0;
127 for (h
= bitmap
->rows
; h
>0 && height
> 0; --h
, height
--, drow
+=width
, srow
+=bitmap
->pitch
)
128 for (w
= bwidth
, sp
=dp
=0; w
>0; --w
, ++dp
, ++sp
)
129 bbuffer
[drow
+dp
] = bitmap
->buffer
[srow
+sp
];
133 static int check_font(font_desc_t
*desc
, float ppem
, int padding
, int pic_idx
,
134 int charset_size
, const FT_ULong
*charset
,
135 const FT_ULong
*charcodes
, int unicode
)
138 FT_Face face
= desc
->faces
[pic_idx
];
139 int const load_flags
= FT_LOAD_DEFAULT
;
140 int ymin
= INT_MAX
, ymax
= INT_MIN
;
141 int space_advance
= 20;
143 unsigned char *bbuffer
;
144 int i
, uni_charmap
= 1;
146 error
= FT_Select_Charmap(face
, ft_encoding_unicode
);
147 // fprintf(stderr, "select unicode charmap: %d\n", error);
149 if (face
->charmap
==NULL
|| face
->charmap
->encoding
!=ft_encoding_unicode
) {
150 WARNING("Unicode charmap not available for this font. Very bad!");
152 error
= FT_Set_Charmap(face
, face
->charmaps
[0]);
153 if (error
) WARNING("No charmaps! Strange.");
157 if (FT_IS_SCALABLE(face
)) {
158 error
= FT_Set_Char_Size(face
, 0, floatTof266(ppem
), 0, 0);
159 if (error
) WARNING("FT_Set_Char_Size failed.");
162 int jppem
= face
->available_sizes
[0].height
;
163 /* find closest size */
164 for (i
= 0; i
<face
->num_fixed_sizes
; ++i
) {
165 if (fabs(face
->available_sizes
[i
].height
- ppem
) < abs(face
->available_sizes
[i
].height
- jppem
)) {
167 jppem
= face
->available_sizes
[i
].height
;
170 WARNING("Selected font is not scalable. Using ppem=%i.", face
->available_sizes
[j
].height
);
171 error
= FT_Set_Pixel_Sizes(face
, face
->available_sizes
[j
].width
, face
->available_sizes
[j
].height
);
172 if (error
) WARNING("FT_Set_Pixel_Sizes failed.");
175 if (FT_IS_FIXED_WIDTH(face
))
176 WARNING("Selected font is fixed-width.");
178 /* compute space advance */
179 error
= FT_Load_Char(face
, ' ', load_flags
);
180 if (error
) WARNING("spacewidth set to default.");
181 else space_advance
= f266ToInt(face
->glyph
->advance
.x
);
183 if (!desc
->spacewidth
) desc
->spacewidth
= 2*padding
+ space_advance
;
184 if (!desc
->charspace
) desc
->charspace
= -2*padding
;
185 if (!desc
->height
) desc
->height
= f266ToInt(face
->size
->metrics
.height
);
188 for (i
= 0; i
<charset_size
; ++i
) {
189 FT_ULong character
, code
;
192 character
= charset
[i
];
194 desc
->font
[unicode
?character
:code
] = pic_idx
;
199 glyph_index
= FT_Get_Char_Index(face
, uni_charmap
? character
:code
);
200 if (glyph_index
==0) {
201 WARNING("Glyph for char 0x%02lx|U+%04lX|%c not found.", code
, character
,
202 code
<' '||code
>255 ? '.':(char)code
);
203 desc
->font
[unicode
?character
:code
] = -1;
207 desc
->glyph_index
[unicode
?character
:code
] = glyph_index
;
209 // fprintf(stderr, "font height: %f\n", (double)(face->bbox.yMax-face->bbox.yMin)/(double)face->units_per_EM*ppem);
210 // fprintf(stderr, "font width: %f\n", (double)(face->bbox.xMax-face->bbox.xMin)/(double)face->units_per_EM*ppem);
212 ymax
= (double)(face
->bbox
.yMax
)/(double)face
->units_per_EM
*ppem
+1;
213 ymin
= (double)(face
->bbox
.yMin
)/(double)face
->units_per_EM
*ppem
-1;
215 width
= ppem
*(face
->bbox
.xMax
-face
->bbox
.xMin
)/face
->units_per_EM
+3+2*padding
;
216 if (desc
->max_width
< width
) desc
->max_width
= width
;
217 width
= ALIGN(width
);
218 desc
->pic_b
[pic_idx
]->charwidth
= width
;
221 mp_msg(MSGT_OSD
, MSGL_ERR
, "Wrong bounding box, width <= 0 !\n");
226 mp_msg(MSGT_OSD
, MSGL_ERR
, "Something went wrong. Use the source!\n");
230 height
= ymax
- ymin
+ 2*padding
;
232 mp_msg(MSGT_OSD
, MSGL_ERR
, "Wrong bounding box, height <= 0 !\n");
236 if (desc
->max_height
< height
) desc
->max_height
= height
;
237 desc
->pic_b
[pic_idx
]->charheight
= height
;
239 // fprintf(stderr, "font height2: %d\n", height);
240 desc
->pic_b
[pic_idx
]->baseline
= ymax
+ padding
;
241 desc
->pic_b
[pic_idx
]->padding
= padding
;
242 desc
->pic_b
[pic_idx
]->current_alloc
= 0;
243 desc
->pic_b
[pic_idx
]->current_count
= 0;
247 desc
->pic_b
[pic_idx
]->w
= width
;
248 desc
->pic_b
[pic_idx
]->h
= height
;
249 desc
->pic_b
[pic_idx
]->c
= colors
;
250 desc
->pic_b
[pic_idx
]->bmp
= bbuffer
;
251 desc
->pic_b
[pic_idx
]->pen
= 0;
269 for (y
= 0; y
<height
; y
++) {
270 for (x
= 0; x
<width
; x
++) {
274 const int x1
=(x
<r
) ? r
-x
: 0;
275 const int y1
=(y
<r
) ? r
-y
: 0;
276 const int x2
=(x
+r
>=width
) ? r
+width
-x
: 2*r
+1;
277 const int y2
=(y
+r
>=height
) ? r
+height
-y
: 2*r
+1;
278 register unsigned char *dstp
= t
+ (y1
+y
-r
)* stride
+ x
-r
;
279 //register int *mp = m + y1 *mwidth;
280 register unsigned char *mp
= m
+ msize
*src
+ y1
*mwidth
;
283 for(my
= y1
; my
<y2
; my
++){
285 for(mx
= x1
; mx
<x2
; mx
++){
286 if(dstp
[mx
] < mp
[mx
]) dstp
[mx
]= mp
[mx
];
299 static void outline1(
307 int skip
= stride
-width
;
309 for (x
= 0; x
<width
; ++x
, ++s
, ++t
) *t
= *s
;
312 for (y
= 1; y
<height
-1; ++y
) {
314 for (x
= 1; x
<width
-1; ++x
, ++s
, ++t
) {
327 *t
= v
>maxcolor
? maxcolor
: v
;
333 for (x
= 0; x
<width
; ++x
, ++s
, ++t
) *t
= *s
;
337 static void outline0(
344 for (y
= 0; y
<height
; ++y
) {
353 unsigned char *buffer
,
354 unsigned short *tmp2
,
364 unsigned char *s
= buffer
;
365 unsigned short *t
= tmp2
+1;
366 for(y
=0; y
<height
; y
++){
367 memset(t
-1, 0, (width
+1)*sizeof(short));
372 register unsigned short *dstp
= t
+ x
-r
;
374 unsigned *m3
= m2
+ src
*mwidth
;
375 for(mx
=r
-x
; mx
<mwidth
; mx
++){
381 for(; x
<width
-r
; x
++){
384 register unsigned short *dstp
= t
+ x
-r
;
386 unsigned *m3
= m2
+ src
*mwidth
;
387 for(mx
=0; mx
<mwidth
; mx
++){
396 register unsigned short *dstp
= t
+ x
-r
;
398 const int x2
= r
+width
-x
;
399 unsigned *m3
= m2
+ src
*mwidth
;
400 for(mx
=0; mx
<x2
; mx
++){
411 for(x
=0; x
<width
; x
++){
413 unsigned short *srcp
= t
+ y
*(width
+1) + 1;
416 register unsigned short *dstp
= srcp
- 1 + width
+1;
417 const int src2
= (src
+ 128)>>8;
418 unsigned *m3
= m2
+ src2
*mwidth
;
422 for(mx
=r
-1; mx
<mwidth
; mx
++){
428 for(; y
<height
-r
; y
++){
429 unsigned short *srcp
= t
+ y
*(width
+1) + 1;
432 register unsigned short *dstp
= srcp
- 1 - r
*(width
+1);
433 const int src2
= (src
+ 128)>>8;
434 unsigned *m3
= m2
+ src2
*mwidth
;
438 for(mx
=0; mx
<mwidth
; mx
++){
444 for(; y
<height
; y
++){
445 unsigned short *srcp
= t
+ y
*(width
+1) + 1;
448 const int y2
=r
+height
-y
;
449 register unsigned short *dstp
= srcp
- 1 - r
*(width
+1);
450 const int src2
= (src
+ 128)>>8;
451 unsigned *m3
= m2
+ src2
*mwidth
;
455 for(mx
=0; mx
<y2
; mx
++){
466 for(y
=0; y
<height
; y
++){
467 for(x
=0; x
<width
; x
++){
475 static void resample_alpha(unsigned char *abuf
, unsigned char *bbuf
, int width
, int height
, int stride
, float factor
)
479 for (i
= 0; i
< height
; i
++) {
480 unsigned char *a
= abuf
+i
*stride
;
481 unsigned char *b
= bbuf
+i
*stride
;
482 for(j
=0;j
<width
;j
++,a
++,b
++){
485 x
=255-((x
*f
)>>8); // scale
486 if (x
+y
>255) x
=255-y
; // to avoid overflows
487 if (x
<1) x
=1; else if (x
>=252) x
=0;
493 #define ALLOC_INCR 32
494 void render_one_glyph(font_desc_t
*desc
, int c
)
498 FT_BitmapGlyph glyph
;
499 int width
, height
, stride
, maxw
, off
;
500 unsigned char *abuffer
, *bbuffer
;
502 int const load_flags
= FT_LOAD_DEFAULT
;
504 int font
= desc
->font
[c
];
507 // fprintf(stderr, "render_one_glyph %d\n", c);
509 if (!desc
->dynamic
) return;
510 if (desc
->width
[c
] != -1) return;
511 if (desc
->font
[c
] == -1) return;
513 glyph_index
= desc
->glyph_index
[c
];
516 error
= FT_Load_Glyph(desc
->faces
[font
], glyph_index
, load_flags
);
518 WARNING("FT_Load_Glyph 0x%02x (char 0x%04x) failed.", glyph_index
, c
);
522 slot
= desc
->faces
[font
]->glyph
;
525 if (slot
->format
!= ft_glyph_format_bitmap
) {
526 error
= FT_Render_Glyph(slot
, ft_render_mode_normal
);
528 WARNING("FT_Render_Glyph 0x%04x (char 0x%04x) failed.", glyph_index
, c
);
534 // extract glyph image
535 error
= FT_Get_Glyph(slot
, (FT_Glyph
*)&glyph
);
537 WARNING("FT_Get_Glyph 0x%04x (char 0x%04x) failed.", glyph_index
, c
);
542 // fprintf(stderr, "glyph generated\n");
544 maxw
= desc
->pic_b
[font
]->charwidth
;
546 if (glyph
->bitmap
.width
> maxw
) {
547 fprintf(stderr
, "glyph too wide!\n");
550 // allocate new memory, if needed
551 // fprintf(stderr, "\n%d %d %d\n", desc->pic_b[font]->charwidth, desc->pic_b[font]->charheight, desc->pic_b[font]->current_alloc);
552 if (desc
->pic_b
[font
]->current_count
>= desc
->pic_b
[font
]->current_alloc
) {
553 int newsize
= desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
*(desc
->pic_b
[font
]->current_alloc
+ALLOC_INCR
);
554 int increment
= desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
*ALLOC_INCR
;
555 desc
->pic_b
[font
]->current_alloc
+= ALLOC_INCR
;
557 // fprintf(stderr, "\nns = %d inc = %d\n", newsize, increment);
559 desc
->pic_b
[font
]->bmp
= realloc(desc
->pic_b
[font
]->bmp
, newsize
);
560 desc
->pic_a
[font
]->bmp
= realloc(desc
->pic_a
[font
]->bmp
, newsize
);
562 off
= desc
->pic_b
[font
]->current_count
*desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
;
563 memset(desc
->pic_b
[font
]->bmp
+off
, 0, increment
);
564 memset(desc
->pic_a
[font
]->bmp
+off
, 0, increment
);
567 abuffer
= desc
->pic_a
[font
]->bmp
;
568 bbuffer
= desc
->pic_b
[font
]->bmp
;
570 off
= desc
->pic_b
[font
]->current_count
*desc
->pic_b
[font
]->charwidth
*desc
->pic_b
[font
]->charheight
;
572 paste_bitmap(bbuffer
+off
,
574 desc
->pic_b
[font
]->padding
+ glyph
->left
,
575 desc
->pic_b
[font
]->baseline
- glyph
->top
,
576 desc
->pic_b
[font
]->charwidth
, desc
->pic_b
[font
]->charheight
,
577 glyph
->bitmap
.width
<= maxw
? glyph
->bitmap
.width
: maxw
);
579 // fprintf(stderr, "glyph pasted\n");
580 FT_Done_Glyph((FT_Glyph
)glyph
);
583 pen_xa
= f266ToInt(slot
->advance
.x
) + 2*desc
->pic_b
[font
]->padding
;
584 if (pen_xa
> maxw
) pen_xa
= maxw
;
586 desc
->start
[c
] = off
;
587 width
= desc
->width
[c
] = pen_xa
;
588 height
= desc
->pic_b
[font
]->charheight
;
589 stride
= desc
->pic_b
[font
]->w
;
591 if (desc
->tables
.o_r
== 0) {
592 outline0(bbuffer
+off
, abuffer
+off
, width
, height
, stride
);
593 } else if (desc
->tables
.o_r
== 1) {
594 outline1(bbuffer
+off
, abuffer
+off
, width
, height
, stride
);
596 outline(bbuffer
+off
, abuffer
+off
, width
, height
, stride
,
597 desc
->tables
.omt
, desc
->tables
.o_r
, desc
->tables
.o_w
,
598 desc
->tables
.o_size
);
600 // fprintf(stderr, "fg: outline t = %f\n", GetTimer()-t);
602 if (desc
->tables
.g_r
) {
603 blur(abuffer
+off
, desc
->tables
.tmp
, width
, height
, stride
,
604 desc
->tables
.gt2
, desc
->tables
.g_r
,
606 // fprintf(stderr, "fg: blur t = %f\n", GetTimer()-t);
609 resample_alpha(abuffer
+off
, bbuffer
+off
, width
, height
, stride
, font_factor
);
611 desc
->pic_b
[font
]->current_count
++;
615 static int prepare_font(font_desc_t
*desc
, FT_Face face
, float ppem
,
616 int pic_idx
, int charset_size
,
617 const FT_ULong
*charset
, const FT_ULong
*charcodes
,
618 int unicode
, double thickness
, double radius
)
621 int padding
= ceil(radius
) + ceil(thickness
);
623 desc
->faces
[pic_idx
] = face
;
625 desc
->pic_a
[pic_idx
] = malloc(sizeof(raw_file
));
626 if (!desc
->pic_a
[pic_idx
]) return -1;
627 desc
->pic_b
[pic_idx
] = malloc(sizeof(raw_file
));
628 if (!desc
->pic_b
[pic_idx
]) return -1;
630 desc
->pic_a
[pic_idx
]->bmp
= NULL
;
631 desc
->pic_a
[pic_idx
]->pal
= NULL
;
632 desc
->pic_b
[pic_idx
]->bmp
= NULL
;
633 desc
->pic_b
[pic_idx
]->pal
= NULL
;
635 desc
->pic_a
[pic_idx
]->pal
= malloc(sizeof(unsigned char)*256*3);
636 if (!desc
->pic_a
[pic_idx
]->pal
) return -1;
637 for (i
= 0; i
<768; ++i
) desc
->pic_a
[pic_idx
]->pal
[i
] = i
/3;
639 desc
->pic_b
[pic_idx
]->pal
= malloc(sizeof(unsigned char)*256*3);
640 if (!desc
->pic_b
[pic_idx
]->pal
) return -1;
641 for (i
= 0; i
<768; ++i
) desc
->pic_b
[pic_idx
]->pal
[i
] = i
/3;
643 // ttime = GetTimer();
644 err
= check_font(desc
, ppem
, padding
, pic_idx
, charset_size
, charset
, charcodes
, unicode
);
645 // ttime=GetTimer()-ttime;
646 // printf("render: %7f us\n",ttime);
648 // fprintf(stderr, "fg: render t = %f\n", GetTimer()-t);
650 desc
->pic_a
[pic_idx
]->w
= desc
->pic_b
[pic_idx
]->w
;
651 desc
->pic_a
[pic_idx
]->h
= desc
->pic_b
[pic_idx
]->h
;
652 desc
->pic_a
[pic_idx
]->c
= colors
;
654 desc
->pic_a
[pic_idx
]->bmp
= NULL
;
656 // fprintf(stderr, "fg: w = %d, h = %d\n", desc->pic_a[pic_idx]->w, desc->pic_a[pic_idx]->h);
661 static int generate_tables(font_desc_t
*desc
, double thickness
, double radius
)
663 int width
= desc
->max_height
;
664 int height
= desc
->max_width
;
666 double A
= log(1.0/base
)/(radius
*radius
*2);
668 double volume_diff
, volume_factor
= 0;
671 desc
->tables
.g_r
= ceil(radius
);
672 desc
->tables
.o_r
= ceil(thickness
);
673 desc
->tables
.g_w
= 2*desc
->tables
.g_r
+1;
674 desc
->tables
.o_w
= 2*desc
->tables
.o_r
+1;
675 desc
->tables
.o_size
= desc
->tables
.o_w
* desc
->tables
.o_w
;
677 // fprintf(stderr, "o_r = %d\n", desc->tables.o_r);
679 if (desc
->tables
.g_r
) {
680 desc
->tables
.g
= malloc(desc
->tables
.g_w
* sizeof(unsigned));
681 desc
->tables
.gt2
= malloc(256 * desc
->tables
.g_w
* sizeof(unsigned));
682 if (desc
->tables
.g
==NULL
|| desc
->tables
.gt2
==NULL
) {
686 desc
->tables
.om
= malloc(desc
->tables
.o_w
*desc
->tables
.o_w
* sizeof(unsigned));
687 desc
->tables
.omt
= malloc(desc
->tables
.o_size
*256);
689 omtp
= desc
->tables
.omt
;
690 desc
->tables
.tmp
= malloc((width
+1)*height
*sizeof(short));
692 if (desc
->tables
.om
==NULL
|| desc
->tables
.omt
==NULL
|| desc
->tables
.tmp
==NULL
) {
696 if (desc
->tables
.g_r
) {
697 // gaussian curve with volume = 256
698 for (volume_diff
=10000000; volume_diff
>0.0000001; volume_diff
*=0.5){
699 volume_factor
+= volume_diff
;
700 desc
->tables
.volume
=0;
701 for (i
= 0; i
<desc
->tables
.g_w
; ++i
) {
702 desc
->tables
.g
[i
] = (unsigned)(exp(A
* (i
-desc
->tables
.g_r
)*(i
-desc
->tables
.g_r
)) * volume_factor
+ .5);
703 desc
->tables
.volume
+= desc
->tables
.g
[i
];
705 if(desc
->tables
.volume
>256) volume_factor
-= volume_diff
;
707 desc
->tables
.volume
=0;
708 for (i
= 0; i
<desc
->tables
.g_w
; ++i
) {
709 desc
->tables
.g
[i
] = (unsigned)(exp(A
* (i
-desc
->tables
.g_r
)*(i
-desc
->tables
.g_r
)) * volume_factor
+ .5);
710 desc
->tables
.volume
+= desc
->tables
.g
[i
];
714 for(mx
=0;mx
<desc
->tables
.g_w
;mx
++){
716 desc
->tables
.gt2
[mx
+i
*desc
->tables
.g_w
] = i
*desc
->tables
.g
[mx
];
722 for (my
= 0; my
<desc
->tables
.o_w
; ++my
) {
723 for (mx
= 0; mx
<desc
->tables
.o_w
; ++mx
) {
724 // antialiased circle would be perfect here, but this one is good enough
725 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
));
726 desc
->tables
.om
[mx
+my
*desc
->tables
.o_w
] = d
>=1 ? base
: d
<=0 ? 0 : (d
*base
+ .5);
732 for(mx
=0;mx
<desc
->tables
.o_size
;mx
++) *(omtp
++) = (i
*desc
->tables
.om
[mx
] + (base
/2))/base
;
739 /* decode from 'encoding' to unicode */
740 static FT_ULong
decode_char(iconv_t
*cd
, char c
) {
743 char *outbuf
= (char*)&o
;
744 size_t inbytesleft
= 1;
745 size_t outbytesleft
= sizeof(FT_ULong
);
747 iconv(*cd
, &inbuf
, &inbytesleft
, &outbuf
, &outbytesleft
);
749 /* convert unicode BigEndian -> MachineEndian */
752 // if (count==-1) o = 0; // not OK, at least my iconv() returns E2BIG for all
753 if (outbytesleft
!=0) o
= 0;
755 /* we don't want control characters */
756 if (o
>=0x7f && o
<0xa0) o
= 0;
760 static int prepare_charset(char *charmap
, char *encoding
, FT_ULong
*charset
, FT_ULong
*charcodes
) {
766 // check if ucs-4 is available
767 cd
= iconv_open(charmap
, charmap
);
768 if (cd
==(iconv_t
)-1) {
769 mp_msg(MSGT_OSD
, MSGL_ERR
, "iconv doesn't know %s encoding. Use the source!\n", charmap
);
775 cd
= iconv_open(charmap
, encoding
);
776 if (cd
==(iconv_t
)-1) {
777 mp_msg(MSGT_OSD
, MSGL_ERR
, "Unsupported encoding `%s', use iconv --list to list character sets known on your system.\n", encoding
);
781 charset_size
= 256 - first_char
;
782 for (i
= 0; i
<charset_size
; ++i
) {
783 charcodes
[count
] = i
+first_char
;
784 charset
[count
] = decode_char(&cd
, i
+first_char
);
785 if (charset
[count
]!=0) ++count
;
787 charcodes
[count
] = charset
[count
] = 0; ++count
;
788 charset_size
= count
;
791 if (charset_size
==0) {
792 mp_msg(MSGT_OSD
, MSGL_ERR
, "No characters to render!\n");
799 static int prepare_charset_unicode(FT_Face face
, FT_ULong
*charset
, FT_ULong
*charcodes
) {
800 #ifdef HAVE_FREETYPE21
808 if (face
->charmap
==NULL
|| face
->charmap
->encoding
!=ft_encoding_unicode
) {
809 WARNING("Unicode charmap not available for this font. Very bad!");
812 #ifdef HAVE_FREETYPE21
814 charcode
= FT_Get_First_Char( face
, &gindex
);
815 while (gindex
!= 0) {
816 if (charcode
< 65536 && charcode
>= 33) { // sanity check
817 charset
[i
] = charcode
;
821 charcode
= FT_Get_Next_Char( face
, charcode
, &gindex
);
824 // for FT < 2.1 we have to use brute force enumeration
826 for (j
= 33; j
< 65536; j
++) {
827 gindex
= FT_Get_Char_Index(face
, j
);
835 mp_msg(MSGT_OSD
, MSGL_V
, "Unicode font: %d glyphs.\n", i
);
841 static font_desc_t
* init_font_desc(void)
845 desc
= calloc(1, sizeof(*desc
));
846 if(!desc
) return NULL
;
850 /* setup sane defaults */
853 memset(desc
->start
, 0xff, sizeof(desc
->start
));
854 memset(desc
->width
, 0xff, sizeof(desc
->width
));
855 memset(desc
->font
, 0xff, sizeof(desc
->font
));
860 void free_font_desc(font_desc_t
*desc
)
866 // if (!desc->dynamic) return; // some vo_aa crap, better leaking than crashing
871 for(i
= 0; i
< 16; i
++) {
872 if (desc
->pic_a
[i
]) {
873 free(desc
->pic_a
[i
]->bmp
);
874 free(desc
->pic_a
[i
]->pal
);
875 free(desc
->pic_a
[i
]);
877 if (desc
->pic_b
[i
]) {
878 free(desc
->pic_b
[i
]->bmp
);
879 free(desc
->pic_b
[i
]->pal
);
880 free(desc
->pic_b
[i
]);
884 free(desc
->tables
.g
);
885 free(desc
->tables
.gt2
);
886 free(desc
->tables
.om
);
887 free(desc
->tables
.omt
);
888 free(desc
->tables
.tmp
);
890 for(i
= 0; i
< desc
->face_cnt
; i
++) {
891 FT_Done_Face(desc
->faces
[i
]);
897 static int load_sub_face(const char *name
, int face_index
, FT_Face
*face
)
901 if (name
) err
= FT_New_Face(library
, name
, face_index
, face
);
904 char *font_file
= get_path("subfont.ttf");
905 err
= FT_New_Face(library
, font_file
, 0, face
);
908 err
= FT_New_Face(library
, MPLAYER_DATADIR
"/subfont.ttf", 0, face
);
910 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "New_Face failed. Maybe the font path is wrong.\nPlease supply the text font file (~/.mplayer/subfont.ttf).\n");
918 static int load_osd_face(FT_Face
*face
)
920 if ( FT_New_Memory_Face(library
, osd_font_pfb
, sizeof(osd_font_pfb
), 0, face
) ) {
921 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "New_Memory_Face failed..\n");
927 int kerning(font_desc_t
*desc
, int prevc
, int c
)
931 if (!desc
->dynamic
) return 0;
932 if (prevc
< 0 || c
< 0) return 0;
933 if (desc
->font
[prevc
] != desc
->font
[c
]) return 0;
934 if (desc
->font
[prevc
] == -1 || desc
->font
[c
] == -1) return 0;
935 FT_Get_Kerning(desc
->faces
[desc
->font
[c
]],
936 desc
->glyph_index
[prevc
], desc
->glyph_index
[c
],
937 ft_kerning_default
, &kern
);
939 // fprintf(stderr, "kern: %c %c %d\n", prevc, c, f266ToInt(kern.x));
941 return f266ToInt(kern
.x
);
944 font_desc_t
* read_font_desc_ft(const char *fname
, int face_index
, int movie_width
, int movie_height
, float font_scale_factor
)
946 font_desc_t
*desc
= NULL
;
950 FT_ULong
*my_charset
= malloc(MAX_CHARSET_SIZE
* sizeof(FT_ULong
)); /* characters we want to render; Unicode */
951 FT_ULong
*my_charcodes
= malloc(MAX_CHARSET_SIZE
* sizeof(FT_ULong
)); /* character codes in 'encoding' */
953 char *charmap
= "ucs-4";
961 float subtitle_font_ppem
;
964 if (my_charset
== NULL
|| my_charcodes
== NULL
) {
965 mp_msg(MSGT_OSD
, MSGL_ERR
, "subtitle font: malloc failed.\n");
969 switch (subtitle_autoscale
) {
971 movie_size
= movie_height
;
974 movie_size
= movie_width
;
977 movie_size
= sqrt(movie_height
*movie_height
+movie_width
*movie_width
);
984 subtitle_font_ppem
= movie_size
*font_scale_factor
/100.0;
985 osd_font_ppem
= movie_size
*(font_scale_factor
+1)/100.0;
987 if (subtitle_font_ppem
< 5) subtitle_font_ppem
= 5;
988 if (osd_font_ppem
< 5) osd_font_ppem
= 5;
990 if (subtitle_font_ppem
> 128) subtitle_font_ppem
= 128;
991 if (osd_font_ppem
> 128) osd_font_ppem
= 128;
993 if ((subtitle_font_encoding
== NULL
)
994 || (strcasecmp(subtitle_font_encoding
, "unicode") == 0)) {
1000 desc
= init_font_desc();
1001 if(!desc
) goto err_out
;
1005 /* generate the subtitle font */
1006 err
= load_sub_face(fname
, face_index
, &face
);
1008 mp_tmsg(MSGT_OSD
, MSGL_WARN
, "subtitle font: load_sub_face failed.\n");
1015 charset_size
= prepare_charset_unicode(face
, my_charset
, my_charcodes
);
1017 if (subtitle_font_encoding
) {
1018 charset_size
= prepare_charset(charmap
, subtitle_font_encoding
, my_charset
, my_charcodes
);
1020 charset_size
= prepare_charset(charmap
, "iso-8859-1", my_charset
, my_charcodes
);
1024 if (charset_size
< 0) {
1025 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "subtitle font: prepare_charset failed.\n");
1032 // fprintf(stderr, "fg: prepare t = %f\n", GetTimer()-t);
1034 err
= prepare_font(desc
, face
, subtitle_font_ppem
, desc
->face_cnt
-1,
1035 charset_size
, my_charset
, my_charcodes
, unicode
,
1036 subtitle_font_thickness
, subtitle_font_radius
);
1039 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "Cannot prepare subtitle font.\n");
1045 /* generate the OSD font */
1046 err
= load_osd_face(&face
);
1052 err
= prepare_font(desc
, face
, osd_font_ppem
, desc
->face_cnt
-1,
1053 OSD_CHARSET_SIZE
, osd_charset
, osd_charcodes
, 0,
1054 subtitle_font_thickness
, subtitle_font_radius
);
1057 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "Cannot prepare OSD font.\n");
1061 err
= generate_tables(desc
, subtitle_font_thickness
, subtitle_font_radius
);
1064 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "Cannot generate tables.\n");
1070 desc
->width
[' ']=desc
->spacewidth
;
1073 if (desc
->font
[j
] < 0) j
= '?';
1074 if (desc
->font
[j
] < 0) j
= ' ';
1075 render_one_glyph(desc
, j
);
1076 for(i
= 0; i
< 65536; i
++) {
1077 if (desc
->font
[i
] < 0 && i
!= ' ') {
1078 desc
->start
[i
] = desc
->start
[j
];
1079 desc
->width
[i
] = desc
->width
[j
];
1080 desc
->font
[i
] = desc
->font
[j
];
1089 free_font_desc(desc
);
1095 int init_freetype(void)
1099 /* initialize freetype */
1100 err
= FT_Init_FreeType(&library
);
1102 mp_msg(MSGT_OSD
, MSGL_ERR
, "Init_FreeType failed.\n");
1105 mp_msg(MSGT_OSD
, MSGL_V
, "init_freetype\n");
1110 int done_freetype(void)
1114 if (!using_freetype
)
1117 err
= FT_Done_FreeType(library
);
1119 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "FT_Done_FreeType failed.\n");
1126 void load_font_ft(int width
, int height
, font_desc_t
** fontp
, const char *font_name
, float font_scale_factor
)
1128 #ifdef CONFIG_FONTCONFIG
1129 FcPattern
*fc_pattern
;
1130 FcPattern
*fc_pattern2
;
1136 font_desc_t
*vo_font
= *fontp
;
1137 vo_image_width
= width
;
1138 vo_image_height
= height
;
1140 // protection against vo_aa font hacks
1141 if (vo_font
&& !vo_font
->dynamic
) return;
1143 if (vo_font
) free_font_desc(vo_font
);
1145 #ifdef CONFIG_FONTCONFIG
1146 if (font_fontconfig
> 0)
1149 fc_pattern
= FcNameParse(font_name
? font_name
: "sans-serif");
1150 FcConfigSubstitute(0, fc_pattern
, FcMatchPattern
);
1151 FcDefaultSubstitute(fc_pattern
);
1152 fc_pattern2
= fc_pattern
;
1153 fc_pattern
= FcFontMatch(0, fc_pattern
, &result
);
1155 FcPatternDestroy(fc_pattern2
);
1156 FcPatternGetBool(fc_pattern
, FC_SCALABLE
, 0, &scalable
);
1157 if (scalable
!= FcTrue
) {
1158 FcPatternDestroy(fc_pattern
);
1159 fc_pattern
= FcNameParse("sans-serif");
1160 FcConfigSubstitute(0, fc_pattern
, FcMatchPattern
);
1161 FcDefaultSubstitute(fc_pattern
);
1162 fc_pattern2
= fc_pattern
;
1163 fc_pattern
= FcFontMatch(0, fc_pattern
, &result
);
1164 FcPatternDestroy(fc_pattern2
);
1166 // s doesn't need to be freed according to fontconfig docs
1167 FcPatternGetString(fc_pattern
, FC_FILE
, 0, &s
);
1168 FcPatternGetInteger(fc_pattern
, FC_INDEX
, 0, &face_index
);
1169 *fontp
=read_font_desc_ft(s
, face_index
, width
, height
, font_scale_factor
);
1170 FcPatternDestroy(fc_pattern
);
1173 mp_tmsg(MSGT_OSD
, MSGL_ERR
, "Fontconfig failed to select a font. "
1174 "Trying without fontconfig...\n");
1177 *fontp
=read_font_desc_ft(font_name
, 0, width
, height
, font_scale_factor
);