root/modules/misc/freetype.c

Revision e93b437d4f5b470e229cab95726a2addb6ef3782, 87.9 kB (checked in by Laurent Aimar <fenrir@videolan.org>, 2 months ago)

Fixed support for font color and opacity for html rendered text.

  • Property mode set to 100644
Line 
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
10  *
11  * This program 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.
15  *
16  * This program 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.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_vout.h>
37 #include <vlc_osd.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_stream.h>
41 #include <vlc_xml.h>
42 #include <vlc_input.h>
43 #include <vlc_strings.h>
44
45 #include <math.h>
46 #include <errno.h>
47
48 #include <ft2build.h>
49 #include FT_FREETYPE_H
50 #include FT_GLYPH_H
51 #define FT_FLOOR(X)     ((X & -64) >> 6)
52 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
53 #define FT_MulFix(v, s) (((v)*(s))>>16)
54
55 #ifdef __APPLE__
56 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
57 #define FC_DEFAULT_FONT "Lucida Grande"
58 #elif defined( SYS_BEOS )
59 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
60 #define FC_DEFAULT_FONT "Swiss"
61 #elif defined( WIN32 )
62 #define DEFAULT_FONT "" /* Default font found at run-time */
63 #define FC_DEFAULT_FONT "Arial"
64 #else
65 #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
66 #define FC_DEFAULT_FONT "Serif Bold"
67 #endif
68
69 #if defined(HAVE_FRIBIDI)
70 #include <fribidi/fribidi.h>
71 #endif
72
73 #ifdef HAVE_FONTCONFIG
74 #include <fontconfig/fontconfig.h>
75 #endif
76
77 #include <assert.h>
78
79 /*****************************************************************************
80  * Module descriptor
81  *****************************************************************************/
82 static int  Create ( vlc_object_t * );
83 static void Destroy( vlc_object_t * );
84
85 #define FONT_TEXT N_("Font")
86 #define FONT_LONGTEXT N_("Filename for the font you want to use")
87 #define FONTSIZE_TEXT N_("Font size in pixels")
88 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
89     "that will be rendered on the video. " \
90     "If set to something different than 0 this option will override the " \
91     "relative font size." )
92 #define OPACITY_TEXT N_("Opacity")
93 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
94     "text that will be rendered on the video. 0 = transparent, " \
95     "255 = totally opaque. " )
96 #define COLOR_TEXT N_("Text default color")
97 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
98     "the video. This must be an hexadecimal (like HTML colors). The first two "\
99     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
100     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
101 #define FONTSIZER_TEXT N_("Relative font size")
102 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
103     "fonts that will be rendered on the video. If absolute font size is set, "\
104     "relative size will be overriden." )
105
106 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
107 static const char *const ppsz_sizes_text[] = {
108     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
109 #define YUVP_TEXT N_("Use YUVP renderer")
110 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
111   "This option is only needed if you want to encode into DVB subtitles" )
112 #define EFFECT_TEXT N_("Font Effect")
113 #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
114 "text to improve its readability." )
115
116 #define EFFECT_BACKGROUND  1
117 #define EFFECT_OUTLINE     2
118 #define EFFECT_OUTLINE_FAT 3
119
120 static int const pi_effects[] = { 1, 2, 3 };
121 static const char *const ppsz_effects_text[] = {
122     N_("Background"),N_("Outline"), N_("Fat Outline") };
123 static const int pi_color_values[] = {
124   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
125   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
126   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
127
128 static const char *const ppsz_color_descriptions[] = {
129   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
130   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
131   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
132
133 vlc_module_begin();
134     set_shortname( N_("Text renderer"));
135     set_description( N_("Freetype2 font renderer") );
136     set_category( CAT_VIDEO );
137     set_subcategory( SUBCAT_VIDEO_SUBPIC );
138
139     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
140               false );
141
142     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
143                  FONTSIZE_LONGTEXT, true );
144
145     /* opacity valid on 0..255, with default 255 = fully opaque */
146     add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
147         OPACITY_TEXT, OPACITY_LONGTEXT, true );
148
149     /* hook to the color values list, with default 0x00ffffff = white */
150     add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
151                  COLOR_LONGTEXT, false );
152         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
153
154     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
155                  FONTSIZER_LONGTEXT, false );
156         change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
157     add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
158                  EFFECT_LONGTEXT, false );
159         change_integer_list( pi_effects, ppsz_effects_text, NULL );
160
161     add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
162               YUVP_LONGTEXT, true );
163     set_capability( "text renderer", 100 );
164     add_shortcut( "text" );
165     set_callbacks( Create, Destroy );
166 vlc_module_end();
167
168
169
170 /*****************************************************************************
171  * Local prototypes
172  *****************************************************************************/
173
174 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
175 static int RenderText( filter_t *, subpicture_region_t *,
176                        subpicture_region_t * );
177 #ifdef HAVE_FONTCONFIG
178 static int RenderHtml( filter_t *, subpicture_region_t *,
179                        subpicture_region_t * );
180 static char *FontConfig_Select( FcConfig *, const char *,
181                                 bool, bool, int * );
182 #endif
183
184
185 static int LoadFontsFromAttachments( filter_t *p_filter );
186
187 static int GetFontSize( filter_t *p_filter );
188 static int SetFontSize( filter_t *, int );
189 static void YUVFromRGB( uint32_t i_argb,
190                         uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
191
192 typedef struct line_desc_t line_desc_t;
193 struct line_desc_t
194 {
195     /** NULL-terminated list of glyphs making the string */
196     FT_BitmapGlyph *pp_glyphs;
197     /** list of relative positions for the glyphs */
198     FT_Vector      *p_glyph_pos;
199     /** list of RGB information for styled text
200      * -- if the rendering mode supports it (RenderYUVA) and
201      *  b_new_color_mode is set, then it becomes possible to
202      *  have multicoloured text within the subtitles. */
203     uint32_t       *p_fg_rgb;
204     uint32_t       *p_bg_rgb;
205     uint8_t        *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
206     bool      b_new_color_mode;
207     /** underline information -- only supplied if text should be underlined */
208     uint16_t       *pi_underline_offset;
209     uint16_t       *pi_underline_thickness;
210
211     int             i_height;
212     int             i_width;
213     int             i_red, i_green, i_blue;
214     int             i_alpha;
215
216     line_desc_t    *p_next;
217 };
218 static line_desc_t *NewLine( int );
219
220 typedef struct
221 {
222     int         i_font_size;
223     uint32_t    i_font_color;         /* ARGB */
224     uint32_t    i_karaoke_bg_color;   /* ARGB */
225     bool  b_italic;
226     bool  b_bold;
227     bool  b_underline;
228     char       *psz_fontname;
229 } ft_style_t;
230
231 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
232 static void FreeLines( line_desc_t * );
233 static void FreeLine( line_desc_t * );
234
235 #ifdef HAVE_FONTCONFIG
236 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock  );
237 static void  FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
238 static void* FontBuilderThread( vlc_object_t *p_this);
239 static void  FontBuilderDestructor( vlc_object_t *p_this );
240 static void  FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
241 static int   FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
242                         void* );
243 #endif
244
245 /*****************************************************************************
246  * filter_sys_t: freetype local data
247  *****************************************************************************
248  * This structure is part of the video output thread descriptor.
249  * It describes the freetype specific properties of an output thread.
250  *****************************************************************************/
251 struct filter_sys_t
252 {
253     FT_Library     p_library;   /* handle to library     */
254     FT_Face        p_face;      /* handle to face object */
255     bool     i_use_kerning;
256     uint8_t        i_font_opacity;
257     int            i_font_color;
258     int            i_font_size;
259     int            i_effect;
260
261     int            i_default_font_size;
262     int            i_display_height;
263 #ifdef HAVE_FONTCONFIG
264     vlc_mutex_t   *p_fontconfig_lock;
265     bool           b_fontconfig_ok;
266     FcConfig      *p_fontconfig;
267 #endif
268
269     input_attachment_t **pp_font_attachments;
270     int                  i_font_attachments;
271
272     vlc_object_t  *p_fontbuilder;
273 };
274
275 #define UCHAR uint32_t
276 #define TR_DEFAULT_FONT FC_DEFAULT_FONT
277 #define TR_FONT_STYLE_PTR ft_style_t *
278
279 #include "text_renderer.h"
280
281 /*****************************************************************************
282  * Create: allocates osd-text video thread output method
283  *****************************************************************************
284  * This function allocates and initializes a Clone vout method.
285  *****************************************************************************/
286 static int Create( vlc_object_t *p_this )
287 {
288     filter_t      *p_filter = (filter_t *)p_this;
289     filter_sys_t  *p_sys;
290     char          *psz_fontfile = NULL;
291     int            i_error;
292     vlc_value_t    val;
293
294     /* Allocate structure */
295     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
296     if( !p_sys )
297         return VLC_ENOMEM;
298     p_sys->p_face = 0;
299     p_sys->p_library = 0;
300     p_sys->i_font_size = 0;
301     p_sys->i_display_height = 0;
302
303     var_Create( p_filter, "freetype-font",
304                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
305     var_Create( p_filter, "freetype-fontsize",
306                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
307     var_Create( p_filter, "freetype-rel-fontsize",
308                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
309     var_Create( p_filter, "freetype-opacity",
310                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
311     var_Create( p_filter, "freetype-effect",
312                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
313     var_Get( p_filter, "freetype-opacity", &val );
314     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
315     var_Create( p_filter, "freetype-color",
316                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
317     var_Get( p_filter, "freetype-color", &val );
318     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
319     p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
320
321     /* Look what method was requested */
322     var_Get( p_filter, "freetype-font", &val );
323     psz_fontfile = val.psz_string;
324     if( !psz_fontfile || !*psz_fontfile )
325     {
326         free( psz_fontfile );
327         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
328         if( !psz_fontfile )
329             goto error;
330 #ifdef WIN32
331         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
332         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
333 #elif defined(__APPLE__)
334         strcpy( psz_fontfile, DEFAULT_FONT );
335 #else
336         msg_Err( p_filter, "user didn't specify a font" );
337         goto error;
338 #endif
339     }
340
341     i_error = FT_Init_FreeType( &p_sys->p_library );
342     if( i_error )
343     {
344         msg_Err( p_filter, "couldn't initialize freetype" );
345         goto error;
346     }
347     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
348                            0, &p_sys->p_face );
349     if( i_error == FT_Err_Unknown_File_Format )
350     {
351         msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
352         goto error;
353     }
354     else if( i_error )
355     {
356         msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
357         goto error;
358     }
359
360     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
361     if( i_error )
362     {
363         msg_Err( p_filter, "font has no unicode translation table" );
364         goto error;
365     }
366
367 #ifdef HAVE_FONTCONFIG
368     p_sys->b_fontconfig_ok = false;
369     p_sys->p_fontconfig    = NULL;
370     p_sys->p_fontbuilder   = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
371 #endif
372
373     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
374
375     var_Get( p_filter, "freetype-fontsize", &val );
376     p_sys->i_default_font_size = val.i_int;
377     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
378
379     free( psz_fontfile );
380
381     p_sys->pp_font_attachments = NULL;
382     p_sys->i_font_attachments = 0;
383
384     p_filter->pf_render_text = RenderText;
385 #ifdef HAVE_FONTCONFIG
386     p_filter->pf_render_html = RenderHtml;
387 #else
388     p_filter->pf_render_html = NULL;
389 #endif
390
391     LoadFontsFromAttachments( p_filter );
392
393     return VLC_SUCCESS;
394
395  error:
396     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
397     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
398     free( psz_fontfile );
399     free( p_sys );
400     return VLC_EGENERIC;
401 }
402
403 /*****************************************************************************
404  * Destroy: destroy Clone video thread output method
405  *****************************************************************************
406  * Clean up all data and library connections
407  *****************************************************************************/
408 static void Destroy( vlc_object_t *p_this )
409 {
410     filter_t *p_filter = (filter_t *)p_this;
411     filter_sys_t *p_sys = p_filter->p_sys;
412
413     if( p_sys->pp_font_attachments )
414     {
415         int   k;
416
417         for( k = 0; k < p_sys->i_font_attachments; k++ )
418             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
419
420         free( p_sys->pp_font_attachments );
421     }
422
423 #ifdef HAVE_FONTCONFIG
424     FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
425 #endif
426
427     /* FcFini asserts calling the subfunction FcCacheFini()
428      * even if no other library functions have been made since FcInit(),
429      * so don't call it. */
430
431     FT_Done_Face( p_sys->p_face );
432     FT_Done_FreeType( p_sys->p_library );
433     free( p_sys );
434 }
435
436 #ifdef HAVE_FONTCONFIG
437 static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
438 {
439     /* Check for an existing Fontbuilder thread */
440     vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
441     vlc_object_t *p_fontbuilder =
442         vlc_object_find_name( p_filter->p_libvlc,
443                               "fontlist builder", FIND_CHILD );
444
445     if( !p_fontbuilder )
446     {
447         /* Create the FontBuilderThread thread as a child of a top-level
448          * object, so that it can survive the destruction of the
449          * freetype object - the fontlist only needs to be built once,
450          * and calling the fontbuild a second time while the first is
451          * still in progress can cause thread instabilities.
452          *
453          * XXX The fontbuilder will be destroy as soon as it is unused.
454          */
455
456         p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
457                                            sizeof(vlc_object_t) );
458         if( p_fontbuilder )
459         {
460             p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
461             p_fontbuilder->p_private = NULL;
462             vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
463
464             vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
465
466             var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
467             var_SetBool( p_fontbuilder, "build-done", false );
468
469             if( vlc_thread_create( p_fontbuilder,
470                                    "fontlist builder",
471                                    FontBuilderThread,
472                                    VLC_THREAD_PRIORITY_LOW,
473                                    false ) )
474             {
475                 msg_Warn( p_filter, "fontconfig database builder thread can't "
476                         "be launched. Font styling support will be limited." );
477             }
478         }
479     }
480     if( p_fontbuilder )
481     {
482         var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
483         FontBuilderGetFcConfig( p_filter, p_fontbuilder );
484     }
485     vlc_mutex_unlock( p_lock );
486     *pp_lock = p_lock;
487     return p_fontbuilder;
488 }
489 static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
490 {
491     vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
492     if( p_fontbuilder )
493     {
494         const bool b_alive = vlc_object_alive( p_fontbuilder );
495
496         var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
497
498         /* We wait for the thread on the first FontBuilderDetach */
499         if( b_alive )
500         {
501             vlc_object_kill( p_fontbuilder );
502             vlc_mutex_unlock( lock );
503
504             /* We need to unlock otherwise we may not join (the thread waiting
505              * for the lock). It is safe to unlock as no one else will try a
506              * join and we have a reference on the object) */
507             vlc_thread_join( p_fontbuilder );
508
509             vlc_mutex_lock( lock );
510         }
511         vlc_object_release( p_fontbuilder );
512     }
513     vlc_mutex_unlock( lock );
514 }
515 static void* FontBuilderThread( vlc_object_t *p_this )
516 {
517     FcConfig      *p_fontconfig = FcInitLoadConfig();
518
519     vlc_thread_ready( p_this );
520
521     if( p_fontconfig )
522     {
523         mtime_t    t1, t2;
524         int canc = vlc_savecancel ();
525
526         //msg_Dbg( p_this, "Building font database..." );
527         msg_Dbg( p_this, "Building font database..." );
528         t1 = mdate();
529         if(! FcConfigBuildFonts( p_fontconfig ))
530         {
531             /* Don't destroy the fontconfig object - we won't be able to do
532              * italics or bold or change the font face, but we will still
533              * be able to do underline and change the font size.
534              */
535             msg_Err( p_this, "fontconfig database can't be built. "
536                                     "Font styling won't be available" );
537         }
538         t2 = mdate();
539
540         msg_Dbg( p_this, "Finished building font database." );
541         msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
542
543         vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
544         p_this->p_private = p_fontconfig;
545         vlc_mutex_unlock( p_lock );
546
547         var_SetBool( p_this, "build-done", true );
548         vlc_restorecancel (canc);
549     }
550     return NULL;
551 }
552 static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
553 {
554     filter_sys_t *p_sys = p_filter->p_sys;
555
556     p_sys->p_fontconfig = p_fontbuilder->p_private;
557     p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
558 }
559 static void FontBuilderDestructor( vlc_object_t *p_this )
560 {
561     FcConfig *p_fontconfig = p_this->p_private;
562
563     if( p_fontconfig )
564         FcConfigDestroy( p_fontconfig );
565 }
566 static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
567                        vlc_value_t oldval, vlc_value_t newval, void *param )
568 {
569     filter_t *p_filter = param;
570
571     if( newval.b_bool )
572     {
573         vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
574
575         FontBuilderGetFcConfig( p_filter, p_this );
576
577         vlc_mutex_unlock( p_lock );
578     }
579
580     VLC_UNUSED(psz_var);
581     VLC_UNUSED(oldval);
582     return VLC_SUCCESS;
583 }
584 #endif
585
586 /*****************************************************************************
587  * Make any TTF/OTF fonts present in the attachments of the media file
588  * and store them for later use by the FreeType Engine
589  *****************************************************************************/
590 static int LoadFontsFromAttachments( filter_t *p_filter )
591 {
592     filter_sys_t         *p_sys = p_filter->p_sys;
593     input_thread_t       *p_input;
594     input_attachment_t  **pp_attachments;
595     int                   i_attachments_cnt;
596     int                   k;
597     int                   rv = VLC_SUCCESS;
598
599     p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
600     if( ! p_input )
601         return VLC_EGENERIC;
602
603     if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
604     {
605         vlc_object_release(p_input);
606         return VLC_EGENERIC;
607     }
608
609     p_sys->i_font_attachments = 0;
610     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
611     if(! p_sys->pp_font_attachments )
612         rv = VLC_ENOMEM;
613
614     for( k = 0; k < i_attachments_cnt; k++ )
615     {
616         input_attachment_t *p_attach = pp_attachments[k];
617
618         if( p_sys->pp_font_attachments )
619         {
620             if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
621                  !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
622                ( p_attach->i_data > 0 ) &&
623                ( p_attach->p_data != NULL ) )
624             {
625                 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
626             }
627             else
628             {
629                 vlc_input_attachment_Delete( p_attach );
630             }
631         }
632         else
633         {
634             vlc_input_attachment_Delete( p_attach );
635         }
636     }
637     free( pp_attachments );
638
639     vlc_object_release(p_input);
640
641     return rv;
642 }
643
644 /*****************************************************************************
645  * Render: place string in picture
646  *****************************************************************************
647  * This function merges the previously rendered freetype glyphs into a picture
648  *****************************************************************************/
649 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
650                    line_desc_t *p_line, int i_width, int i_height )
651 {
652     static const uint8_t pi_gamma[16] =
653         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
654           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
655
656     uint8_t *p_dst;
657     video_format_t fmt;
658     int i, x, y, i_pitch;
659     uint8_t i_y; /* YUV values, derived from incoming RGB */
660     int8_t i_u, i_v;
661
662     /* Create a new subpicture region */
663     memset( &fmt, 0, sizeof(video_format_t) );
664     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
665     fmt.i_aspect = 0;
666     fmt.i_width = fmt.i_visible_width = i_width + 4;
667     fmt.i_height = fmt.i_visible_height = i_height + 4;
668     if( p_region->fmt.i_visible_width > 0 )
669         fmt.i_visible_width = p_region->fmt.i_visible_width;
670     if( p_region->fmt.i_visible_height > 0 )
671         fmt.i_visible_height = p_region->fmt.i_visible_height;
672     fmt.i_x_offset = fmt.i_y_offset = 0;
673
674     assert( !p_region->p_picture );
675     p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
676     if( !p_region->p_picture )
677         return VLC_EGENERIC;
678     p_region->fmt = fmt;
679
680     /* Calculate text color components */
681     i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
682                       25 * p_line->i_blue + 128) >> 8) +  16;
683     i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
684                      112 * p_line->i_blue + 128) >> 8) + 128;
685     i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
686                       18 * p_line->i_blue + 128) >> 8) + 128;
687
688     /* Build palette */
689     fmt.p_palette->i_entries = 16;
690     for( i = 0; i < 8; i++ )
691     {
692         fmt.p_palette->palette[i][0] = 0;
693         fmt.p_palette->palette[i][1] = 0x80;
694         fmt.p_palette->palette[i][2] = 0x80;
695         fmt.p_palette->palette[i][3] = pi_gamma[i];
696         fmt.p_palette->palette[i][3] =
697             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
698     }
699     for( i = 8; i < fmt.p_palette->i_entries; i++ )
700     {
701         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
702         fmt.p_palette->palette[i][1] = i_u;
703         fmt.p_palette->palette[i][2] = i_v;
704         fmt.p_palette->palette[i][3] = pi_gamma[i];
705         fmt.p_palette->palette[i][3] =
706             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
707     }
708
709     p_dst = p_region->p_picture->Y_PIXELS;
710     i_pitch = p_region->p_picture->Y_PITCH;
711
712     /* Initialize the region pixels */
713     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
714
715     for( ; p_line != NULL; p_line = p_line->p_next )
716     {
717         int i_glyph_tmax = 0;
718         int i_bitmap_offset, i_offset, i_align_offset = 0;
719         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
720         {
721             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
722             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
723         }
724
725         if( p_line->i_width < i_width )
726         {
727             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
728             {
729                 i_align_offset = i_width - p_line->i_width;
730             }
731             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
732             {
733                 i_align_offset = ( i_width - p_line->i_width ) / 2;
734             }
735         }
736
737         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
738         {
739             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
740
741             i_offset = ( p_line->p_glyph_pos[ i ].y +
742                 i_glyph_tmax - p_glyph->top + 2 ) *
743                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
744                 i_align_offset;
745
746             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
747             {
748                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
749                 {
750                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
751                         p_dst[i_offset+x] =
752                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
753                 }
754                 i_offset += i_pitch;
755             }
756         }
757     }
758
759     /* Outlining (find something better than nearest neighbour filtering ?) */
760     if( 1 )
761     {
762         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
763         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
764         uint8_t left, current;
765
766         for( y = 1; y < (int)fmt.i_height - 1; y++ )
767         {
768             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
769             p_dst += p_region->p_picture->Y_PITCH;
770             left = 0;
771
772             for( x = 1; x < (int)fmt.i_width - 1; x++ )
773             {
774                 current = p_dst[x];
775                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
776                              p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
777                 left = current;
778             }
779         }
780         memset( p_top, 0, fmt.i_width );
781     }
782
783     return VLC_SUCCESS;
784 }
785
786 static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
787                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
788                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
789                                 int i_glyph_tmax, int i_align_offset,
790                                 uint8_t i_y, uint8_t i_u, uint8_t i_v,
791                                 subpicture_region_t *p_region)
792 {
793     int y, x, z;
794     int i_pitch;
795     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
796
797     p_dst_y = p_region->p_picture->Y_PIXELS;
798     p_dst_u = p_region->p_picture->U_PIXELS;
799     p_dst_v = p_region->p_picture->V_PIXELS;
800     p_dst_a = p_region->p_picture->A_PIXELS;
801     i_pitch = p_region->p_picture->A_PITCH;
802
803     int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
804                      p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
805
806     for( y = 0; y < i_line_thickness; y++ )
807     {
808         int i_extra = p_this_glyph->bitmap.width;
809
810         if( b_ul_next_char )
811         {
812             i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
813                       (p_this_glyph_pos->x + p_this_glyph->left);
814         }
815         for( x = 0; x < i_extra; x++ )
816         {
817             bool b_ok = true;
818
819             /* break the underline around the tails of any glyphs which cross it */
820             for( z = x - i_line_thickness;
821                  z < x + i_line_thickness && b_ok;
822                  z++ )
823             {
824                 if( p_next_glyph && ( z >= i_extra ) )
825                 {
826                     int i_row = i_line_offset + p_next_glyph->top + y;
827
828                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
829                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
830                     {
831                         b_ok = false;
832                     }
833                 }
834                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
835                 {
836                     int i_row = i_line_offset + p_this_glyph->top + y;
837
838                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
839                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
840                     {
841                         b_ok = false;
842                     }
843                 }
844             }
845
846             if( b_ok )
847             {
848                 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
849                 p_dst_u[i_offset+x] = i_u;
850                 p_dst_v[i_offset+x] = i_v;
851                 p_dst_a[i_offset+x] = 255;
852             }
853         }
854         i_offset += i_pitch;
855     }
856 }
857
858 static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
859 {
860     uint8_t *p_dst = p_region->p_picture->A_PIXELS;
861     int i_pitch = p_region->p_picture->A_PITCH;
862     int x,y;
863
864     for( ; p_line != NULL; p_line = p_line->p_next )
865     {
866         int i_glyph_tmax=0, i = 0;
867         int i_bitmap_offset, i_offset, i_align_offset = 0;
868