]> git.sesse.net Git - vlc/blob - modules/text_renderer/freetype.c
mux: avi: fix leak on format failure
[vlc] / modules / text_renderer / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
10  *          Jean-Baptiste Kempf <jb@videolan.org>
11  *          Felix Paul Kühne <fkuehne@videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation, Inc.,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35 #include <math.h>
36
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_stream.h>                        /* stream_MemoryNew */
40 #include <vlc_input.h>                         /* vlc_input_attachment_* */
41 #include <vlc_xml.h>                           /* xml_reader */
42 #include <vlc_dialog.h>                        /* FcCache dialog */
43 #include <vlc_filter.h>                                      /* filter_sys_t */
44 #include <vlc_text_style.h>                                   /* text_style_t*/
45
46 /* Freetype */
47 #include <ft2build.h>
48 #include FT_FREETYPE_H
49 #include FT_GLYPH_H
50 #include FT_STROKER_H
51 #include FT_SYNTHESIS_H
52
53 #define FT_FLOOR(X)     ((X & -64) >> 6)
54 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
55 #ifndef FT_MulFix
56  #define FT_MulFix(v, s) (((v)*(s))>>16)
57 #endif
58
59 /* RTL */
60 #if defined(HAVE_FRIBIDI)
61 # include <fribidi/fribidi.h>
62 #endif
63
64 /* apple stuff */
65 #ifdef __APPLE__
66 # include <TargetConditionals.h>
67 # undef HAVE_FONTCONFIG
68 # define HAVE_GET_FONT_BY_FAMILY_NAME
69 #endif
70
71 /* Win32 */
72 #ifdef _WIN32
73 # undef HAVE_FONTCONFIG
74 # if !VLC_WINSTORE_APP
75 #  define HAVE_GET_FONT_BY_FAMILY_NAME
76 # endif
77 #endif
78
79 /* FontConfig */
80 #ifdef HAVE_FONTCONFIG
81 # define HAVE_GET_FONT_BY_FAMILY_NAME
82 #endif
83
84 #include <assert.h>
85
86 #include "text_renderer.h"
87 #include "platform_fonts.h"
88
89 /*****************************************************************************
90  * Module descriptor
91  *****************************************************************************/
92 static int  Create ( vlc_object_t * );
93 static void Destroy( vlc_object_t * );
94
95 #define FONT_TEXT N_("Font")
96 #define MONOSPACE_FONT_TEXT N_("Monospace Font")
97
98 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
99 #define FONT_LONGTEXT N_("Font file for the font you want to use")
100
101 #define FONTSIZE_TEXT N_("Font size in pixels")
102 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
103     "that will be rendered on the video. " \
104     "If set to something different than 0 this option will override the " \
105     "relative font size." )
106 #define OPACITY_TEXT N_("Text opacity")
107 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
108     "text that will be rendered on the video. 0 = transparent, " \
109     "255 = totally opaque. " )
110 #define COLOR_TEXT N_("Text default color")
111 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
112     "the video. This must be an hexadecimal (like HTML colors). The first two "\
113     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
114     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
115 #define FONTSIZER_TEXT N_("Relative font size")
116 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
117     "fonts that will be rendered on the video. If absolute font size is set, "\
118     "relative size will be overridden." )
119 #define BOLD_TEXT N_("Force bold")
120
121 #define BG_OPACITY_TEXT N_("Background opacity")
122 #define BG_COLOR_TEXT N_("Background color")
123
124 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
125 #define OUTLINE_COLOR_TEXT N_("Outline color")
126 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
127
128 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
129 #define SHADOW_COLOR_TEXT N_("Shadow color")
130 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
131 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
132
133
134 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
135 static const char *const ppsz_sizes_text[] = {
136     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
137 #define YUVP_TEXT N_("Use YUVP renderer")
138 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
139   "This option is only needed if you want to encode into DVB subtitles" )
140
141 static const int pi_color_values[] = {
142   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
143   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
144   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
145
146 static const char *const ppsz_color_descriptions[] = {
147   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
148   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
149   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
150
151 static const int pi_outline_thickness[] = {
152     0, 2, 4, 6,
153 };
154 static const char *const ppsz_outline_thickness[] = {
155     N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
156 };
157
158 vlc_module_begin ()
159     set_shortname( N_("Text renderer"))
160     set_description( N_("Freetype2 font renderer") )
161     set_category( CAT_VIDEO )
162     set_subcategory( SUBCAT_VIDEO_SUBPIC )
163
164 #ifdef HAVE_GET_FONT_BY_FAMILY_NAME
165     add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
166     add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
167 #else
168     add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
169     add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
170 #endif
171
172     add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
173                  FONTSIZE_LONGTEXT, true )
174         change_integer_range( 0, 4096)
175         change_safe()
176
177     add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
178                  FONTSIZER_LONGTEXT, false )
179         change_integer_list( pi_sizes, ppsz_sizes_text )
180         change_safe()
181
182     /* opacity valid on 0..255, with default 255 = fully opaque */
183     add_integer_with_range( "freetype-opacity", 255, 0, 255,
184         OPACITY_TEXT, OPACITY_LONGTEXT, false )
185         change_safe()
186
187     /* hook to the color values list, with default 0x00ffffff = white */
188     add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
189                  COLOR_LONGTEXT, false )
190         change_integer_list( pi_color_values, ppsz_color_descriptions )
191         change_safe()
192
193     add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
194         change_safe()
195
196     add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
197                             BG_OPACITY_TEXT, NULL, false )
198         change_safe()
199     add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
200              NULL, false )
201         change_integer_list( pi_color_values, ppsz_color_descriptions )
202         change_safe()
203
204     add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
205                             OUTLINE_OPACITY_TEXT, NULL, false )
206         change_safe()
207     add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
208              NULL, false )
209         change_integer_list( pi_color_values, ppsz_color_descriptions )
210         change_safe()
211     add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
212              NULL, false )
213         change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
214         change_safe()
215
216     add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
217                             SHADOW_OPACITY_TEXT, NULL, false )
218         change_safe()
219     add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
220              NULL, false )
221         change_integer_list( pi_color_values, ppsz_color_descriptions )
222         change_safe()
223     add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
224                           SHADOW_ANGLE_TEXT, NULL, false )
225         change_safe()
226     add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
227                           SHADOW_DISTANCE_TEXT, NULL, false )
228         change_safe()
229
230     add_obsolete_integer( "freetype-effect" );
231
232     add_bool( "freetype-yuvp", false, YUVP_TEXT,
233               YUVP_LONGTEXT, true )
234     set_capability( "text renderer", 100 )
235     add_shortcut( "text" )
236     set_callbacks( Create, Destroy )
237 vlc_module_end ()
238
239
240 /*****************************************************************************
241  * Local prototypes
242  *****************************************************************************/
243
244 typedef struct
245 {
246     FT_BitmapGlyph p_glyph;
247     FT_BitmapGlyph p_outline;
248     FT_BitmapGlyph p_shadow;
249     uint32_t       i_color;             /* ARGB color */
250     int            i_line_offset;       /* underline/strikethrough offset */
251     int            i_line_thickness;    /* underline/strikethrough thickness */
252 } line_character_t;
253
254 typedef struct line_desc_t line_desc_t;
255 struct line_desc_t
256 {
257     line_desc_t      *p_next;
258
259     int              i_width;
260     int              i_height;
261     int              i_base_line;
262     int              i_character_count;
263     line_character_t *p_character;
264 };
265
266 /*****************************************************************************
267  * filter_sys_t: freetype local data
268  *****************************************************************************
269  * This structure is part of the video output thread descriptor.
270  * It describes the freetype specific properties of an output thread.
271  *****************************************************************************/
272 struct filter_sys_t
273 {
274     FT_Library     p_library;   /* handle to library     */
275     FT_Face        p_face;      /* handle to face object */
276     FT_Stroker     p_stroker;   /* handle to path stroker object */
277
278     xml_reader_t  *p_xml;       /* vlc xml parser */
279
280     text_style_t   style;       /* Current Style */
281
282     /* More styles... */
283     float          f_shadow_vector_x;
284     float          f_shadow_vector_y;
285     int            i_default_font_size;
286
287     /* Attachments */
288     input_attachment_t **pp_font_attachments;
289     int                  i_font_attachments;
290
291     char * (*pf_select) (filter_t *, const char* family,
292                                bool bold, bool italic, int size,
293                                int *index);
294
295 };
296
297 /* */
298 static void YUVFromRGB( uint32_t i_argb,
299                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
300 {
301     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
302     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
303     int i_blue  = ( i_argb & 0x000000ff );
304
305     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
306                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
307     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
308                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
309     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
310                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
311 }
312 static void RGBFromRGB( uint32_t i_argb,
313                         uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
314 {
315     *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
316     *pi_g = ( i_argb & 0x0000ff00 ) >>  8;
317     *pi_b = ( i_argb & 0x000000ff );
318 }
319
320 /*****************************************************************************
321  * Make any TTF/OTF fonts present in the attachments of the media file
322  * and store them for later use by the FreeType Engine
323  *****************************************************************************/
324 static int LoadFontsFromAttachments( filter_t *p_filter )
325 {
326     filter_sys_t         *p_sys = p_filter->p_sys;
327     input_attachment_t  **pp_attachments;
328     int                   i_attachments_cnt;
329
330     if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
331         return VLC_EGENERIC;
332
333     p_sys->i_font_attachments = 0;
334     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
335     if( !p_sys->pp_font_attachments )
336         return VLC_ENOMEM;
337
338     for( int k = 0; k < i_attachments_cnt; k++ )
339     {
340         input_attachment_t *p_attach = pp_attachments[k];
341
342         if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
343               !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
344             p_attach->i_data > 0 && p_attach->p_data )
345         {
346             p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
347         }
348         else
349         {
350             vlc_input_attachment_Delete( p_attach );
351         }
352     }
353     free( pp_attachments );
354
355     return VLC_SUCCESS;
356 }
357
358 static int GetFontSize( filter_t *p_filter )
359 {
360     filter_sys_t *p_sys = p_filter->p_sys;
361     int           i_size = 0;
362
363     if( p_sys->i_default_font_size )
364     {
365         i_size = p_sys->i_default_font_size;
366     }
367     else
368     {
369         int i_ratio = var_InheritInteger( p_filter, "freetype-rel-fontsize" );
370         if( i_ratio > 0 )
371         {
372             i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
373         }
374     }
375     if( i_size <= 0 )
376     {
377         msg_Warn( p_filter, "invalid fontsize, using 12" );
378         i_size = 12;
379     }
380     return i_size;
381 }
382
383 static int SetFontSize( filter_t *p_filter, int i_size )
384 {
385     filter_sys_t *p_sys = p_filter->p_sys;
386
387     if( !i_size )
388     {
389         i_size = GetFontSize( p_filter );
390
391         msg_Dbg( p_filter, "using fontsize: %i", i_size );
392     }
393
394     p_sys->style.i_font_size = i_size;
395
396     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
397     {
398         msg_Err( p_filter, "couldn't set font size to %d", i_size );
399         return VLC_EGENERIC;
400     }
401
402     return VLC_SUCCESS;
403 }
404
405 /*****************************************************************************
406  * RenderYUVP: place string in picture
407  *****************************************************************************
408  * This function merges the previously rendered freetype glyphs into a picture
409  *****************************************************************************/
410 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
411                        line_desc_t *p_line,
412                        FT_BBox *p_bbox )
413 {
414     VLC_UNUSED(p_filter);
415     static const uint8_t pi_gamma[16] =
416         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
417           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
418
419     uint8_t *p_dst;
420     video_format_t fmt;
421     int i, x, y, i_pitch;
422     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
423
424     /* Create a new subpicture region */
425     video_format_Init( &fmt, VLC_CODEC_YUVP );
426     fmt.i_width          =
427     fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
428     fmt.i_height         =
429     fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
430
431     assert( !p_region->p_picture );
432     p_region->p_picture = picture_NewFromFormat( &fmt );
433     if( !p_region->p_picture )
434         return VLC_EGENERIC;
435     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
436     p_region->fmt = fmt;
437
438     /* Calculate text color components
439      * Only use the first color */
440     int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
441     YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
442
443     /* Build palette */
444     fmt.p_palette->i_entries = 16;
445     for( i = 0; i < 8; i++ )
446     {
447         fmt.p_palette->palette[i][0] = 0;
448         fmt.p_palette->palette[i][1] = 0x80;
449         fmt.p_palette->palette[i][2] = 0x80;
450         fmt.p_palette->palette[i][3] = pi_gamma[i];
451         fmt.p_palette->palette[i][3] =
452             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
453     }
454     for( i = 8; i < fmt.p_palette->i_entries; i++ )
455     {
456         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
457         fmt.p_palette->palette[i][1] = i_u;
458         fmt.p_palette->palette[i][2] = i_v;
459         fmt.p_palette->palette[i][3] = pi_gamma[i];
460         fmt.p_palette->palette[i][3] =
461             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
462     }
463
464     p_dst = p_region->p_picture->Y_PIXELS;
465     i_pitch = p_region->p_picture->Y_PITCH;
466
467     /* Initialize the region pixels */
468     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
469
470     for( ; p_line != NULL; p_line = p_line->p_next )
471     {
472         int i_align_left = 0;
473         if( p_line->i_width < (int)fmt.i_visible_width )
474         {
475             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
476                 i_align_left = ( fmt.i_visible_width - p_line->i_width );
477             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
478                 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
479         }
480         int i_align_top = 0;
481
482         for( i = 0; i < p_line->i_character_count; i++ )
483         {
484             const line_character_t *ch = &p_line->p_character[i];
485             FT_BitmapGlyph p_glyph = ch->p_glyph;
486
487             int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
488             int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
489
490             for( y = 0; y < p_glyph->bitmap.rows; y++ )
491             {
492                 for( x = 0; x < p_glyph->bitmap.width; x++ )
493                 {
494                     if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
495                         p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
496                             (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
497                 }
498             }
499         }
500     }
501
502     /* Outlining (find something better than nearest neighbour filtering ?) */
503     if( 1 )
504     {
505         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
506         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
507         uint8_t left, current;
508
509         for( y = 1; y < (int)fmt.i_height - 1; y++ )
510         {
511             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
512             p_dst += p_region->p_picture->Y_PITCH;
513             left = 0;
514
515             for( x = 1; x < (int)fmt.i_width - 1; x++ )
516             {
517                 current = p_dst[x];
518                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
519                              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;
520                 left = current;
521             }
522         }
523         memset( p_top, 0, fmt.i_width );
524     }
525
526     return VLC_SUCCESS;
527 }
528
529 /*****************************************************************************
530  * RenderYUVA: place string in picture
531  *****************************************************************************
532  * This function merges the previously rendered freetype glyphs into a picture
533  *****************************************************************************/
534 static void FillYUVAPicture( picture_t *p_picture,
535                              int i_a, int i_y, int i_u, int i_v )
536 {
537     memset( p_picture->p[0].p_pixels, i_y,
538             p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
539     memset( p_picture->p[1].p_pixels, i_u,
540             p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
541     memset( p_picture->p[2].p_pixels, i_v,
542             p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
543     memset( p_picture->p[3].p_pixels, i_a,
544             p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
545 }
546
547 static inline void BlendYUVAPixel( picture_t *p_picture,
548                                    int i_picture_x, int i_picture_y,
549                                    int i_a, int i_y, int i_u, int i_v,
550                                    int i_alpha )
551 {
552     int i_an = i_a * i_alpha / 255;
553
554     uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
555     uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
556     uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
557     uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
558
559     int i_ao = *p_a;
560     if( i_ao == 0 )
561     {
562         *p_y = i_y;
563         *p_u = i_u;
564         *p_v = i_v;
565         *p_a = i_an;
566     }
567     else
568     {
569         *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
570         if( *p_a != 0 )
571         {
572             *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
573             *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
574             *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
575         }
576     }
577 }
578
579 static void FillRGBAPicture( picture_t *p_picture,
580                              int i_a, int i_r, int i_g, int i_b )
581 {
582     for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
583     {
584         for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
585         {
586             uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
587             p_rgba[0] = i_r;
588             p_rgba[1] = i_g;
589             p_rgba[2] = i_b;
590             p_rgba[3] = i_a;
591         }
592     }
593 }
594
595 static inline void BlendRGBAPixel( picture_t *p_picture,
596                                    int i_picture_x, int i_picture_y,
597                                    int i_a, int i_r, int i_g, int i_b,
598                                    int i_alpha )
599 {
600     int i_an = i_a * i_alpha / 255;
601
602     uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
603
604     int i_ao = p_rgba[3];
605     if( i_ao == 0 )
606     {
607         p_rgba[0] = i_r;
608         p_rgba[1] = i_g;
609         p_rgba[2] = i_b;
610         p_rgba[3] = i_an;
611     }
612     else
613     {
614         p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
615         if( p_rgba[3] != 0 )
616         {
617             p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
618             p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
619             p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
620         }
621     }
622 }
623
624 static void FillARGBPicture(picture_t *pic, int a, int r, int g, int b)
625 {
626     if (a == 0)
627         r = g = b = 0;
628     if (a == r && a == b && a == g)
629     {   /* fast path */
630         memset(pic->p->p_pixels, a, pic->p->i_visible_lines * pic->p->i_pitch);
631         return;
632     }
633
634     uint_fast32_t pixel = VLC_FOURCC(a, r, g, b);
635     uint8_t *line = pic->p->p_pixels;
636
637     for (unsigned lines = pic->p->i_visible_lines; lines > 0; lines--)
638     {
639         uint32_t *pixels = (uint32_t *)line;
640         for (unsigned cols = pic->p->i_visible_pitch; cols > 0; cols -= 4)
641             *(pixels++) = pixel;
642         line += pic->p->i_pitch;
643     }
644 }
645
646 static inline void BlendARGBPixel(picture_t *pic, int pic_x, int pic_y,
647                                   int a, int r, int g, int b, int alpha)
648 {
649     uint8_t *rgba = &pic->p->p_pixels[pic_y * pic->p->i_pitch + 4 * pic_x];
650     int an = a * alpha / 255;
651     int ao = rgba[3];
652
653     if (ao == 0)
654     {
655         rgba[0] = an;
656         rgba[1] = r;
657         rgba[2] = g;
658         rgba[3] = b;
659     }
660     else
661     {
662         rgba[0] = 255 - (255 - rgba[0]) * (255 - an) / 255;
663         if (rgba[0] != 0)
664         {
665             rgba[1] = (rgba[1] * ao * (255 - an) / 255 + r * an ) / rgba[0];
666             rgba[2] = (rgba[2] * ao * (255 - an) / 255 + g * an ) / rgba[0];
667             rgba[3] = (rgba[3] * ao * (255 - an) / 255 + b * an ) / rgba[0];
668         }
669     }
670 }
671
672 static inline void BlendAXYZGlyph( picture_t *p_picture,
673                                    int i_picture_x, int i_picture_y,
674                                    int i_a, int i_x, int i_y, int i_z,
675                                    FT_BitmapGlyph p_glyph,
676                                    void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
677
678 {
679     for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
680     {
681         for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
682             BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
683                         i_a, i_x, i_y, i_z,
684                         p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
685     }
686 }
687
688 static inline void BlendAXYZLine( picture_t *p_picture,
689                                   int i_picture_x, int i_picture_y,
690                                   int i_a, int i_x, int i_y, int i_z,
691                                   const line_character_t *p_current,
692                                   const line_character_t *p_next,
693                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
694 {
695     int i_line_width = p_current->p_glyph->bitmap.width;
696     if( p_next )
697         i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
698
699     for( int dx = 0; dx < i_line_width; dx++ )
700     {
701         for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
702             BlendPixel( p_picture,
703                         i_picture_x + dx,
704                         i_picture_y + p_current->i_line_offset + dy,
705                         i_a, i_x, i_y, i_z, 0xff );
706     }
707 }
708
709 static inline void RenderBackground( subpicture_region_t *p_region,
710                                      line_desc_t *p_line_head,
711                                      FT_BBox *p_bbox,
712                                      int i_margin,
713                                      picture_t *p_picture,
714                                      int i_text_width,
715                                      void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
716                                      void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
717 {
718     for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
719     {
720         int i_align_left = i_margin;
721         int i_align_top = i_margin;
722         int line_start = 0;
723         int line_end = 0;
724         unsigned line_top = 0;
725         int line_bottom = 0;
726         int max_height = 0;
727
728         if( p_line->i_width < i_text_width )
729         {
730             /* Left offset to take into account alignment */
731             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
732                 i_align_left += ( i_text_width - p_line->i_width );
733             else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
734                 i_align_left = i_margin; /* Keep it the way it is */
735             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
736                 i_align_left += ( i_text_width - p_line->i_width ) / 2;
737         }
738
739         /* Find the tallest character in the line */
740         for( int i = 0; i < p_line->i_character_count; i++ ) {
741             const line_character_t *ch = &p_line->p_character[i];
742             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
743             if (p_glyph->top > max_height)
744                 max_height = p_glyph->top;
745         }
746
747         /* Compute the background for the line (identify leading/trailing space) */
748         for( int i = 0; i < p_line->i_character_count; i++ ) {
749             const line_character_t *ch = &p_line->p_character[i];
750             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
751             if (p_glyph && p_glyph->bitmap.rows > 0) {
752                 // Found a non-whitespace character
753                 line_start = i_align_left + p_glyph->left - p_bbox->xMin;
754                 break;
755             }
756         }
757
758         /* Fudge factor to make sure caption background edges are left aligned
759            despite variable font width */
760         if (line_start < 12)
761             line_start = 0;
762
763         /* Find right boundary for bounding box for background */
764         for( int i = p_line->i_character_count; i > 0; i-- ) {
765             const line_character_t *ch = &p_line->p_character[i - 1];
766             FT_BitmapGlyph p_glyph = ch->p_shadow ? ch->p_shadow : ch->p_glyph;
767             if (p_glyph && p_glyph->bitmap.rows > 0) {
768                 // Found a non-whitespace character
769                 line_end = i_align_left + p_glyph->left - p_bbox->xMin + p_glyph->bitmap.width;
770                 break;
771             }
772         }
773
774         /* Setup color for the background */
775         uint8_t i_x, i_y, i_z;
776         ExtractComponents( 0x000000, &i_x, &i_y, &i_z );
777
778         /* Compute the upper boundary for the background */
779         if ((i_align_top + p_line->i_base_line - max_height) < 0)
780             line_top = i_align_top + p_line->i_base_line;
781         else
782             line_top = i_align_top + p_line->i_base_line - max_height;
783
784         /* Compute lower boundary for the background */
785         line_bottom =  __MIN(line_top + p_line->i_height, p_region->fmt.i_visible_height);
786
787         /* Render the actual background */
788         for( int dy = line_top; dy < line_bottom; dy++ )
789         {
790             for( int dx = line_start; dx < line_end; dx++ )
791                 BlendPixel( p_picture, dx, dy, 0xff, i_x, i_y, i_z, 0xff );
792         }
793     }
794 }
795
796 static inline int RenderAXYZ( filter_t *p_filter,
797                               subpicture_region_t *p_region,
798                               line_desc_t *p_line_head,
799                               FT_BBox *p_bbox,
800                               int i_margin,
801                               vlc_fourcc_t i_chroma,
802                               void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
803                               void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
804                               void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
805 {
806     filter_sys_t *p_sys = p_filter->p_sys;
807
808     /* Create a new subpicture region */
809     const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
810     const int i_text_height = p_bbox->yMax - p_bbox->yMin;
811     video_format_t fmt;
812     video_format_Init( &fmt, i_chroma );
813     fmt.i_width          =
814     fmt.i_visible_width  = i_text_width  + 2 * i_margin;
815     fmt.i_height         =
816     fmt.i_visible_height = i_text_height + 2 * i_margin;
817
818     picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
819     if( !p_region->p_picture )
820         return VLC_EGENERIC;
821     p_region->fmt = fmt;
822
823     /* Initialize the picture background */
824     uint8_t i_a = var_InheritInteger( p_filter, "freetype-background-opacity" );
825     i_a = VLC_CLIP( i_a, 0, 255 );
826     uint8_t i_x, i_y, i_z;
827
828     if (p_region->b_renderbg) {
829         /* Render the background just under the text */
830         FillPicture( p_picture, 0x00, 0x00, 0x00, 0x00 );
831         RenderBackground(p_region, p_line_head, p_bbox, i_margin, p_picture, i_text_width,
832                          ExtractComponents, BlendPixel);
833     } else {
834         /* Render background under entire subpicture block */
835         int i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
836         i_background_color = VLC_CLIP( i_background_color, 0, 0xFFFFFF );
837         ExtractComponents( i_background_color, &i_x, &i_y, &i_z );
838         FillPicture( p_picture, i_a, i_x, i_y, i_z );
839     }
840
841     /* Render shadow then outline and then normal glyphs */
842     for( int g = 0; g < 3; g++ )
843     {
844         /* Render all lines */
845         for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
846         {
847             int i_align_left = i_margin;
848             if( p_line->i_width < i_text_width )
849             {
850                 /* Left offset to take into account alignment */
851                 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
852                     i_align_left += ( i_text_width - p_line->i_width );
853                 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
854                     i_align_left = i_margin; /* Keep it the way it is */
855                 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
856                     i_align_left += ( i_text_width - p_line->i_width ) / 2;
857             }
858             int i_align_top = i_margin;
859
860             /* Render all glyphs and underline/strikethrough */
861             for( int i = 0; i < p_line->i_character_count; i++ )
862             {
863                 const line_character_t *ch = &p_line->p_character[i];
864                 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
865                 if( !p_glyph )
866                     continue;
867
868                 i_a = (ch->i_color >> 24) & 0xff;
869                 uint32_t i_color;
870                 switch (g) {
871                 case 0:
872                     i_a     = i_a * p_sys->style.i_shadow_alpha / 255;
873                     i_color = p_sys->style.i_shadow_color;
874                     break;
875                 case 1:
876                     i_a     = i_a * p_sys->style.i_outline_alpha / 255;
877                     i_color = p_sys->style.i_outline_color;
878                     break;
879                 default:
880                     i_color = ch->i_color;
881                     break;
882                 }
883                 ExtractComponents( i_color, &i_x, &i_y, &i_z );
884
885                 int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
886                 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
887
888                 BlendAXYZGlyph( p_picture,
889                                 i_glyph_x, i_glyph_y,
890                                 i_a, i_x, i_y, i_z,
891                                 p_glyph,
892                                 BlendPixel );
893
894                 /* underline/strikethrough are only rendered for the normal glyph */
895                 if( g == 2 && ch->i_line_thickness > 0 )
896                     BlendAXYZLine( p_picture,
897                                    i_glyph_x, i_glyph_y + p_glyph->top,
898                                    i_a, i_x, i_y, i_z,
899                                    &ch[0],
900                                    i + 1 < p_line->i_character_count ? &ch[1] : NULL,
901                                    BlendPixel );
902             }
903         }
904     }
905
906     return VLC_SUCCESS;
907 }
908
909
910
911 static void FreeLine( line_desc_t *p_line )
912 {
913     for( int i = 0; i < p_line->i_character_count; i++ )
914     {
915         line_character_t *ch = &p_line->p_character[i];
916         FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
917         if( ch->p_outline )
918             FT_Done_Glyph( (FT_Glyph)ch->p_outline );
919         if( ch->p_shadow )
920             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
921     }
922
923     free( p_line->p_character );
924     free( p_line );
925 }
926
927 static void FreeLines( line_desc_t *p_lines )
928 {
929     for( line_desc_t *p_line = p_lines; p_line != NULL; )
930     {
931         line_desc_t *p_next = p_line->p_next;
932         FreeLine( p_line );
933         p_line = p_next;
934     }
935 }
936
937 static line_desc_t *NewLine( int i_count )
938 {
939     line_desc_t *p_line = malloc( sizeof(*p_line) );
940
941     if( !p_line )
942         return NULL;
943
944     p_line->p_next = NULL;
945     p_line->i_width = 0;
946     p_line->i_base_line = 0;
947     p_line->i_character_count = 0;
948
949     p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
950     if( !p_line->p_character )
951     {
952         free( p_line );
953         return NULL;
954     }
955     return p_line;
956 }
957
958 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
959 {
960     for( int k = 0; k < p_sys->i_font_attachments; k++ )
961     {
962         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
963         int                 i_font_idx = 0;
964         FT_Face             p_face = NULL;
965
966         while( 0 == FT_New_Memory_Face( p_sys->p_library,
967                                         p_attach->p_data,
968                                         p_attach->i_data,
969                                         i_font_idx,
970                                         &p_face ))
971         {
972             if( p_face )
973             {
974                 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD)    ? STYLE_BOLD   : 0) |
975                                        ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
976                 if( p_face->family_name != NULL
977                  && !strcasecmp( p_face->family_name, p_style->psz_fontname )
978                  && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
979                                                           == i_style_received )
980                     return p_face;
981
982                 FT_Done_Face( p_face );
983             }
984             i_font_idx++;
985         }
986     }
987     return NULL;
988 }
989
990 static FT_Face LoadFace( filter_t *p_filter,
991                          const text_style_t *p_style )
992 {
993     filter_sys_t *p_sys = p_filter->p_sys;
994
995     /* Look for a match amongst our attachments first */
996     FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
997
998     /* Load system wide font otheriwse */
999     if( !p_face )
1000     {
1001         int  i_idx = 0;
1002         char *psz_fontfile = NULL;
1003         if( p_sys->pf_select )
1004             psz_fontfile = p_sys->pf_select( p_filter,
1005                                              p_style->psz_fontname,
1006                                              (p_style->i_style_flags & STYLE_BOLD) != 0,
1007                                              (p_style->i_style_flags & STYLE_ITALIC) != 0,
1008                                              -1,
1009                                              &i_idx );
1010         else
1011             psz_fontfile = NULL;
1012
1013         if( !psz_fontfile )
1014             return NULL;
1015
1016         if( *psz_fontfile == '\0' )
1017         {
1018             msg_Warn( p_filter,
1019                       "We were not able to find a matching font: \"%s\" (%s %s),"
1020                       " so using default font",
1021                       p_style->psz_fontname,
1022                       (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
1023                       (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1024             p_face = NULL;
1025         }
1026         else
1027         {
1028             if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1029                 p_face = NULL;
1030         }
1031         free( psz_fontfile );
1032     }
1033     if( !p_face )
1034         return NULL;
1035
1036     if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1037     {
1038         /* We've loaded a font face which is unhelpful for actually
1039          * rendering text - fallback to the default one.
1040          */
1041         FT_Done_Face( p_face );
1042         return NULL;
1043     }
1044     return p_face;
1045 }
1046
1047 static int GetGlyph( filter_t *p_filter,
1048                      FT_Glyph *pp_glyph,   FT_BBox *p_glyph_bbox,
1049                      FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1050                      FT_Glyph *pp_shadow,  FT_BBox *p_shadow_bbox,
1051
1052                      FT_Face  p_face,
1053                      int i_glyph_index,
1054                      int i_style_flags,
1055                      FT_Vector *p_pen,
1056                      FT_Vector *p_pen_shadow )
1057 {
1058     if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1059         FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1060     {
1061         msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1062         return VLC_EGENERIC;
1063     }
1064
1065     /* Do synthetic styling now that Freetype supports it;
1066      * ie. if the font we have loaded is NOT already in the
1067      * style that the tags want, then switch it on; if they
1068      * are then don't. */
1069     if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1070         FT_GlyphSlot_Embolden( p_face->glyph );
1071     if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1072         FT_GlyphSlot_Oblique( p_face->glyph );
1073
1074     FT_Glyph glyph;
1075     if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1076     {
1077         msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1078         return VLC_EGENERIC;
1079     }
1080
1081     FT_Glyph outline = NULL;
1082     if( p_filter->p_sys->p_stroker )
1083     {
1084         outline = glyph;
1085         if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1086             outline = NULL;
1087     }
1088
1089     FT_Glyph shadow = NULL;
1090     if( p_filter->p_sys->style.i_shadow_alpha > 0 )
1091     {
1092         shadow = outline ? outline : glyph;
1093         if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0  ) )
1094         {
1095             shadow = NULL;
1096         }
1097         else
1098         {
1099             FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1100         }
1101     }
1102     *pp_shadow = shadow;
1103
1104     if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1105     {
1106         FT_Done_Glyph( glyph );
1107         if( outline )
1108             FT_Done_Glyph( outline );
1109         if( shadow )
1110             FT_Done_Glyph( shadow );
1111         return VLC_EGENERIC;
1112     }
1113     FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1114     *pp_glyph = glyph;
1115
1116     if( outline )
1117     {
1118         FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1119         FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1120     }
1121     *pp_outline = outline;
1122
1123     return VLC_SUCCESS;
1124 }
1125
1126 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1127 {
1128     FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1129     if( p_bbox->xMin >= p_bbox->xMax )
1130     {
1131         p_bbox->xMin = FT_CEIL(p_pen->x);
1132         p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1133         glyph_bmp->left = p_bbox->xMin;
1134     }
1135     if( p_bbox->yMin >= p_bbox->yMax )
1136     {
1137         p_bbox->yMax = FT_CEIL(p_pen->y);
1138         p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1139         glyph_bmp->top  = p_bbox->yMax;
1140     }
1141 }
1142
1143 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1144 {
1145     p_max->xMin = __MIN(p_max->xMin, p->xMin);
1146     p_max->yMin = __MIN(p_max->yMin, p->yMin);
1147     p_max->xMax = __MAX(p_max->xMax, p->xMax);
1148     p_max->yMax = __MAX(p_max->yMax, p->yMax);
1149 }
1150
1151 static int ProcessLines( filter_t *p_filter,
1152                          line_desc_t **pp_lines,
1153                          FT_BBox     *p_bbox,
1154                          int         *pi_max_face_height,
1155
1156                          uni_char_t *psz_text,
1157                          text_style_t **pp_styles,
1158                          uint32_t *pi_k_dates,
1159                          int i_len )
1160 {
1161     filter_sys_t   *p_sys = p_filter->p_sys;
1162     uni_char_t     *p_fribidi_string = NULL;
1163     text_style_t   **pp_fribidi_styles = NULL;
1164     int            *p_new_positions = NULL;
1165
1166 #if defined(HAVE_FRIBIDI)
1167     {
1168         int    *p_old_positions;
1169         int start_pos, pos = 0;
1170
1171         pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1172
1173         p_fribidi_string  = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1174         p_old_positions   = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1175         p_new_positions   = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1176
1177         if( ! pp_fribidi_styles ||
1178             ! p_fribidi_string ||
1179             ! p_old_positions ||
1180             ! p_new_positions )
1181         {
1182             free( p_old_positions );
1183             free( p_new_positions );
1184             free( p_fribidi_string );
1185             free( pp_fribidi_styles );
1186             return VLC_ENOMEM;
1187         }
1188
1189         /* Do bidi conversion line-by-line */
1190         while(pos < i_len)
1191         {
1192             while(pos < i_len) {
1193                 if (psz_text[pos] != '\n')
1194                     break;
1195                 p_fribidi_string[pos] = psz_text[pos];
1196                 pp_fribidi_styles[pos] = pp_styles[pos];
1197                 p_new_positions[pos] = pos;
1198                 ++pos;
1199             }
1200             start_pos = pos;
1201             while(pos < i_len) {
1202                 if (psz_text[pos] == '\n')
1203                     break;
1204                 ++pos;
1205             }
1206             if (pos > start_pos)
1207             {
1208 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1209                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1210 #else
1211                 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1212 #endif
1213                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1214                         pos - start_pos, &base_dir,
1215                         (FriBidiChar*)p_fribidi_string + start_pos,
1216                         p_new_positions + start_pos,
1217                         p_old_positions,
1218                         NULL );
1219                 for( int j = start_pos; j < pos; j++ )
1220                 {
1221                     pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1222                     p_new_positions[ j ] += start_pos;
1223                 }
1224             }
1225         }
1226         p_fribidi_string[ i_len ] = 0;
1227         free( p_old_positions );
1228
1229         pp_styles = pp_fribidi_styles;
1230         psz_text = p_fribidi_string;
1231     }
1232 #endif
1233     /* Work out the karaoke */
1234     uint8_t *pi_karaoke_bar = NULL;
1235     if( pi_k_dates )
1236     {
1237         pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1238         if( pi_karaoke_bar )
1239         {
1240             int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1241             for( int i = 0; i < i_len; i++ )
1242             {
1243                 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1244                 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1245             }
1246         }
1247     }
1248     free( p_new_positions );
1249
1250     *pi_max_face_height = 0;
1251     *pp_lines = NULL;
1252     line_desc_t **pp_line_next = pp_lines;
1253
1254     FT_BBox bbox = {
1255         .xMin = INT_MAX,
1256         .yMin = INT_MAX,
1257         .xMax = INT_MIN,
1258         .yMax = INT_MIN,
1259     };
1260     int i_face_height_previous = 0;
1261     int i_base_line = 0;
1262     const text_style_t *p_previous_style = NULL;
1263     FT_Face p_face = NULL;
1264     for( int i_start = 0; i_start < i_len; )
1265     {
1266         /* Compute the length of the current text line */
1267         int i_length = 0;
1268         while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1269             i_length++;
1270
1271         /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1272         line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1273         int i_index = i_start;
1274         FT_Vector pen = {
1275             .x = 0,
1276             .y = 0,
1277         };
1278         int i_face_height = 0;
1279         FT_BBox line_bbox = {
1280             .xMin = INT_MAX,
1281             .yMin = INT_MAX,
1282             .xMax = INT_MIN,
1283             .yMax = INT_MIN,
1284         };
1285         int i_ul_offset = 0;
1286         int i_ul_thickness = 0;
1287         typedef struct {
1288             int       i_index;
1289             FT_Vector pen;
1290             FT_BBox   line_bbox;
1291             int i_face_height;
1292             int i_ul_offset;
1293             int i_ul_thickness;
1294         } break_point_t;
1295         break_point_t break_point;
1296         break_point_t break_point_fallback;
1297
1298 #define SAVE_BP(dst) do { \
1299         dst.i_index = i_index; \
1300         dst.pen = pen; \
1301         dst.line_bbox = line_bbox; \
1302         dst.i_face_height = i_face_height; \
1303         dst.i_ul_offset = i_ul_offset; \
1304         dst.i_ul_thickness = i_ul_thickness; \
1305     } while(0)
1306
1307         SAVE_BP( break_point );
1308         SAVE_BP( break_point_fallback );
1309
1310         while( i_index < i_start + i_length )
1311         {
1312             /* Split by common FT_Face + Size */
1313             const text_style_t *p_current_style = pp_styles[i_index];
1314             int i_part_length = 0;
1315             while( i_index + i_part_length < i_start + i_length )
1316             {
1317                 const text_style_t *p_style = pp_styles[i_index + i_part_length];
1318                 if( !FaceStyleEquals( p_style, p_current_style ) ||
1319                     p_style->i_font_size != p_current_style->i_font_size )
1320                     break;
1321                 i_part_length++;
1322             }
1323
1324             /* (Re)load/reconfigure the face if needed */
1325             if( !FaceStyleEquals( p_current_style, p_previous_style ) )
1326             {
1327                 if( p_face )
1328                     FT_Done_Face( p_face );
1329                 p_previous_style = NULL;
1330
1331                 p_face = LoadFace( p_filter, p_current_style );
1332             }
1333             FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
1334             if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size ||
1335                 ((p_previous_style->i_style_flags ^ p_current_style->i_style_flags) & STYLE_HALFWIDTH) )
1336
1337             {
1338                 int i_font_width = ( p_current_style->i_style_flags & STYLE_HALFWIDTH )
1339                                     ? p_current_style->i_font_size / 2
1340                                     : p_current_style->i_font_size;
1341                 if( FT_Set_Pixel_Sizes( p_current_face,
1342                                         i_font_width,
1343                                         p_current_style->i_font_size ) )
1344                     msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
1345                 if( p_sys->p_stroker )
1346                 {
1347                     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
1348                     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
1349                     int i_radius = (p_current_style->i_font_size << 6) * f_outline_thickness;
1350                     FT_Stroker_Set( p_sys->p_stroker,
1351                                     i_radius,
1352                                     FT_STROKER_LINECAP_ROUND,
1353                                     FT_STROKER_LINEJOIN_ROUND, 0 );
1354                 }
1355             }
1356             p_previous_style = p_current_style;
1357
1358             i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
1359                                                                    p_current_face->size->metrics.y_scale)));
1360
1361             /* Render the part */
1362             bool b_break_line = false;
1363             int i_glyph_last = 0;
1364             FT_Vector advance = {
1365                 .x = 0,
1366                 .y = 0,
1367             };
1368             while( i_part_length > 0 )
1369             {
1370                 const text_style_t *p_glyph_style = pp_styles[i_index];
1371                 uni_char_t character = psz_text[i_index];
1372                 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
1373
1374                 /* If the missing glyph is U+FEFF (ZERO WIDTH NO-BREAK SPACE) */
1375                 /* we can safely ignore it. Otherwise extra squares show up   */
1376                 /* in Arabic text.                                            */
1377                 if( i_glyph_index == 0 && character == 0xFEFF )
1378                     goto next;
1379
1380 /* These are the most common Arabic diacritics */
1381 #define DIACRITIC( a ) ( a >= 0x064B && a <= 0x0653 )
1382
1383                 /* Diacritics should be rendered over the preceding base glyph */
1384                 if( DIACRITIC( character ) )
1385                 {
1386                     pen.x -= advance.x;
1387                     pen.y -= advance.y;
1388                 }
1389
1390                 /* Get kerning vector */
1391                 FT_Vector kerning = { .x = 0, .y = 0 };
1392                 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
1393                 {
1394                     FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
1395                 }
1396                 if( p_glyph_style->i_spacing > 0 && i_glyph_last != 0 && i_glyph_index != 0 )
1397                 {
1398                     kerning.x = (p_glyph_style->i_spacing) << 6;
1399                 }
1400
1401                 /* Get the glyph bitmap and its bounding box and all the associated properties */
1402                 FT_Vector pen_new = {
1403                     .x = pen.x + kerning.x,
1404                     .y = pen.y + kerning.y,
1405                 };
1406
1407                 int i_font_width = ( p_current_style->i_style_flags & STYLE_HALFWIDTH )
1408                                     ? p_current_style->i_font_size / 2
1409                                     : p_current_style->i_font_size;
1410                 FT_Vector pen_shadow_new = {
1411                     .x = pen_new.x + p_sys->f_shadow_vector_x * (i_font_width << 6),
1412                     .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
1413                 };
1414
1415                 FT_Glyph glyph;
1416                 FT_BBox  glyph_bbox;
1417                 FT_Glyph outline;
1418                 FT_BBox  outline_bbox;
1419                 FT_Glyph shadow;
1420                 FT_BBox  shadow_bbox;
1421
1422                 if( GetGlyph( p_filter,
1423                               &glyph, &glyph_bbox,
1424                               &outline, &outline_bbox,
1425                               &shadow, &shadow_bbox,
1426                               p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
1427                               &pen_new, &pen_shadow_new ) )
1428                     goto next;
1429
1430                 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
1431                 if( outline )
1432                     FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
1433                 if( shadow )
1434                     FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
1435
1436                 /* FIXME and what about outline */
1437
1438                 bool     b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
1439                 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
1440                                                 (p_glyph_style->i_karaoke_background_alpha << 24))
1441                                              : (p_glyph_style->i_font_color |
1442                                                 (p_glyph_style->i_font_alpha << 24));
1443                 int i_line_offset    = 0;
1444                 int i_line_thickness = 0;
1445                 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
1446                 {
1447                     i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
1448                                                             p_current_face->size->metrics.y_scale)) );
1449
1450                     i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
1451                                                               p_current_face->size->metrics.y_scale)) );
1452
1453                     if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
1454                     {
1455                         /* Move the baseline to make it strikethrough instead of
1456                          * underline. That means that strikethrough takes precedence
1457                          */
1458                         i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
1459                                                                  p_current_face->size->metrics.y_scale)) );
1460                     }
1461                     else if( i_line_thickness > 0 )
1462                     {
1463                         glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
1464
1465                         /* The real underline thickness and position are
1466                          * updated once the whole line has been parsed */
1467                         i_ul_offset = __MAX( i_ul_offset, i_line_offset );
1468                         i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
1469                         i_line_thickness = -1;
1470                     }
1471                 }
1472                 FT_BBox line_bbox_new = line_bbox;
1473                 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
1474                 if( outline )
1475                     BBoxEnlarge( &line_bbox_new, &outline_bbox );
1476                 if( shadow )
1477                     BBoxEnlarge( &line_bbox_new, &shadow_bbox );
1478
1479                 b_break_line = i_index > i_start &&
1480                                line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
1481                 if( b_break_line )
1482                 {
1483                     FT_Done_Glyph( glyph );
1484                     if( outline )
1485                         FT_Done_Glyph( outline );
1486                     if( shadow )
1487                         FT_Done_Glyph( shadow );
1488
1489                     break_point_t *p_bp = NULL;
1490                     if( break_point.i_index > i_start )
1491                         p_bp = &break_point;
1492                     else if( break_point_fallback.i_index > i_start )
1493                         p_bp = &break_point_fallback;
1494
1495                     if( p_bp )
1496                     {
1497                         msg_Dbg( p_filter, "Breaking line");
1498                         for( int i = p_bp->i_index; i < i_index; i++ )
1499                         {
1500                             line_character_t *ch = &p_line->p_character[i - i_start];
1501                             FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1502                             if( ch->p_outline )
1503                                 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1504                             if( ch->p_shadow )
1505                                 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1506                         }
1507                         p_line->i_character_count = p_bp->i_index - i_start;
1508
1509                         i_index = p_bp->i_index;
1510                         pen = p_bp->pen;
1511                         line_bbox = p_bp->line_bbox;
1512                         i_face_height = p_bp->i_face_height;
1513                         i_ul_offset = p_bp->i_ul_offset;
1514                         i_ul_thickness = p_bp->i_ul_thickness;
1515                     }
1516                     else
1517                     {
1518                         msg_Err( p_filter, "Breaking unbreakable line");
1519                     }
1520                     break;
1521                 }
1522
1523                 p_line->p_character[p_line->i_character_count++] = (line_character_t){
1524                     .p_glyph = (FT_BitmapGlyph)glyph,
1525                     .p_outline = (FT_BitmapGlyph)outline,
1526                     .p_shadow = (FT_BitmapGlyph)shadow,
1527                     .i_color = i_color,
1528                     .i_line_offset = i_line_offset,
1529                     .i_line_thickness = i_line_thickness,
1530                 };
1531
1532                 /* Diacritics do not determine advance values. We use        */
1533                 /* the advance values from the last encountered base glyph,  */
1534                 /* since multiple diacritics may follow a single base glyph. */
1535                 if( !DIACRITIC( character ) )
1536                 {
1537                     advance.x = p_current_face->glyph->advance.x;
1538                     advance.y = p_current_face->glyph->advance.y;
1539                 }
1540
1541                 pen.x = pen_new.x + advance.x;
1542                 pen.y = pen_new.y + advance.y;
1543                 line_bbox = line_bbox_new;
1544             next:
1545                 i_glyph_last = i_glyph_index;
1546                 i_part_length--;
1547                 i_index++;
1548
1549                 if( character == ' ' || character == '\t' )
1550                     SAVE_BP( break_point );
1551                 else if( character == 160 )
1552                     SAVE_BP( break_point_fallback );
1553             }
1554             if( b_break_line )
1555                 break;
1556         }
1557 #undef SAVE_BP
1558         /* Update our baseline */
1559         if( i_face_height_previous > 0 )
1560             i_base_line += __MAX(i_face_height, i_face_height_previous);
1561         if( i_face_height > 0 )
1562             i_face_height_previous = i_face_height;
1563
1564         /* Update the line bbox with the actual base line */
1565         if (line_bbox.yMax > line_bbox.yMin) {
1566             line_bbox.yMin -= i_base_line;
1567             line_bbox.yMax -= i_base_line;
1568         }
1569         BBoxEnlarge( &bbox, &line_bbox );
1570
1571         /* Terminate and append the line */
1572         if( p_line )
1573         {
1574             p_line->i_width  = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
1575             p_line->i_base_line = i_base_line;
1576             p_line->i_height = __MAX(i_face_height, i_face_height_previous);
1577             if( i_ul_thickness > 0 )
1578             {
1579                 for( int i = 0; i < p_line->i_character_count; i++ )
1580                 {
1581                     line_character_t *ch = &p_line->p_character[i];
1582                     if( ch->i_line_thickness < 0 )
1583                     {
1584                         ch->i_line_offset    = i_ul_offset;
1585                         ch->i_line_thickness = i_ul_thickness;
1586                     }
1587                 }
1588             }
1589
1590             *pp_line_next = p_line;
1591             pp_line_next = &p_line->p_next;
1592         }
1593
1594         *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
1595
1596         /* Skip what we have rendered and the line delimitor if present */
1597         i_start = i_index;
1598         if( i_start < i_len && psz_text[i_start] == '\n' )
1599             i_start++;
1600
1601         if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
1602         {
1603             msg_Err( p_filter, "Truncated too high subtitle" );
1604             break;
1605         }
1606     }
1607     if( p_face )
1608         FT_Done_Face( p_face );
1609
1610     free( pp_fribidi_styles );
1611     free( p_fribidi_string );
1612     free( pi_karaoke_bar );
1613
1614     *p_bbox = bbox;
1615     return VLC_SUCCESS;
1616 }
1617
1618 static xml_reader_t *GetXMLReader( filter_t *p_filter, stream_t *p_sub )
1619 {
1620     xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
1621     if( !p_xml_reader )
1622         p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
1623     else
1624         p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
1625     p_filter->p_sys->p_xml = p_xml_reader;
1626
1627     return p_xml_reader;
1628 }
1629
1630 /**
1631  * This function renders a text subpicture region into another one.
1632  * It also calculates the size needed for this string, and renders the
1633  * needed glyphs into memory. It is used as pf_add_string callback in
1634  * the vout method by this module
1635  */
1636 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
1637                          subpicture_region_t *p_region_in, bool b_html,
1638                          const vlc_fourcc_t *p_chroma_list )
1639 {
1640     filter_sys_t *p_sys = p_filter->p_sys;
1641
1642     if( !p_region_in )
1643         return VLC_EGENERIC;
1644     if( b_html && !p_region_in->psz_html )
1645         return VLC_EGENERIC;
1646     if( !b_html && !p_region_in->psz_text )
1647         return VLC_EGENERIC;
1648
1649     const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
1650                                              : p_region_in->psz_text );
1651
1652     uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
1653     text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
1654     if( !psz_text || !pp_styles )
1655     {
1656         free( psz_text );
1657         free( pp_styles );
1658         return VLC_EGENERIC;
1659     }
1660
1661     /* Reset the default fontsize in case screen metrics have changed */
1662     p_filter->p_sys->style.i_font_size = GetFontSize( p_filter );
1663
1664     /* */
1665     int rv = VLC_SUCCESS;
1666     int i_text_length = 0;
1667     FT_BBox bbox;
1668     int i_max_face_height;
1669     line_desc_t *p_lines = NULL;
1670
1671     uint32_t *pi_k_durations   = NULL;
1672
1673     if( b_html )
1674     {
1675         stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1676                                             (uint8_t *) p_region_in->psz_html,
1677                                             strlen( p_region_in->psz_html ),
1678                                             true );
1679         if( unlikely(p_sub == NULL) )
1680         {
1681             free( psz_text );
1682             free( pp_styles );
1683             return VLC_SUCCESS;
1684         }
1685
1686         xml_reader_t *p_xml_reader = GetXMLReader( p_filter, p_sub );
1687
1688         if( !p_xml_reader )
1689             rv = VLC_EGENERIC;
1690
1691         if( !rv )
1692         {
1693             /* Look for Root Node */
1694             const char *node;
1695
1696             if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
1697             {
1698                 if( strcasecmp( "karaoke", node ) == 0 )
1699                 {
1700                     pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
1701                 }
1702                 else if( strcasecmp( "text", node ) != 0 )
1703                 {
1704                     /* Only text and karaoke tags are supported */
1705                     msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
1706                              node );
1707                     rv = VLC_EGENERIC;
1708                 }
1709             }
1710             else
1711             {
1712                 msg_Err( p_filter, "Malformed HTML subtitle" );
1713                 rv = VLC_EGENERIC;
1714             }
1715         }
1716         if( !rv )
1717         {
1718             rv = ProcessNodes( p_filter,
1719                                psz_text, pp_styles, pi_k_durations, &i_text_length,
1720                                p_xml_reader, p_region_in->p_style, &p_filter->p_sys->style );
1721         }
1722
1723         if( p_xml_reader )
1724             p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
1725
1726         stream_Delete( p_sub );
1727     }
1728     else
1729     {
1730         text_style_t *p_style;
1731         if( p_region_in->p_style )
1732         {
1733             p_style = CreateStyle( p_region_in->p_style->psz_fontname ? p_region_in->p_style->psz_fontname
1734                                                                       : p_sys->style.psz_fontname,
1735                                    p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
1736                                                                          : p_sys->style.i_font_size,
1737                                    (p_region_in->p_style->i_font_color & 0xffffff) |
1738                                    ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
1739                                    0x00ffffff,
1740                                    p_region_in->p_style->i_style_flags & (STYLE_BOLD |
1741                                                                           STYLE_ITALIC |
1742                                                                           STYLE_UNDERLINE |
1743                                                                           STYLE_STRIKEOUT |
1744                                                                           STYLE_HALFWIDTH) );
1745             p_style->i_spacing = p_region_in->p_style->i_spacing;
1746         }
1747         else
1748         {
1749             uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
1750             i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
1751             p_style = CreateStyle( p_sys->style.psz_fontname,
1752                                    p_sys->style.i_font_size,
1753                                    (i_font_color & 0xffffff) |
1754                                    ((p_sys->style.i_font_alpha & 0xff) << 24),
1755                                    0x00ffffff, 0);
1756         }
1757         if( p_sys->style.i_style_flags & STYLE_BOLD )
1758             p_style->i_style_flags |= STYLE_BOLD;
1759
1760         i_text_length = SetupText( p_filter,
1761                                    psz_text,
1762                                    pp_styles,
1763                                    NULL,
1764                                    p_region_in->psz_text, p_style, 0 );
1765     }
1766
1767     if( !rv && i_text_length > 0 )
1768     {
1769         rv = ProcessLines( p_filter,
1770                            &p_lines, &bbox, &i_max_face_height,
1771                            psz_text, pp_styles, pi_k_durations, i_text_length );
1772     }
1773
1774     p_region_out->i_x = p_region_in->i_x;
1775     p_region_out->i_y = p_region_in->i_y;
1776
1777     /* Don't attempt to render text that couldn't be layed out
1778      * properly. */
1779     if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
1780     {
1781         const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
1782         const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
1783
1784         if( var_InheritBool( p_filter, "freetype-yuvp" ) )
1785             p_chroma_list = p_chroma_list_yuvp;
1786         else if( !p_chroma_list || *p_chroma_list == 0 )
1787             p_chroma_list = p_chroma_list_rgba;
1788
1789         uint8_t i_background_opacity = var_InheritInteger( p_filter, "freetype-background-opacity" );
1790         i_background_opacity = VLC_CLIP( i_background_opacity, 0, 255 );
1791         const int i_margin = i_background_opacity > 0 ? i_max_face_height / 4 : 0;
1792         for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
1793         {
1794             rv = VLC_EGENERIC;
1795             if( *p_chroma == VLC_CODEC_YUVP )
1796                 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
1797             else if( *p_chroma == VLC_CODEC_YUVA )
1798                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
1799                                  VLC_CODEC_YUVA,
1800                                  YUVFromRGB,
1801                                  FillYUVAPicture,
1802                                  BlendYUVAPixel );
1803             else if( *p_chroma == VLC_CODEC_RGBA )
1804                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
1805                                  VLC_CODEC_RGBA,
1806                                  RGBFromRGB,
1807                                  FillRGBAPicture,
1808                                  BlendRGBAPixel );
1809             else if( *p_chroma == VLC_CODEC_ARGB )
1810                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox,
1811                                  i_margin, *p_chroma, RGBFromRGB,
1812                                  FillARGBPicture, BlendARGBPixel );
1813
1814             if( !rv )
1815                 break;
1816         }
1817
1818         /* With karaoke, we're going to have to render the text a number
1819          * of times to show the progress marker on the text.
1820          */
1821         if( pi_k_durations )
1822             var_SetBool( p_filter, "text-rerender", true );
1823     }
1824
1825     FreeLines( p_lines );
1826
1827     free( psz_text );
1828     for( int i = 0; i < i_text_length; i++ )
1829     {
1830         if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
1831             text_style_Delete( pp_styles[i] );
1832     }
1833     free( pp_styles );
1834     free( pi_k_durations );
1835
1836     return rv;
1837 }
1838
1839 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
1840                        subpicture_region_t *p_region_in,
1841                        const vlc_fourcc_t *p_chroma_list )
1842 {
1843     return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
1844 }
1845
1846 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
1847                        subpicture_region_t *p_region_in,
1848                        const vlc_fourcc_t *p_chroma_list )
1849 {
1850     return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
1851 }
1852
1853 /*****************************************************************************
1854  * Create: allocates osd-text video thread output method
1855  *****************************************************************************
1856  * This function allocates and initializes a Clone vout method.
1857  *****************************************************************************/
1858 static int Init_FT( vlc_object_t *p_this,
1859                     const char *psz_fontfile,
1860                     const int fontindex,
1861                     const float f_outline_thickness)
1862 {
1863     filter_t      *p_filter = (filter_t *)p_this;
1864     filter_sys_t  *p_sys = p_filter->p_sys;
1865
1866     /* */
1867     int i_error = FT_Init_FreeType( &p_sys->p_library );
1868     if( i_error )
1869     {
1870         msg_Err( p_filter, "couldn't initialize freetype" );
1871         goto error;
1872     }
1873
1874     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
1875                            fontindex, &p_sys->p_face );
1876
1877     if( i_error == FT_Err_Unknown_File_Format )
1878     {
1879         msg_Err( p_filter, "file %s have unknown format",
1880                  psz_fontfile ? psz_fontfile : "(null)" );
1881         goto error;
1882     }
1883     else if( i_error )
1884     {
1885         msg_Err( p_filter, "failed to load font file %s",
1886                  psz_fontfile ? psz_fontfile : "(null)" );
1887         goto error;
1888     }
1889
1890     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
1891     if( i_error )
1892     {
1893         msg_Err( p_filter, "font has no unicode translation table" );
1894         goto error;
1895     }
1896
1897     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
1898
1899     p_sys->p_stroker = NULL;
1900     if( f_outline_thickness > .001f )
1901     {
1902         i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
1903         if( i_error )
1904             msg_Err( p_filter, "Failed to create stroker for outlining" );
1905     }
1906
1907     return VLC_SUCCESS;
1908
1909 error:
1910     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
1911     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
1912
1913     return VLC_EGENERIC;
1914 }
1915
1916
1917 static int Create( vlc_object_t *p_this )
1918 {
1919     filter_t      *p_filter = (filter_t *)p_this;
1920     filter_sys_t  *p_sys;
1921     char          *psz_fontfile   = NULL;
1922     char          *psz_fontname = NULL;
1923     char          *psz_monofontfile   = NULL;
1924     char          *psz_monofontfamily = NULL;
1925     int            fontindex = 0, monofontindex = 0;
1926
1927     /* Allocate structure */
1928     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
1929     if( !p_sys )
1930         return VLC_ENOMEM;
1931
1932     p_sys->style.psz_fontname   = NULL;
1933     p_sys->p_xml            = NULL;
1934     p_sys->p_face           = 0;
1935     p_sys->p_library        = 0;
1936     p_sys->style.i_font_size      = 0;
1937     p_sys->style.i_style_flags = 0;
1938
1939     /*
1940      * The following variables should not be cached, as they might be changed on-the-fly:
1941      * freetype-rel-fontsize, freetype-background-opacity, freetype-background-color,
1942      * freetype-outline-thickness, freetype-color
1943      *
1944      */
1945
1946     psz_fontname = var_InheritString( p_filter, "freetype-font" );
1947     psz_monofontfamily = var_InheritString( p_filter, "freetype-monofont" );
1948     p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
1949     p_sys->style.i_font_alpha = var_InheritInteger( p_filter,"freetype-opacity" );
1950     p_sys->style.i_font_alpha = VLC_CLIP( p_sys->style.i_font_alpha, 0, 255 );
1951     if( var_InheritBool( p_filter, "freetype-bold" ) )
1952         p_sys->style.i_style_flags |= STYLE_BOLD;
1953
1954     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
1955     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
1956     p_sys->style.i_outline_alpha = var_InheritInteger( p_filter, "freetype-outline-opacity" );
1957     p_sys->style.i_outline_alpha = VLC_CLIP( p_sys->style.i_outline_alpha, 0, 255 );
1958     p_sys->style.i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
1959     p_sys->style.i_outline_color = VLC_CLIP( p_sys->style.i_outline_color, 0, 0xFFFFFF );
1960
1961     p_sys->style.i_shadow_alpha = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
1962     p_sys->style.i_shadow_alpha = VLC_CLIP( p_sys->style.i_shadow_alpha, 0, 255 );
1963     p_sys->style.i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
1964     p_sys->style.i_shadow_color = VLC_CLIP( p_sys->style.i_shadow_color, 0, 0xFFFFFF );
1965     float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
1966     float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
1967     f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
1968     p_sys->f_shadow_vector_x = f_shadow_distance * cosf((float)(2. * M_PI) * f_shadow_angle / 360);
1969     p_sys->f_shadow_vector_y = f_shadow_distance * sinf((float)(2. * M_PI) * f_shadow_angle / 360);
1970
1971     /* Set default psz_fontname */
1972     if( !psz_fontname || !*psz_fontname )
1973     {
1974         free( psz_fontname );
1975 #ifdef HAVE_GET_FONT_BY_FAMILY_NAME
1976         psz_fontname = strdup( DEFAULT_FAMILY );
1977 #else
1978         psz_fontname = File_Select( DEFAULT_FONT_FILE );
1979 #endif
1980     }
1981
1982     /* set default psz_monofontname */
1983     if( !psz_monofontfamily || !*psz_monofontfamily )
1984     {
1985         free( psz_monofontfamily );
1986 #ifdef HAVE_GET_FONT_BY_FAMILY_NAME
1987         psz_monofontfamily = strdup( DEFAULT_MONOSPACE_FAMILY );
1988 #else
1989         psz_monofontfamily = File_Select( DEFAULT_MONOSPACE_FONT_FILE );
1990 #endif
1991     }
1992
1993     /* Set the current font file */
1994     p_sys->style.psz_fontname = psz_fontname;
1995     p_sys->style.psz_monofontname = psz_monofontfamily;
1996
1997 #ifdef HAVE_FONTCONFIG
1998     p_sys->pf_select = FontConfig_Select;
1999     FontConfig_BuildCache( p_filter );
2000 #elif defined( __APPLE__ )
2001 #if !TARGET_OS_IPHONE
2002     p_sys->pf_select = MacLegacy_Select;
2003 #endif
2004 #elif defined( _WIN32 ) && defined( HAVE_GET_FONT_BY_FAMILY_NAME )
2005     p_sys->pf_select = Win32_Select;
2006 #else
2007     p_sys->pf_select = Dummy_Select;
2008 #endif
2009
2010     /* */
2011     psz_fontfile = p_sys->pf_select( p_filter, psz_fontname, false, false,
2012                                       p_sys->i_default_font_size, &fontindex );
2013     psz_monofontfile = p_sys->pf_select( p_filter, psz_monofontfamily, false,
2014                                           false, p_sys->i_default_font_size,
2015                                           &monofontindex );
2016     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontname, psz_fontfile );
2017     msg_Dbg( p_filter, "Using %s as mono-font from file %s", psz_monofontfamily, psz_monofontfile );
2018
2019     /* If nothing is found, use the default family */
2020     if( !psz_fontfile )
2021         psz_fontfile = File_Select( psz_fontname );
2022     if( !psz_monofontfile )
2023         psz_monofontfile = File_Select( psz_monofontfamily );
2024
2025     if( Init_FT( p_this, psz_fontfile, fontindex, f_outline_thickness ) != VLC_SUCCESS )
2026         goto error;
2027
2028     p_sys->pp_font_attachments = NULL;
2029     p_sys->i_font_attachments = 0;
2030
2031     p_filter->pf_render_text = RenderText;
2032     p_filter->pf_render_html = RenderHtml;
2033
2034     LoadFontsFromAttachments( p_filter );
2035
2036     free( psz_fontfile );
2037     free( psz_monofontfile );
2038
2039     return VLC_SUCCESS;
2040
2041 error:
2042     free( psz_fontfile );
2043     free( psz_monofontfile );
2044     free( psz_fontname );
2045     free( psz_monofontfamily );
2046     free( p_sys );
2047     return VLC_EGENERIC;
2048 }
2049
2050
2051 static void Destroy_FT( vlc_object_t *p_this )
2052 {
2053     filter_t *p_filter = (filter_t *)p_this;
2054     filter_sys_t *p_sys = p_filter->p_sys;
2055
2056     if( p_sys->p_stroker )
2057         FT_Stroker_Done( p_sys->p_stroker );
2058     FT_Done_Face( p_sys->p_face );
2059     FT_Done_FreeType( p_sys->p_library );
2060 }
2061
2062 /*****************************************************************************
2063  * Destroy: destroy Clone video thread output method
2064  *****************************************************************************
2065  * Clean up all data and library connections
2066  *****************************************************************************/
2067 static void Destroy( vlc_object_t *p_this )
2068 {
2069     filter_t *p_filter = (filter_t *)p_this;
2070     filter_sys_t *p_sys = p_filter->p_sys;
2071
2072     if( p_sys->pp_font_attachments )
2073     {
2074         for( int k = 0; k < p_sys->i_font_attachments; k++ )
2075             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2076
2077         free( p_sys->pp_font_attachments );
2078     }
2079
2080     if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2081     free( p_sys->style.psz_fontname );
2082     free( p_sys->style.psz_monofontname );
2083
2084     Destroy_FT( p_this );
2085     free( p_sys );
2086 }