]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
ede233e6dde1bc7017df138bca1ab9a8b119ed3f
[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 rendering." )
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_region->p_style && p_line->i_width < i_width )
409         {
410             if( p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT )
411             {
412                 i_align_offset = i_width - p_line->i_width;
413             }
414             else if( p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT )
415             {
416                 i_align_offset = ( i_width - p_line->i_width ) / 2;
417             }
418         }
419
420         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
421         {
422             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
423
424             i_offset = ( p_line->p_glyph_pos[ i ].y +
425                 i_glyph_tmax - p_glyph->top + 2 ) *
426                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
427                 i_align_offset;
428
429             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
430             {
431                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
432                 {
433                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
434                         p_dst[i_offset+x] =
435                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
436                 }
437                 i_offset += i_pitch;
438             }
439         }
440     }
441
442     /* Outlining (find something better than nearest neighbour filtering ?) */
443     if( 1 )
444     {
445         uint8_t *p_dst = p_region->picture.Y_PIXELS;
446         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
447         uint8_t left, current;
448
449         for( y = 1; y < (int)fmt.i_height - 1; y++ )
450         {
451             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
452             p_dst += p_region->picture.Y_PITCH;
453             left = 0;
454
455             for( x = 1; x < (int)fmt.i_width - 1; x++ )
456             {
457                 current = p_dst[x];
458                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
459                              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;
460                 left = current;
461             }
462         }
463         memset( p_top, 0, fmt.i_width );
464     }
465
466     return VLC_SUCCESS;
467 }
468
469 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
470 {
471     uint8_t *p_dst = p_region->picture.A_PIXELS;
472     int i_pitch = p_region->picture.A_PITCH;
473     int x,y;
474
475     for( ; p_line != NULL; p_line = p_line->p_next )
476     {
477         int i_glyph_tmax=0, i = 0;
478         int i_bitmap_offset, i_offset, i_align_offset = 0;
479         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
480         {
481             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
482             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
483         }
484
485         if( p_region->p_style && p_line->i_width < i_width )
486         {
487             if( p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT )
488             {
489                 i_align_offset = i_width - p_line->i_width;
490             }
491             else if( p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT )
492             {
493                 i_align_offset = ( i_width - p_line->i_width ) / 2;
494             }
495         }
496
497         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
498         {
499             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
500
501             i_offset = ( p_line->p_glyph_pos[ i ].y +
502                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
503                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
504                 i_align_offset +xoffset;
505
506             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
507             {
508                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
509                 {
510                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
511                         if( p_dst[i_offset+x] <
512                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
513                             p_dst[i_offset+x] =
514                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
515                 }
516                 i_offset += i_pitch;
517             }
518         }
519     }
520     
521 }
522
523 /*****************************************************************************
524  * Render: place string in picture
525  *****************************************************************************
526  * This function merges the previously rendered freetype glyphs into a picture
527  *****************************************************************************/
528 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
529                    line_desc_t *p_line, int i_width, int i_height )
530 {
531     static uint8_t pi_gamma[16] =
532         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
533           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
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_region->p_style && p_line->i_width < i_width )
636         {
637             if( p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT )
638             {
639                 i_align_offset = i_width - p_line->i_width;
640             }
641             else if( p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT )
642             {
643                 i_align_offset = ( i_width - p_line->i_width ) / 2;
644             }
645         }
646
647         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
648         {
649             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
650
651             i_offset = ( p_line->p_glyph_pos[ i ].y +
652                 i_glyph_tmax - p_glyph->top + 3 ) *
653                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
654                 i_align_offset;
655
656             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
657             {
658                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
659                 {
660                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
661                     {
662                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
663                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
664
665                         p_dst_u[i_offset+x] = i_u;
666                         p_dst_v[i_offset+x] = i_v;
667                         
668                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
669                             p_dst_a[i_offset+x] = 0xff;
670                     }
671                 }
672                 i_offset += i_pitch;
673             }
674         }
675     }
676     
677     /* Apply the alpha setting */
678     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
679         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
680
681     return VLC_SUCCESS;
682 }
683
684 /**
685  * This function renders a text subpicture region into another one.
686  * It also calculates the size needed for this string, and renders the
687  * needed glyphs into memory. It is used as pf_add_string callback in
688  * the vout method by this module
689  */
690 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
691                        subpicture_region_t *p_region_in )
692 {
693     filter_sys_t *p_sys = p_filter->p_sys;
694     line_desc_t  *p_lines = 0, *p_line = 0, *p_next = 0, *p_prev = 0;
695     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
696     uint32_t *psz_unicode, *psz_unicode_orig = 0, i_char, *psz_line_start;
697     int i_string_length;
698     char *psz_string;
699     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
700     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
701
702     FT_BBox line;
703     FT_BBox glyph_size;
704     FT_Vector result;
705     FT_Glyph tmp_glyph;
706
707     /* Sanity check */
708     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
709     psz_string = p_region_in->psz_text;
710     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
711
712     if( p_region_in->p_style )
713     {
714         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
715         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
716         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
717     }
718     else
719     {
720         i_font_color = p_sys->i_font_color;
721         i_font_alpha = 255 - p_sys->i_font_opacity;
722         i_font_size  = p_sys->i_default_font_size;
723     }
724
725     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
726     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
727     SetFontSize( p_filter, i_font_size );
728
729     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
730     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
731     i_blue  =   i_font_color & 0x000000FF;
732
733     result.x =  result.y = 0;
734     line.xMin = line.xMax = line.yMin = line.yMax = 0;
735
736     psz_unicode = psz_unicode_orig =
737         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
738     if( psz_unicode == NULL )
739     {
740         msg_Err( p_filter, "out of memory" );
741         goto error;
742     }
743 #if defined(WORDS_BIGENDIAN)
744     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
745 #else
746     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
747 #endif
748     if( iconv_handle == (vlc_iconv_t)-1 )
749     {
750         msg_Warn( p_filter, "unable to do conversion" );
751         goto error;
752     }
753
754     {
755         char *p_in_buffer, *p_out_buffer;
756         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
757         i_in_bytes = strlen( psz_string );
758         i_out_bytes = i_in_bytes * sizeof( uint32_t );
759         i_out_bytes_left = i_out_bytes;
760         p_in_buffer = psz_string;
761         p_out_buffer = (char *)psz_unicode;
762         i_ret = vlc_iconv( iconv_handle, &p_in_buffer, &i_in_bytes,
763                            &p_out_buffer, &i_out_bytes_left );
764
765         vlc_iconv_close( iconv_handle );
766
767         if( i_in_bytes )
768         {
769             msg_Warn( p_filter, "failed to convert string to unicode (%s), "
770                       "bytes left %d", strerror(errno), (int)i_in_bytes );
771             goto error;
772         }
773         *(uint32_t*)p_out_buffer = 0;
774         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
775     }
776
777 #if defined(HAVE_FRIBIDI)
778     {
779         uint32_t *p_fribidi_string;
780         int start_pos, pos = 0;
781         
782         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
783
784         /* Do bidi conversion line-by-line */
785         while(pos < i_string_length)
786         {
787             while(pos < i_string_length) {
788                 i_char = psz_unicode[pos];
789                 if (i_char != '\r' && i_char != '\n')
790                     break;
791                 p_fribidi_string[pos] = i_char;
792                 ++pos;
793             }
794             start_pos = pos;
795             while(pos < i_string_length) {
796                 i_char = psz_unicode[pos];
797                 if (i_char == '\r' || i_char == '\n')
798                     break;
799                 ++pos;
800             }
801             if (pos > start_pos)
802             {
803                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
804                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
805                                 &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
806             }
807         }
808
809         free( psz_unicode_orig );
810         psz_unicode = psz_unicode_orig = p_fribidi_string;
811         p_fribidi_string[ i_string_length ] = 0;
812     }
813 #endif
814
815     /* Calculate relative glyph positions and a bounding box for the
816      * entire string */
817     if( !(p_line = NewLine( (byte_t *)psz_string )) )
818     {
819         msg_Err( p_filter, "out of memory" );
820         goto error;
821     }
822     p_lines = p_line;
823     i_pen_x = i_pen_y = 0;
824     i_previous = i = 0;
825     psz_line_start = psz_unicode;
826
827 #define face p_sys->p_face
828 #define glyph face->glyph
829
830     while( *psz_unicode )
831     {
832         i_char = *psz_unicode++;
833         if( i_char == '\r' ) /* ignore CR chars wherever they may be */
834         {
835             continue;
836         }
837
838         if( i_char == '\n' )
839         {
840             psz_line_start = psz_unicode;
841             if( !(p_next = NewLine( (byte_t *)psz_string )) )
842             {
843                 msg_Err( p_filter, "out of memory" );
844                 goto error;
845             }
846             p_line->p_next = p_next;
847             p_line->i_width = line.xMax;
848             p_line->i_height = face->size->metrics.height >> 6;
849             p_line->pp_glyphs[ i ] = NULL;
850             p_line->i_alpha = i_font_alpha;
851             p_line->i_red = i_red;
852             p_line->i_green = i_green;
853             p_line->i_blue = i_blue;
854             p_prev = p_line;
855             p_line = p_next;
856             result.x = __MAX( result.x, line.xMax );
857             result.y += face->size->metrics.height >> 6;
858             i_pen_x = 0;
859             i_previous = i = 0;
860             line.xMin = line.xMax = line.yMin = line.yMax = 0;
861             i_pen_y += face->size->metrics.height >> 6;
862 #if 0
863             msg_Dbg( p_filter, "Creating new line, i is %d", i );
864 #endif
865             continue;
866         }
867
868         i_glyph_index = FT_Get_Char_Index( face, i_char );
869         if( p_sys->i_use_kerning && i_glyph_index
870             && i_previous )
871         {
872             FT_Vector delta;
873             FT_Get_Kerning( face, i_previous, i_glyph_index,
874                             ft_kerning_default, &delta );
875             i_pen_x += delta.x >> 6;
876
877         }
878         p_line->p_glyph_pos[ i ].x = i_pen_x;
879         p_line->p_glyph_pos[ i ].y = i_pen_y;
880         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
881         if( i_error )
882         {
883             msg_Err( p_filter, "Unable to render text (FT_Load_Glyph returned"
884                                " %d", i_error );
885             goto error;
886         }
887         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
888         if( i_error )
889         {
890             msg_Err( p_filter, "Unable to render text (FT_Get_Glyph returned "
891                                "%d", i_error );
892             goto error;
893         }
894         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
895         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
896         if( i_error )
897         {
898             FT_Done_Glyph( tmp_glyph );
899             continue;
900         }
901         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
902
903         /* Do rest */
904         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
905             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
906         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
907         {
908             p_line->pp_glyphs[ i ] = NULL;
909             FreeLine( p_line );
910             p_line = NewLine( (byte_t *)psz_string );
911             if( p_prev ) p_prev->p_next = p_line;
912             else p_lines = p_line;
913
914             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
915             {
916                 psz_unicode--;
917             }
918             if( psz_unicode == psz_line_start )
919             {
920                 msg_Warn( p_filter, "unbreakable string" );
921                 goto error;
922             }
923             else
924             {
925                 *psz_unicode = '\n';
926             }
927             psz_unicode = psz_line_start;
928             i_pen_x = 0;
929             i_previous = i = 0;
930             line.xMin = line.xMax = line.yMin = line.yMax = 0;
931             continue;
932         }
933         line.yMax = __MAX( line.yMax, glyph_size.yMax );
934         line.yMin = __MIN( line.yMin, glyph_size.yMin );
935
936         i_previous = i_glyph_index;
937         i_pen_x += glyph->advance.x >> 6;
938         i++;
939     }
940
941     p_line->i_width = line.xMax;
942     p_line->i_height = face->size->metrics.height >> 6;
943     p_line->pp_glyphs[ i ] = NULL;
944     p_line->i_alpha = i_font_alpha;
945     p_line->i_red = i_red;
946     p_line->i_green = i_green;
947     p_line->i_blue = i_blue;
948     result.x = __MAX( result.x, line.xMax );
949     result.y += line.yMax - line.yMin;
950
951 #undef face
952 #undef glyph
953
954     p_region_out->i_x = p_region_in->i_x;
955     p_region_out->i_y = p_region_in->i_y;
956
957     if( config_GetInt( p_filter, "freetype-yuvp" ) )
958         Render( p_filter, p_region_out, p_lines, result.x, result.y );
959     else
960         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
961
962     if( psz_unicode_orig ) free( psz_unicode_orig );
963     FreeLines( p_lines );
964     return VLC_SUCCESS;
965
966  error:
967     if( psz_unicode_orig ) free( psz_unicode_orig );
968     FreeLines( p_lines );
969     return VLC_EGENERIC;
970 }
971
972 static void FreeLine( line_desc_t *p_line )
973 {
974     unsigned int i;
975     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
976     {
977         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
978     }
979     free( p_line->pp_glyphs );
980     free( p_line->p_glyph_pos );
981     free( p_line );
982 }
983
984 static void FreeLines( line_desc_t *p_lines )
985 {
986     line_desc_t *p_line, *p_next;
987
988     if( !p_lines ) return;
989
990     for( p_line = p_lines; p_line != NULL; p_line = p_next )
991     {
992         p_next = p_line->p_next;
993         FreeLine( p_line );
994     }
995 }
996
997 static line_desc_t *NewLine( byte_t *psz_string )
998 {
999     int i_count;
1000     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
1001
1002     if( !p_line ) return NULL;
1003     p_line->i_height = 0;
1004     p_line->i_width = 0;
1005     p_line->p_next = NULL;
1006
1007     /* We don't use CountUtf8Characters() here because we are not acutally
1008      * sure the string is utf8. Better be safe than sorry. */
1009     i_count = strlen( (char *)psz_string );
1010
1011     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
1012     if( p_line->pp_glyphs == NULL )
1013     {
1014         free( p_line );
1015         return NULL;
1016     }
1017     p_line->pp_glyphs[0] = NULL;
1018
1019     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
1020     if( p_line->p_glyph_pos == NULL )
1021     {
1022         free( p_line->pp_glyphs );
1023         free( p_line );
1024         return NULL;
1025     }
1026
1027     return p_line;
1028 }
1029
1030 static int SetFontSize( filter_t *p_filter, int i_size )
1031 {
1032     filter_sys_t *p_sys = p_filter->p_sys;
1033
1034     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
1035
1036     if( !i_size )
1037     {
1038         vlc_value_t val;
1039
1040         if( !p_sys->i_default_font_size &&
1041             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
1042             return VLC_SUCCESS;
1043
1044         if( p_sys->i_default_font_size )
1045         {
1046             i_size = p_sys->i_default_font_size;
1047         }
1048         else
1049         {
1050             var_Get( p_filter, "freetype-rel-fontsize", &val );
1051             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
1052             p_filter->p_sys->i_display_height =
1053                 p_filter->fmt_out.video.i_height;
1054         }
1055         if( i_size <= 0 )
1056         {
1057             msg_Warn( p_filter, "Invalid fontsize, using 12" );
1058             i_size = 12;
1059         }
1060
1061         msg_Dbg( p_filter, "Using fontsize: %i", i_size );
1062     }
1063
1064     p_sys->i_font_size = i_size;
1065
1066     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
1067     {
1068         msg_Err( p_filter, "couldn't set font size to %d", i_size );
1069         return VLC_EGENERIC;
1070     }
1071
1072     return VLC_SUCCESS;
1073 }