]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
Fix memory leak
[vlc] / modules / misc / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #ifdef HAVE_LINUX_LIMITS_H
32 #   include <linux/limits.h>
33 #endif
34
35 #include <vlc/vlc.h>
36 #include <vlc_vout.h>
37 #include <vlc_osd.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40
41 #include <math.h>
42
43 #ifdef HAVE_ERRNO_H
44 #   include <errno.h>
45 #endif
46
47 #include <ft2build.h>
48 #include FT_FREETYPE_H
49 #include FT_GLYPH_H
50
51 #ifdef __APPLE__
52 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
53 #elif defined( SYS_BEOS )
54 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
55 #elif defined( WIN32 )
56 #define DEFAULT_FONT "" /* Default font found at run-time */
57 #else
58 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
59 #endif
60
61 #if defined(HAVE_FRIBIDI)
62 #include <fribidi/fribidi.h>
63 #endif
64
65 typedef struct line_desc_t line_desc_t;
66
67 /*****************************************************************************
68  * Local prototypes
69  *****************************************************************************/
70 static int  Create ( vlc_object_t * );
71 static void Destroy( vlc_object_t * );
72
73 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
74 static int RenderText( filter_t *, subpicture_region_t *,
75                        subpicture_region_t * );
76 static line_desc_t *NewLine( byte_t * );
77
78 static int SetFontSize( filter_t *, int );
79
80 /*****************************************************************************
81  * Module descriptor
82  *****************************************************************************/
83 #define FONT_TEXT N_("Font")
84 #define FONT_LONGTEXT N_("Filename for the font you want to use")
85 #define FONTSIZE_TEXT N_("Font size in pixels")
86 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
87     "that will be rendered on the video. " \
88     "If set to something different than 0 this option will override the " \
89     "relative font size." )
90 #define OPACITY_TEXT N_("Opacity")
91 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
92     "text that will be rendered on the video. 0 = transparent, " \
93     "255 = totally opaque. " )
94 #define COLOR_TEXT N_("Text default color")
95 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
96     "the video. This must be an hexadecimal (like HTML colors). The first two "\
97     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
98     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
99 #define FONTSIZER_TEXT N_("Relative font size")
100 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
101     "fonts that will be rendered on the video. If absolute font size is set, "\
102     "relative size will be overriden." )
103
104 static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
105 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
106                                    N_("Large"), N_("Larger") };
107 #define YUVP_TEXT N_("Use YUVP renderer")
108 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
109   "This option is only needed if you want to encode into DVB subtitles" )
110 #define EFFECT_TEXT N_("Font Effect")
111 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
112 "text to improve its readability." )
113
114 #define EFFECT_BACKGROUND  1 
115 #define EFFECT_OUTLINE     2
116 #define EFFECT_OUTLINE_FAT 3
117
118 static int   pi_effects[] = { 1, 2, 3 };
119 static char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
120                                      N_("Fat Outline") };
121 static int pi_color_values[] = {
122   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
123   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080, 
124   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF }; 
125
126 static char *ppsz_color_descriptions[] = {
127   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
128   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
129   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
130
131 vlc_module_begin();
132     set_shortname( _("Text renderer"));
133     set_description( _("Freetype2 font renderer") );
134     set_category( CAT_VIDEO );
135     set_subcategory( SUBCAT_VIDEO_SUBPIC );
136
137     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
138               VLC_FALSE );
139
140     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
141                  FONTSIZE_LONGTEXT, VLC_TRUE );
142
143     /* opacity valid on 0..255, with default 255 = fully opaque */
144     add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
145         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
146
147     /* hook to the color values list, with default 0x00ffffff = white */
148     add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
149                  COLOR_LONGTEXT, VLC_FALSE );
150         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
151
152     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
153                  FONTSIZER_LONGTEXT, VLC_FALSE );
154         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
155     add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
156                  EFFECT_LONGTEXT, VLC_FALSE );
157         change_integer_list( pi_effects, ppsz_effects_text, 0 );
158
159     add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
160               YUVP_LONGTEXT, VLC_TRUE );
161     set_capability( "text renderer", 100 );
162     add_shortcut( "text" );
163     set_callbacks( Create, Destroy );
164 vlc_module_end();
165
166 struct line_desc_t
167 {
168     /** NULL-terminated list of glyphs making the string */
169     FT_BitmapGlyph *pp_glyphs;
170     /** list of relative positions for the glyphs */
171     FT_Vector      *p_glyph_pos;
172
173     int             i_height;
174     int             i_width;
175     int             i_red, i_green, i_blue;
176     int             i_alpha;
177
178     line_desc_t    *p_next;
179 };
180
181 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
182 static void FreeLines( line_desc_t * );
183 static void FreeLine( line_desc_t * );
184
185 /*****************************************************************************
186  * filter_sys_t: freetype local data
187  *****************************************************************************
188  * This structure is part of the video output thread descriptor.
189  * It describes the freetype specific properties of an output thread.
190  *****************************************************************************/
191 struct filter_sys_t
192 {
193     FT_Library     p_library;   /* handle to library     */
194     FT_Face        p_face;      /* handle to face object */
195     vlc_bool_t     i_use_kerning;
196     uint8_t        i_font_opacity;
197     int            i_font_color;
198     int            i_font_size;
199     int            i_effect;
200
201     int            i_default_font_size;
202     int            i_display_height;
203 };
204
205 /*****************************************************************************
206  * Create: allocates osd-text video thread output method
207  *****************************************************************************
208  * This function allocates and initializes a Clone vout method.
209  *****************************************************************************/
210 static int Create( vlc_object_t *p_this )
211 {
212     filter_t *p_filter = (filter_t *)p_this;
213     filter_sys_t *p_sys;
214     char *psz_fontfile = NULL;
215     int i_error;
216     vlc_value_t val;
217
218     /* Allocate structure */
219     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
220     if( !p_sys )
221     {
222         msg_Err( p_filter, "out of memory" );
223         return VLC_ENOMEM;
224     }
225     p_sys->p_face = 0;
226     p_sys->p_library = 0;
227     p_sys->i_font_size = 0;
228     p_sys->i_display_height = 0;
229
230     var_Create( p_filter, "freetype-font",
231                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
232     var_Create( p_filter, "freetype-fontsize",
233                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
234     var_Create( p_filter, "freetype-rel-fontsize",
235                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
236     var_Create( p_filter, "freetype-opacity",
237                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
238     var_Create( p_filter, "freetype-effect",
239                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
240     var_Get( p_filter, "freetype-opacity", &val );
241     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
242     var_Create( p_filter, "freetype-color",
243                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
244     var_Get( p_filter, "freetype-color", &val );
245     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
246     p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
247     
248     /* Look what method was requested */
249     var_Get( p_filter, "freetype-font", &val );
250     psz_fontfile = val.psz_string;
251     if( !psz_fontfile || !*psz_fontfile )
252     {
253         if( psz_fontfile ) free( psz_fontfile );
254         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
255 #ifdef WIN32
256         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
257         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
258 #elif __APPLE__
259         strcpy( psz_fontfile, DEFAULT_FONT );
260 #else
261         msg_Err( p_filter, "user didn't specify a font" );
262         goto error;
263 #endif
264     }
265
266     i_error = FT_Init_FreeType( &p_sys->p_library );
267     if( i_error )
268     {
269         msg_Err( p_filter, "couldn't initialize freetype" );
270         goto error;
271     }
272
273     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
274                            0, &p_sys->p_face );
275     if( i_error == FT_Err_Unknown_File_Format )
276     {
277         msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
278         goto error;
279     }
280     else if( i_error )
281     {
282         msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
283         goto error;
284     }
285
286     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
287     if( i_error )
288     {
289         msg_Err( p_filter, "font has no unicode translation table" );
290         goto error;
291     }
292
293     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
294
295     var_Get( p_filter, "freetype-fontsize", &val );
296     p_sys->i_default_font_size = val.i_int;
297     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
298
299     if( psz_fontfile ) free( psz_fontfile );
300     p_filter->pf_render_text = RenderText;
301     return VLC_SUCCESS;
302
303  error:
304     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
305     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
306     if( psz_fontfile ) free( psz_fontfile );
307     free( p_sys );
308     return VLC_EGENERIC;
309 }
310
311 /*****************************************************************************
312  * Destroy: destroy Clone video thread output method
313  *****************************************************************************
314  * Clean up all data and library connections
315  *****************************************************************************/
316 static void Destroy( vlc_object_t *p_this )
317 {
318     filter_t *p_filter = (filter_t *)p_this;
319     filter_sys_t *p_sys = p_filter->p_sys;
320
321     FT_Done_Face( p_sys->p_face );
322     FT_Done_FreeType( p_sys->p_library );
323     free( p_sys );
324 }
325
326 /*****************************************************************************
327  * Render: place string in picture
328  *****************************************************************************
329  * This function merges the previously rendered freetype glyphs into a picture
330  *****************************************************************************/
331 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
332                    line_desc_t *p_line, int i_width, int i_height )
333 {
334     static uint8_t pi_gamma[16] =
335         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
336           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
337
338     uint8_t *p_dst;
339     video_format_t fmt;
340     int i, x, y, i_pitch;
341     uint8_t i_y; /* YUV values, derived from incoming RGB */
342     int8_t i_u, i_v;
343     subpicture_region_t *p_region_tmp;
344
345     /* Create a new subpicture region */
346     memset( &fmt, 0, sizeof(video_format_t) );
347     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
348     fmt.i_aspect = 0;
349     fmt.i_width = fmt.i_visible_width = i_width + 4;
350     fmt.i_height = fmt.i_visible_height = i_height + 4;
351     fmt.i_x_offset = fmt.i_y_offset = 0;
352     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
353     if( !p_region_tmp )
354     {
355         msg_Err( p_filter, "cannot allocate SPU region" );
356         return VLC_EGENERIC;
357     }
358
359     p_region->fmt = p_region_tmp->fmt;
360     p_region->picture = p_region_tmp->picture;
361     free( p_region_tmp );
362
363     /* Calculate text color components */
364     i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
365                       25 * p_line->i_blue + 128) >> 8) +  16;
366     i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
367                      112 * p_line->i_blue + 128) >> 8) + 128;
368     i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
369                       18 * p_line->i_blue + 128) >> 8) + 128;
370
371     /* Build palette */
372     fmt.p_palette->i_entries = 16;
373     for( i = 0; i < 8; i++ )
374     {
375         fmt.p_palette->palette[i][0] = 0;
376         fmt.p_palette->palette[i][1] = 0x80;
377         fmt.p_palette->palette[i][2] = 0x80;
378         fmt.p_palette->palette[i][3] = pi_gamma[i];
379         fmt.p_palette->palette[i][3] =
380             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
381     }
382     for( i = 8; i < fmt.p_palette->i_entries; i++ )
383     {
384         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
385         fmt.p_palette->palette[i][1] = i_u;
386         fmt.p_palette->palette[i][2] = i_v;
387         fmt.p_palette->palette[i][3] = pi_gamma[i];
388         fmt.p_palette->palette[i][3] =
389             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
390     }
391
392     p_dst = p_region->picture.Y_PIXELS;
393     i_pitch = p_region->picture.Y_PITCH;
394
395     /* Initialize the region pixels */
396     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
397
398     for( ; p_line != NULL; p_line = p_line->p_next )
399     {
400         int i_glyph_tmax = 0;
401         int i_bitmap_offset, i_offset, i_align_offset = 0;
402         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
403         {
404             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
405             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
406         }
407
408         if( p_line->i_width < i_width )
409         {
410             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
411                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
412             {
413                 i_align_offset = i_width - p_line->i_width;
414             }
415             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
416                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
417             {
418                 i_align_offset = ( i_width - p_line->i_width ) / 2;
419             }
420         }
421
422         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
423         {
424             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
425
426             i_offset = ( p_line->p_glyph_pos[ i ].y +
427                 i_glyph_tmax - p_glyph->top + 2 ) *
428                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
429                 i_align_offset;
430
431             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
432             {
433                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
434                 {
435                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
436                         p_dst[i_offset+x] =
437                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
438                 }
439                 i_offset += i_pitch;
440             }
441         }
442     }
443
444     /* Outlining (find something better than nearest neighbour filtering ?) */
445     if( 1 )
446     {
447         uint8_t *p_dst = p_region->picture.Y_PIXELS;
448         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
449         uint8_t left, current;
450
451         for( y = 1; y < (int)fmt.i_height - 1; y++ )
452         {
453             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
454             p_dst += p_region->picture.Y_PITCH;
455             left = 0;
456
457             for( x = 1; x < (int)fmt.i_width - 1; x++ )
458             {
459                 current = p_dst[x];
460                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
461                              p_dst[x -1 + p_region->picture.Y_PITCH ] + p_dst[x + p_region->picture.Y_PITCH] + p_dst[x + 1 + p_region->picture.Y_PITCH]) / 16;
462                 left = current;
463             }
464         }
465         memset( p_top, 0, fmt.i_width );
466     }
467
468     return VLC_SUCCESS;
469 }
470
471 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
472 {
473     uint8_t *p_dst = p_region->picture.A_PIXELS;
474     int i_pitch = p_region->picture.A_PITCH;
475     int x,y;
476
477     for( ; p_line != NULL; p_line = p_line->p_next )
478     {
479         int i_glyph_tmax=0, i = 0;
480         int i_bitmap_offset, i_offset, i_align_offset = 0;
481         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
482         {
483             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
484             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
485         }
486
487         if( p_line->i_width < i_width )
488         {
489             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
490                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
491             {
492                 i_align_offset = i_width - p_line->i_width;
493             }
494             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
495                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
496             {
497                 i_align_offset = ( i_width - p_line->i_width ) / 2;
498             }
499         }
500
501         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
502         {
503             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
504
505             i_offset = ( p_line->p_glyph_pos[ i ].y +
506                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
507                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
508                 i_align_offset +xoffset;
509
510             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
511             {
512                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
513                 {
514                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
515                         if( p_dst[i_offset+x] <
516                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
517                             p_dst[i_offset+x] =
518                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
519                 }
520                 i_offset += i_pitch;
521             }
522         }
523     }
524     
525 }
526
527 /*****************************************************************************
528  * Render: place string in picture
529  *****************************************************************************
530  * This function merges the previously rendered freetype glyphs into a picture
531  *****************************************************************************/
532 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
533                    line_desc_t *p_line, int i_width, int i_height )
534 {
535     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
536     video_format_t fmt;
537     int i, x, y, i_pitch, i_alpha;
538     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
539     subpicture_region_t *p_region_tmp;
540
541     if( i_width == 0 || i_height == 0 )
542         return VLC_SUCCESS;
543
544     /* Create a new subpicture region */
545     memset( &fmt, 0, sizeof(video_format_t) );
546     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
547     fmt.i_aspect = 0;
548     fmt.i_width = fmt.i_visible_width = i_width + 6;
549     fmt.i_height = fmt.i_visible_height = i_height + 6;
550     fmt.i_x_offset = fmt.i_y_offset = 0;
551     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
552     if( !p_region_tmp )
553     {
554         msg_Err( p_filter, "cannot allocate SPU region" );
555         return VLC_EGENERIC;
556     }
557
558     p_region->fmt = p_region_tmp->fmt;
559     p_region->picture = p_region_tmp->picture;
560     free( p_region_tmp );
561
562     /* Calculate text color components */
563     i_y = (uint8_t)__MIN(abs( 2104 * p_line->i_red  + 4130 * p_line->i_green +
564                       802 * p_line->i_blue + 4096 + 131072 ) >> 13, 235);
565     i_u = (uint8_t)__MIN(abs( -1214 * p_line->i_red  + -2384 * p_line->i_green +
566                      3598 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
567     i_v = (uint8_t)__MIN(abs( 3598 * p_line->i_red + -3013 * p_line->i_green +
568                       -585 * p_line->i_blue + 4096 + 1048576) >> 13, 240);
569     i_alpha = p_line->i_alpha;
570
571     p_dst_y = p_region->picture.Y_PIXELS;
572     p_dst_u = p_region->picture.U_PIXELS;
573     p_dst_v = p_region->picture.V_PIXELS;
574     p_dst_a = p_region->picture.A_PIXELS;
575     i_pitch = p_region->picture.A_PITCH;
576
577     /* Initialize the region pixels */
578     if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
579     {
580         memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
581         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
582         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
583         memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
584     }
585     else
586     {
587         memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
588         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
589         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
590         memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
591     }
592     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
593         p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
594     {
595         DrawBlack( p_line, i_width, p_region,  0,  0);
596         DrawBlack( p_line, i_width, p_region, -1,  0);
597         DrawBlack( p_line, i_width, p_region,  0, -1);
598         DrawBlack( p_line, i_width, p_region,  1,  0);
599         DrawBlack( p_line, i_width, p_region,  0,  1);
600     }
601
602     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
603     {
604         DrawBlack( p_line, i_width, p_region, -1, -1);
605         DrawBlack( p_line, i_width, p_region, -1,  1);
606         DrawBlack( p_line, i_width, p_region,  1, -1);
607         DrawBlack( p_line, i_width, p_region,  1,  1);
608
609         DrawBlack( p_line, i_width, p_region, -2,  0);
610         DrawBlack( p_line, i_width, p_region,  0, -2);
611         DrawBlack( p_line, i_width, p_region,  2,  0);
612         DrawBlack( p_line, i_width, p_region,  0,  2);
613
614         DrawBlack( p_line, i_width, p_region, -2, -2);
615         DrawBlack( p_line, i_width, p_region, -2,  2);
616         DrawBlack( p_line, i_width, p_region,  2, -2);
617         DrawBlack( p_line, i_width, p_region,  2,  2);
618
619         DrawBlack( p_line, i_width, p_region, -3,  0);
620         DrawBlack( p_line, i_width, p_region,  0, -3);
621         DrawBlack( p_line, i_width, p_region,  3,  0);
622         DrawBlack( p_line, i_width, p_region,  0,  3);
623     }
624
625     for( ; p_line != NULL; p_line = p_line->p_next )
626     {
627         int i_glyph_tmax = 0;
628         int i_bitmap_offset, i_offset, i_align_offset = 0;
629         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
630         {
631             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
632             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
633         }
634
635         if( p_line->i_width < i_width )
636         {
637             if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
638                 ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
639             {
640                 i_align_offset = i_width - p_line->i_width;
641             }
642             else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
643                 ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
644             {
645                 i_align_offset = ( i_width - p_line->i_width ) / 2;
646             }
647         }
648
649         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
650         {
651             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
652
653             i_offset = ( p_line->p_glyph_pos[ i ].y +
654                 i_glyph_tmax - p_glyph->top + 3 ) *
655                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
656                 i_align_offset;
657
658             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
659             {
660                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
661                 {
662                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
663                     {
664                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
665                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
666
667                         p_dst_u[i_offset+x] = i_u;
668                         p_dst_v[i_offset+x] = i_v;
669
670                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
671                             p_dst_a[i_offset+x] = 0xff;
672                     }
673                 }
674                 i_offset += i_pitch;
675             }
676         }
677     }
678
679     /* Apply the alpha setting */
680     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
681         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
682
683     return VLC_SUCCESS;
684 }
685
686 /**
687  * This function renders a text subpicture region into another one.
688  * It also calculates the size needed for this string, and renders the
689  * needed glyphs into memory. It is used as pf_add_string callback in
690  * the vout method by this module
691  */
692 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
693                        subpicture_region_t *p_region_in )
694 {
695     filter_sys_t *p_sys = p_filter->p_sys;
696     line_desc_t  *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
697     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
698     uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
699     int i_string_length;
700     char *psz_string;
701     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
702     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
703
704     FT_BBox line;
705     FT_BBox glyph_size;
706     FT_Vector result;
707     FT_Glyph tmp_glyph;
708
709     /* Sanity check */
710     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
711     psz_string = p_region_in->psz_text;
712     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
713
714     if( p_region_in->p_style )
715     {
716         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
717         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
718         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
719     }
720     else
721     {
722         i_font_color = p_sys->i_font_color;
723         i_font_alpha = 255 - p_sys->i_font_opacity;
724         i_font_size  = p_sys->i_default_font_size;
725     }
726
727     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
728     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
729     SetFontSize( p_filter, i_font_size );
730
731     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
732     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
733     i_blue  =   i_font_color & 0x000000FF;
734
735     result.x =  result.y = 0;
736     line.xMin = line.xMax = line.yMin = line.yMax = 0;
737
738     psz_unicode = psz_unicode_orig =
739         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
740     if( psz_unicode == NULL )
741     {
742         msg_Err( p_filter, "out of memory" );
743         goto error;
744     }
745 #if defined(WORDS_BIGENDIAN)
746     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
747 #else
748     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
749 #endif
750     if( iconv_handle == (vlc_iconv_t)-1 )
751     {
752         msg_Warn( p_filter, "unable to do conversion" );
753         goto error;
754     }
755
756     {
757         char *p_out_buffer;
758         const char *p_in_buffer = psz_string;
759         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
760         i_in_bytes = strlen( psz_string );
761         i_out_bytes = i_in_bytes * sizeof( uint32_t );
762         i_out_bytes_left = i_out_bytes;
763         p_out_buffer = (char *)psz_unicode;
764         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
765                            &p_out_buffer, &i_out_bytes_left );
766
767         vlc_iconv_close( iconv_handle );
768
769         if( i_in_bytes )
770         {
771             msg_Warn( p_filter, "failed to convert string to unicode (%s), "
772                       "bytes left %d", strerror(errno), (int)i_in_bytes );
773             goto error;
774         }
775         *(uint32_t*)p_out_buffer = 0;
776         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
777     }
778
779 #if defined(HAVE_FRIBIDI)
780     {
781         uint32_t *p_fribidi_string;
782         int start_pos, pos = 0;
783
784         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
785
786         /* Do bidi conversion line-by-line */
787         while(pos < i_string_length)
788         {
789             while(pos < i_string_length) {
790                 i_char = psz_unicode[pos];
791                 if (i_char != '\r' && i_char != '\n')
792                     break;
793                 p_fribidi_string[pos] = i_char;
794                 ++pos;
795             }
796             start_pos = pos;
797             while(pos < i_string_length) {
798                 i_char = psz_unicode[pos];
799                 if (i_char == '\r' || i_char == '\n')
800                     break;
801                 ++pos;
802             }
803             if (pos > start_pos)
804             {
805                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
806                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
807                                 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
808             }
809         }
810
811         free( psz_unicode_orig );
812         psz_unicode = psz_unicode_orig = p_fribidi_string;
813         p_fribidi_string[ i_string_length ] = 0;
814     }
815 #endif
816
817     /* Calculate relative glyph positions and a bounding box for the
818      * entire string */
819     if( !(p_line = NewLine( (byte_t *)psz_string )) )
820     {
821         msg_Err( p_filter, "out of memory" );
822         goto error;
823     }
824     p_lines = p_line;
825     i_pen_x = i_pen_y = 0;
826     i_previous = i = 0;
827     psz_line_start = psz_unicode;
828
829 #define face p_sys->p_face
830 #define glyph face->glyph
831
832     while( *psz_unicode )
833     {
834         i_char = *psz_unicode++;
835         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
836         {
837             continue;
838         }
839
840         if( i_char == '\n' )
841         {
842             psz_line_start = psz_unicode;
843             if( !(p_next = NewLine( (byte_t *)psz_string )) )
844             {
845                 msg_Err( p_filter, "out of memory" );
846                 goto error;
847             }
848             p_line->p_next = p_next;
849             p_line->i_width = line.xMax;
850             p_line->i_height = face->size->metrics.height >> 6;
851             p_line->pp_glyphs[ i ] = NULL;
852             p_line->i_alpha = i_font_alpha;
853             p_line->i_red = i_red;
854             p_line->i_green = i_green;
855             p_line->i_blue = i_blue;
856             p_prev = p_line;
857             p_line = p_next;
858             result.x = __MAX( result.x, line.xMax );
859             result.y += face->size->metrics.height >> 6;
860             i_pen_x = 0;
861             i_previous = i = 0;
862             line.xMin = line.xMax = line.yMin = line.yMax = 0;
863             i_pen_y += face->size->metrics.height >> 6;
864 #if 0
865             msg_Dbg( p_filter, "Creating new line, i is %d", i );
866 #endif
867             continue;
868         }
869
870         i_glyph_index = FT_Get_Char_Index( face, i_char );
871         if( p_sys->i_use_kerning && i_glyph_index
872             && i_previous )
873         {
874             FT_Vector delta;
875             FT_Get_Kerning( face, i_previous, i_glyph_index,
876                             ft_kerning_default, &delta );
877             i_pen_x += delta.x >> 6;
878
879         }
880         p_line->p_glyph_pos[ i ].x = i_pen_x;
881         p_line->p_glyph_pos[ i ].y = i_pen_y;
882         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
883         if( i_error )
884         {
885             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
886                                " %d", i_error );
887             goto error;
888         }
889         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
890         if( i_error )
891         {
892             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
893                                "%d", i_error );
894             goto error;
895         }
896         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
897         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
898         if( i_error )
899         {
900             FT_Done_Glyph( tmp_glyph );
901             continue;
902         }
903         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
904
905         /* Do rest */
906         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
907             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
908         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
909         {
910             p_line->pp_glyphs[ i ] = NULL;
911             FreeLine( p_line );
912             p_line = NewLine( (byte_t *)psz_string );
913             if( p_prev ) p_prev->p_next = p_line;
914             else p_lines = p_line;
915
916             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
917             {
918                 psz_unicode--;
919             }
920             if( psz_unicode == psz_line_start )
921             {
922                 msg_Warn( p_filter, "unbreakable string" );
923                 goto error;
924             }
925             else
926             {
927                 *psz_unicode = '\n';
928             }
929             psz_unicode = psz_line_start;
930             i_pen_x = 0;
931             i_previous = i = 0;
932             line.xMin = line.xMax = line.yMin = line.yMax = 0;
933             continue;
934         }
935         line.yMax = __MAX( line.yMax, glyph_size.yMax );
936         line.yMin = __MIN( line.yMin, glyph_size.yMin );
937
938         i_previous = i_glyph_index;
939         i_pen_x += glyph->advance.x >> 6;
940         i++;
941     }
942
943     p_line->i_width = line.xMax;
944     p_line->i_height = face->size->metrics.height >> 6;
945     p_line->pp_glyphs[ i ] = NULL;
946     p_line->i_alpha = i_font_alpha;
947     p_line->i_red = i_red;
948     p_line->i_green = i_green;
949     p_line->i_blue = i_blue;
950     result.x = __MAX( result.x, line.xMax );
951     result.y += line.yMax - line.yMin;
952
953 #undef face
954 #undef glyph
955
956     p_region_out->i_x = p_region_in->i_x;
957     p_region_out->i_y = p_region_in->i_y;
958
959     if( config_GetInt( p_filter, "freetype-yuvp" ) )
960         Render( p_filter, p_region_out, p_lines, result.x, result.y );
961     else
962         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
963
964     if( psz_unicode_orig ) free( psz_unicode_orig );
965     FreeLines( p_lines );
966     return VLC_SUCCESS;
967
968  error:
969     if( psz_unicode_orig ) free( psz_unicode_orig );
970     FreeLines( p_lines );
971     return VLC_EGENERIC;
972 }
973
974 static void FreeLine( line_desc_t *p_line )
975 {
976     unsigned int i;
977     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
978     {
979         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
980     }
981     free( p_line->pp_glyphs );
982     free( p_line->p_glyph_pos );
983     free( p_line );
984 }
985
986 static void FreeLines( line_desc_t *p_lines )
987 {
988     line_desc_t *p_line, *p_next;
989
990     if( !p_lines ) return;
991
992     for( p_line = p_lines; p_line != NULL; p_line = p_next )
993     {
994         p_next = p_line->p_next;
995         FreeLine( p_line );
996     }
997 }
998
999 static line_desc_t *NewLine( byte_t *psz_string )
1000 {
1001     int i_count;
1002     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1003
1004     if( !p_line ) return NULL;
1005     p_line->i_height = 0;
1006     p_line->i_width = 0;
1007     p_line->p_next = NULL;
1008
1009     /* We don't use CountUtf8Characters() here because we are not acutally
1010      * sure the string is utf8. Better be safe than sorry. */
1011     i_count = strlen( (char *)psz_string );
1012
1013     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1014     if( p_line->pp_glyphs == NULL )
1015     {
1016         free( p_line );
1017         return NULL;
1018     }
1019     p_line->pp_glyphs[0] = NULL;
1020
1021     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1022     if( p_line->p_glyph_pos == NULL )
1023     {
1024         free( p_line->pp_glyphs );
1025         free( p_line );
1026         return NULL;
1027     }
1028
1029     return p_line;
1030 }
1031
1032 static int SetFontSize( filter_t *p_filter, int i_size )
1033 {
1034     filter_sys_t *p_sys = p_filter->p_sys;
1035
1036     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1037
1038     if( !i_size )
1039     {
1040         vlc_value_t val;
1041
1042         if( !p_sys->i_default_font_size &&
1043             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1044             return VLC_SUCCESS;
1045
1046         if( p_sys->i_default_font_size )
1047         {
1048             i_size = p_sys->i_default_font_size;
1049         }
1050         else
1051         {
1052             var_Get( p_filter, "freetype-rel-fontsize", &val );
1053             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1054             p_filter->p_sys->i_display_height =
1055                 p_filter->fmt_out.video.i_height;
1056         }
1057         if( i_size <= 0 )
1058         {
1059             msg_Warn( p_filter, "invalid fontsize, using 12" );
1060             i_size = 12;
1061         }
1062
1063         msg_Dbg( p_filter, "using fontsize: %i", i_size );
1064     }
1065
1066     p_sys->i_font_size = i_size;
1067
1068     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1069     {
1070         msg_Err( p_filter, "couldn't set font size to %d", i_size );
1071         return VLC_EGENERIC;
1072     }
1073
1074     return VLC_SUCCESS;
1075 }