freetype.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:89k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * freetype.c : Put text on the video, using freetype2
  3.  *****************************************************************************
  4.  * Copyright (C) 2002 - 2007 the VideoLAN team
  5.  * $Id: 99b7a80304afd53d0038e167aa0edc46dd46fc35 $
  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.  * Preamble
  27.  *****************************************************************************/
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <vlc_plugin.h>
  33. #include <vlc_osd.h>
  34. #include <vlc_filter.h>
  35. #include <vlc_stream.h>
  36. #include <vlc_xml.h>
  37. #include <vlc_input.h>
  38. #include <vlc_strings.h>
  39. #include <math.h>
  40. #include <errno.h>
  41. #include <ft2build.h>
  42. #include FT_FREETYPE_H
  43. #include FT_GLYPH_H
  44. #define FT_FLOOR(X)     ((X & -64) >> 6)
  45. #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
  46. #define FT_MulFix(v, s) (((v)*(s))>>16)
  47. #ifdef __APPLE__
  48. #import <Carbon/Carbon.h>
  49. #endif
  50. #ifdef __APPLE__
  51. #define DEFAULT_FONT "/Library/Fonts/Arial Black.ttf"
  52. #define FC_DEFAULT_FONT "Arial Black"
  53. #elif defined( SYS_BEOS )
  54. #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
  55. #define FC_DEFAULT_FONT "Swiss"
  56. #elif defined( WIN32 )
  57. #define DEFAULT_FONT "" /* Default font found at run-time */
  58. #define FC_DEFAULT_FONT "Arial"
  59. #else
  60. #define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
  61. #define FC_DEFAULT_FONT "Serif Bold"
  62. #endif
  63. #if defined(HAVE_FRIBIDI)
  64. #include <fribidi/fribidi.h>
  65. #endif
  66. #ifdef HAVE_FONTCONFIG
  67. #include <fontconfig/fontconfig.h>
  68. #endif
  69. #include <assert.h>
  70. /*****************************************************************************
  71.  * Module descriptor
  72.  *****************************************************************************/
  73. static int  Create ( vlc_object_t * );
  74. static void Destroy( vlc_object_t * );
  75. #define FONT_TEXT N_("Font")
  76. #define FONT_LONGTEXT N_("Filename for the font you want to use")
  77. #define FONTSIZE_TEXT N_("Font size in pixels")
  78. #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " 
  79.     "that will be rendered on the video. " 
  80.     "If set to something different than 0 this option will override the " 
  81.     "relative font size." )
  82. #define OPACITY_TEXT N_("Opacity")
  83. #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " 
  84.     "text that will be rendered on the video. 0 = transparent, " 
  85.     "255 = totally opaque. " )
  86. #define COLOR_TEXT N_("Text default color")
  87. #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "
  88.     "the video. This must be an hexadecimal (like HTML colors). The first two "
  89.     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"
  90.     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
  91. #define FONTSIZER_TEXT N_("Relative font size")
  92. #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " 
  93.     "fonts that will be rendered on the video. If absolute font size is set, "
  94.     "relative size will be overriden." )
  95. static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
  96. static const char *const ppsz_sizes_text[] = {
  97.     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
  98. #define YUVP_TEXT N_("Use YUVP renderer")
  99. #define YUVP_LONGTEXT N_("This renders the font using "paletized YUV". " 
  100.   "This option is only needed if you want to encode into DVB subtitles" )
  101. #define EFFECT_TEXT N_("Font Effect")
  102. #define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " 
  103. "text to improve its readability." )
  104. #define EFFECT_BACKGROUND  1
  105. #define EFFECT_OUTLINE     2
  106. #define EFFECT_OUTLINE_FAT 3
  107. static int const pi_effects[] = { 1, 2, 3 };
  108. static const char *const ppsz_effects_text[] = {
  109.     N_("Background"),N_("Outline"), N_("Fat Outline") };
  110. static const int pi_color_values[] = {
  111.   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
  112.   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
  113.   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
  114. static const char *const ppsz_color_descriptions[] = {
  115.   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
  116.   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
  117.   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
  118. vlc_module_begin ()
  119.     set_shortname( N_("Text renderer"))
  120.     set_description( N_("Freetype2 font renderer") )
  121.     set_category( CAT_VIDEO )
  122.     set_subcategory( SUBCAT_VIDEO_SUBPIC )
  123.     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
  124.               false )
  125.     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
  126.                  FONTSIZE_LONGTEXT, true )
  127.     /* opacity valid on 0..255, with default 255 = fully opaque */
  128.     add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
  129.         OPACITY_TEXT, OPACITY_LONGTEXT, true )
  130.     /* hook to the color values list, with default 0x00ffffff = white */
  131.     add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
  132.                  COLOR_LONGTEXT, false )
  133.         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
  134.     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
  135.                  FONTSIZER_LONGTEXT, false )
  136.         change_integer_list( pi_sizes, ppsz_sizes_text, NULL )
  137.     add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
  138.                  EFFECT_LONGTEXT, false )
  139.         change_integer_list( pi_effects, ppsz_effects_text, NULL )
  140.     add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
  141.               YUVP_LONGTEXT, true )
  142.     set_capability( "text renderer", 100 )
  143.     add_shortcut( "text" )
  144.     set_callbacks( Create, Destroy )
  145. vlc_module_end ()
  146. /*****************************************************************************
  147.  * Local prototypes
  148.  *****************************************************************************/
  149. /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
  150. static int RenderText( filter_t *, subpicture_region_t *,
  151.                        subpicture_region_t * );
  152. #ifdef HAVE_FONTCONFIG
  153. static int RenderHtml( filter_t *, subpicture_region_t *,
  154.                        subpicture_region_t * );
  155. static char *FontConfig_Select( FcConfig *, const char *,
  156.                                 bool, bool, int * );
  157. #endif
  158. static int LoadFontsFromAttachments( filter_t *p_filter );
  159. static int GetFontSize( filter_t *p_filter );
  160. static int SetFontSize( filter_t *, int );
  161. static void YUVFromRGB( uint32_t i_argb,
  162.                         uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
  163. typedef struct line_desc_t line_desc_t;
  164. struct line_desc_t
  165. {
  166.     /** NULL-terminated list of glyphs making the string */
  167.     FT_BitmapGlyph *pp_glyphs;
  168.     /** list of relative positions for the glyphs */
  169.     FT_Vector      *p_glyph_pos;
  170.     /** list of RGB information for styled text
  171.      * -- if the rendering mode supports it (RenderYUVA) and
  172.      *  b_new_color_mode is set, then it becomes possible to
  173.      *  have multicoloured text within the subtitles. */
  174.     uint32_t       *p_fg_rgb;
  175.     uint32_t       *p_bg_rgb;
  176.     uint8_t        *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
  177.     bool      b_new_color_mode;
  178.     /** underline information -- only supplied if text should be underlined */
  179.     uint16_t       *pi_underline_offset;
  180.     uint16_t       *pi_underline_thickness;
  181.     int             i_height;
  182.     int             i_width;
  183.     int             i_red, i_green, i_blue;
  184.     int             i_alpha;
  185.     line_desc_t    *p_next;
  186. };
  187. static line_desc_t *NewLine( int );
  188. typedef struct
  189. {
  190.     int         i_font_size;
  191.     uint32_t    i_font_color;         /* ARGB */
  192.     uint32_t    i_karaoke_bg_color;   /* ARGB */
  193.     bool  b_italic;
  194.     bool  b_bold;
  195.     bool  b_underline;
  196.     char       *psz_fontname;
  197. } ft_style_t;
  198. static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
  199. static void FreeLines( line_desc_t * );
  200. static void FreeLine( line_desc_t * );
  201. #ifdef HAVE_FONTCONFIG
  202. static vlc_object_t *FontBuilderAttach( filter_t *p_filter );
  203. static void  FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
  204. static void* FontBuilderThread( vlc_object_t *p_this);
  205. static void  FontBuilderDestructor( vlc_object_t *p_this );
  206. static void  FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder );
  207. static int   FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
  208.                         void* );
  209. #endif
  210. /*****************************************************************************
  211.  * filter_sys_t: freetype local data
  212.  *****************************************************************************
  213.  * This structure is part of the video output thread descriptor.
  214.  * It describes the freetype specific properties of an output thread.
  215.  *****************************************************************************/
  216. struct filter_sys_t
  217. {
  218.     FT_Library     p_library;   /* handle to library     */
  219.     FT_Face        p_face;      /* handle to face object */
  220.     bool     i_use_kerning;
  221.     uint8_t        i_font_opacity;
  222.     int            i_font_color;
  223.     int            i_font_size;
  224.     int            i_effect;
  225.     int            i_default_font_size;
  226.     int            i_display_height;
  227. #ifdef HAVE_FONTCONFIG
  228.     bool           b_fontconfig_ok;
  229.     FcConfig      *p_fontconfig;
  230.     xml_t         *p_xml;
  231. #endif
  232.     input_attachment_t **pp_font_attachments;
  233.     int                  i_font_attachments;
  234.     vlc_object_t  *p_fontbuilder;
  235. };
  236. #define UCHAR uint32_t
  237. #define TR_DEFAULT_FONT FC_DEFAULT_FONT
  238. #define TR_FONT_STYLE_PTR ft_style_t *
  239. #include "text_renderer.h"
  240. /*****************************************************************************
  241.  * Create: allocates osd-text video thread output method
  242.  *****************************************************************************
  243.  * This function allocates and initializes a Clone vout method.
  244.  *****************************************************************************/
  245. static int Create( vlc_object_t *p_this )
  246. {
  247.     filter_t      *p_filter = (filter_t *)p_this;
  248.     filter_sys_t  *p_sys;
  249.     char          *psz_fontfile = NULL;
  250.     int            i_error;
  251.     vlc_value_t    val;
  252.     /* Allocate structure */
  253.     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
  254.     if( !p_sys )
  255.         return VLC_ENOMEM;
  256.  #ifdef HAVE_FONTCONFIG
  257.     p_sys->p_xml = NULL;
  258. #endif
  259.     p_sys->p_face = 0;
  260.     p_sys->p_library = 0;
  261.     p_sys->i_font_size = 0;
  262.     p_sys->i_display_height = 0;
  263.     var_Create( p_filter, "freetype-font",
  264.                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
  265.     var_Create( p_filter, "freetype-fontsize",
  266.                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  267.     var_Create( p_filter, "freetype-rel-fontsize",
  268.                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  269.     var_Create( p_filter, "freetype-opacity",
  270.                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  271.     var_Create( p_filter, "freetype-effect",
  272.                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  273.     var_Get( p_filter, "freetype-opacity", &val );
  274.     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
  275.     var_Create( p_filter, "freetype-color",
  276.                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  277.     var_Get( p_filter, "freetype-color", &val );
  278.     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
  279.     p_sys->i_effect = var_GetInteger( p_filter, "freetype-effect" );
  280.     /* Look what method was requested */
  281.     var_Get( p_filter, "freetype-font", &val );
  282.     psz_fontfile = val.psz_string;
  283.     if( !psz_fontfile || !*psz_fontfile )
  284.     {
  285.         free( psz_fontfile );
  286.         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
  287.         if( !psz_fontfile )
  288.             goto error;
  289. #ifdef WIN32
  290.         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
  291.         strcat( psz_fontfile, "\fonts\arial.ttf" );
  292. #else
  293.         msg_Err( p_filter, "user didn't specify a font" );
  294.         goto error;
  295. #endif
  296.     }
  297.     i_error = FT_Init_FreeType( &p_sys->p_library );
  298.     if( i_error )
  299.     {
  300.         msg_Err( p_filter, "couldn't initialize freetype" );
  301.         goto error;
  302.     }
  303.     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
  304.                            0, &p_sys->p_face );
  305.     if( i_error == FT_Err_Unknown_File_Format )
  306.     {
  307.         msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
  308.         goto error;
  309.     }
  310.     else if( i_error )
  311.     {
  312.         msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
  313.         goto error;
  314.     }
  315.     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
  316.     if( i_error )
  317.     {
  318.         msg_Err( p_filter, "font has no unicode translation table" );
  319.         goto error;
  320.     }
  321. #ifdef HAVE_FONTCONFIG
  322.     p_sys->b_fontconfig_ok = false;
  323.     p_sys->p_fontconfig    = NULL;
  324.     p_sys->p_fontbuilder   = FontBuilderAttach( p_filter );
  325. #endif
  326.     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
  327.     var_Get( p_filter, "freetype-fontsize", &val );
  328.     p_sys->i_default_font_size = val.i_int;
  329.     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
  330.     free( psz_fontfile );
  331.     p_sys->pp_font_attachments = NULL;
  332.     p_sys->i_font_attachments = 0;
  333.     p_filter->pf_render_text = RenderText;
  334. #ifdef HAVE_FONTCONFIG
  335.     p_filter->pf_render_html = RenderHtml;
  336. #else
  337.     p_filter->pf_render_html = NULL;
  338. #endif
  339.     LoadFontsFromAttachments( p_filter );
  340.     return VLC_SUCCESS;
  341.  error:
  342.     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
  343.     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
  344.     free( psz_fontfile );
  345.     free( p_sys );
  346.     return VLC_EGENERIC;
  347. }
  348. /*****************************************************************************
  349.  * Destroy: destroy Clone video thread output method
  350.  *****************************************************************************
  351.  * Clean up all data and library connections
  352.  *****************************************************************************/
  353. static void Destroy( vlc_object_t *p_this )
  354. {
  355.     filter_t *p_filter = (filter_t *)p_this;
  356.     filter_sys_t *p_sys = p_filter->p_sys;
  357.     if( p_sys->pp_font_attachments )
  358.     {
  359.         int   k;
  360.         for( k = 0; k < p_sys->i_font_attachments; k++ )
  361.             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
  362.         free( p_sys->pp_font_attachments );
  363.     }
  364. #ifdef HAVE_FONTCONFIG
  365.     FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
  366.     if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
  367. #endif
  368.     /* FcFini asserts calling the subfunction FcCacheFini()
  369.      * even if no other library functions have been made since FcInit(),
  370.      * so don't call it. */
  371.     FT_Done_Face( p_sys->p_face );
  372.     FT_Done_FreeType( p_sys->p_library );
  373.     free( p_sys );
  374. }
  375. #ifdef HAVE_FONTCONFIG
  376. static vlc_mutex_t fb_lock = VLC_STATIC_MUTEX;
  377. static vlc_object_t *FontBuilderAttach( filter_t *p_filter )
  378. {
  379.     /* Check for an existing Fontbuilder thread */
  380.     vlc_mutex_lock( &fb_lock );
  381.     vlc_object_t *p_fontbuilder =
  382.         vlc_object_find_name( p_filter->p_libvlc,
  383.                               "fontlist builder", FIND_CHILD );
  384.     if( !p_fontbuilder )
  385.     {
  386.         /* Create the FontBuilderThread thread as a child of a top-level
  387.          * object, so that it can survive the destruction of the
  388.          * freetype object - the fontlist only needs to be built once,
  389.          * and calling the fontbuild a second time while the first is
  390.          * still in progress can cause thread instabilities.
  391.          *
  392.          * XXX The fontbuilder will be destroy as soon as it is unused.
  393.          */
  394.         p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
  395.                                            sizeof(vlc_object_t) );
  396.         if( p_fontbuilder )
  397.         {
  398.             p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
  399.             p_fontbuilder->p_private = NULL;
  400.             vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
  401.             vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
  402.             var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
  403.             var_SetBool( p_fontbuilder, "build-done", false );
  404.             var_Create( p_fontbuilder, "build-joined", VLC_VAR_BOOL );
  405.             var_SetBool( p_fontbuilder, "build-joined", false );
  406.             if( vlc_thread_create( p_fontbuilder,
  407.                                    "fontlist builder",
  408.                                    FontBuilderThread,
  409.                                    VLC_THREAD_PRIORITY_LOW ) )
  410.             {
  411.                 msg_Warn( p_filter, "fontconfig database builder thread can't "
  412.                         "be launched. Font styling support will be limited." );
  413.             }
  414.         }
  415.     }
  416.     if( p_fontbuilder )
  417.     {
  418.         var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
  419.         FontBuilderGetFcConfig( p_filter, p_fontbuilder );
  420.     }
  421.     vlc_mutex_unlock( &fb_lock );
  422.     return p_fontbuilder;
  423. }
  424. static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
  425. {
  426.     vlc_mutex_lock( &fb_lock );
  427.     if( p_fontbuilder )
  428.     {
  429.         var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
  430.         /* We wait for the thread on the first FontBuilderDetach */
  431.         if( !var_GetBool( p_fontbuilder, "build-joined" ) )
  432.         {
  433.             var_SetBool( p_fontbuilder, "build-joined", true );
  434.             vlc_mutex_unlock( &fb_lock );
  435.             /* We need to unlock otherwise we may not join (the thread waiting
  436.              * for the lock). It is safe to unlock as no one else will try a
  437.              * join and we have a reference on the object) */
  438.             vlc_thread_join( p_fontbuilder );
  439.             vlc_mutex_lock( &fb_lock );
  440.         }
  441.         vlc_object_release( p_fontbuilder );
  442.     }
  443.     vlc_mutex_unlock( &fb_lock );
  444. }
  445. static void* FontBuilderThread( vlc_object_t *p_this )
  446. {
  447.     FcConfig      *p_fontconfig = FcInitLoadConfig();
  448.     if( p_fontconfig )
  449.     {
  450.         mtime_t    t1, t2;
  451.         int canc = vlc_savecancel ();
  452.         //msg_Dbg( p_this, "Building font database..." );
  453.         msg_Dbg( p_this, "Building font database..." );
  454.         t1 = mdate();
  455.         if(! FcConfigBuildFonts( p_fontconfig ))
  456.         {
  457.             /* Don't destroy the fontconfig object - we won't be able to do
  458.              * italics or bold or change the font face, but we will still
  459.              * be able to do underline and change the font size.
  460.              */
  461.             msg_Err( p_this, "fontconfig database can't be built. "
  462.                                     "Font styling won't be available" );
  463.         }
  464.         t2 = mdate();
  465.         msg_Dbg( p_this, "Finished building font database." );
  466.         msg_Dbg( p_this, "Took %ld microseconds", (long)((t2 - t1)) );
  467.         vlc_mutex_lock( &fb_lock );
  468.         p_this->p_private = p_fontconfig;
  469.         vlc_mutex_unlock( &fb_lock );
  470.         var_SetBool( p_this, "build-done", true );
  471.         vlc_restorecancel (canc);
  472.     }
  473.     return NULL;
  474. }
  475. static void FontBuilderGetFcConfig( filter_t *p_filter, vlc_object_t *p_fontbuilder )
  476. {
  477.     filter_sys_t *p_sys = p_filter->p_sys;
  478.     p_sys->p_fontconfig = p_fontbuilder->p_private;
  479.     p_sys->b_fontconfig_ok = p_fontbuilder->p_private != NULL;
  480. }
  481. static void FontBuilderDestructor( vlc_object_t *p_this )
  482. {
  483.     FcConfig *p_fontconfig = p_this->p_private;
  484.     if( p_fontconfig )
  485.         FcConfigDestroy( p_fontconfig );
  486. }
  487. static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
  488.                        vlc_value_t oldval, vlc_value_t newval, void *param )
  489. {
  490.     filter_t *p_filter = param;
  491.     if( newval.b_bool )
  492.     {
  493.         vlc_mutex_lock( &fb_lock );
  494.         FontBuilderGetFcConfig( p_filter, p_this );
  495.         vlc_mutex_unlock( &fb_lock );
  496.     }
  497.     VLC_UNUSED(psz_var);
  498.     VLC_UNUSED(oldval);
  499.     return VLC_SUCCESS;
  500. }
  501. #endif
  502. /*****************************************************************************
  503.  * Make any TTF/OTF fonts present in the attachments of the media file
  504.  * and store them for later use by the FreeType Engine
  505.  *****************************************************************************/
  506. static int LoadFontsFromAttachments( filter_t *p_filter )
  507. {
  508.     filter_sys_t         *p_sys = p_filter->p_sys;
  509.     input_thread_t       *p_input;
  510.     input_attachment_t  **pp_attachments;
  511.     int                   i_attachments_cnt;
  512.     int                   k;
  513.     int                   rv = VLC_SUCCESS;
  514.     p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
  515.     if( ! p_input )
  516.         return VLC_EGENERIC;
  517.     if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
  518.     {
  519.         vlc_object_release(p_input);
  520.         return VLC_EGENERIC;
  521.     }
  522.     p_sys->i_font_attachments = 0;
  523.     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
  524.     if(! p_sys->pp_font_attachments )
  525.         rv = VLC_ENOMEM;
  526.     for( k = 0; k < i_attachments_cnt; k++ )
  527.     {
  528.         input_attachment_t *p_attach = pp_attachments[k];
  529.         if( p_sys->pp_font_attachments )
  530.         {
  531.             if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
  532.                  !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
  533.                ( p_attach->i_data > 0 ) &&
  534.                ( p_attach->p_data != NULL ) )
  535.             {
  536.                 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
  537.             }
  538.             else
  539.             {
  540.                 vlc_input_attachment_Delete( p_attach );
  541.             }
  542.         }
  543.         else
  544.         {
  545.             vlc_input_attachment_Delete( p_attach );
  546.         }
  547.     }
  548.     free( pp_attachments );
  549.     vlc_object_release(p_input);
  550.     return rv;
  551. }
  552. /*****************************************************************************
  553.  * Render: place string in picture
  554.  *****************************************************************************
  555.  * This function merges the previously rendered freetype glyphs into a picture
  556.  *****************************************************************************/
  557. static int Render( filter_t *p_filter, subpicture_region_t *p_region,
  558.                    line_desc_t *p_line, int i_width, int i_height )
  559. {
  560.     static const uint8_t pi_gamma[16] =
  561.         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
  562.           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
  563.     uint8_t *p_dst;
  564.     video_format_t fmt;
  565.     int i, x, y, i_pitch;
  566.     uint8_t i_y; /* YUV values, derived from incoming RGB */
  567.     int8_t i_u, i_v;
  568.     /* Create a new subpicture region */
  569.     memset( &fmt, 0, sizeof(video_format_t) );
  570.     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
  571.     fmt.i_aspect = 0;
  572.     fmt.i_width = fmt.i_visible_width = i_width + 4;
  573.     fmt.i_height = fmt.i_visible_height = i_height + 4;
  574.     if( p_region->fmt.i_visible_width > 0 )
  575.         fmt.i_visible_width = p_region->fmt.i_visible_width;
  576.     if( p_region->fmt.i_visible_height > 0 )
  577.         fmt.i_visible_height = p_region->fmt.i_visible_height;
  578.     fmt.i_x_offset = fmt.i_y_offset = 0;
  579.     assert( !p_region->p_picture );
  580.     p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
  581.     if( !p_region->p_picture )
  582.         return VLC_EGENERIC;
  583.     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
  584.     p_region->fmt = fmt;
  585.     /* Calculate text color components */
  586.     i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
  587.                       25 * p_line->i_blue + 128) >> 8) +  16;
  588.     i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
  589.                      112 * p_line->i_blue + 128) >> 8) + 128;
  590.     i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
  591.                       18 * p_line->i_blue + 128) >> 8) + 128;
  592.     /* Build palette */
  593.     fmt.p_palette->i_entries = 16;
  594.     for( i = 0; i < 8; i++ )
  595.     {
  596.         fmt.p_palette->palette[i][0] = 0;
  597.         fmt.p_palette->palette[i][1] = 0x80;
  598.         fmt.p_palette->palette[i][2] = 0x80;
  599.         fmt.p_palette->palette[i][3] = pi_gamma[i];
  600.         fmt.p_palette->palette[i][3] =
  601.             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
  602.     }
  603.     for( i = 8; i < fmt.p_palette->i_entries; i++ )
  604.     {
  605.         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
  606.         fmt.p_palette->palette[i][1] = i_u;
  607.         fmt.p_palette->palette[i][2] = i_v;
  608.         fmt.p_palette->palette[i][3] = pi_gamma[i];
  609.         fmt.p_palette->palette[i][3] =
  610.             (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
  611.     }
  612.     p_dst = p_region->p_picture->Y_PIXELS;
  613.     i_pitch = p_region->p_picture->Y_PITCH;
  614.     /* Initialize the region pixels */
  615.     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
  616.     for( ; p_line != NULL; p_line = p_line->p_next )
  617.     {
  618.         int i_glyph_tmax = 0;
  619.         int i_bitmap_offset, i_offset, i_align_offset = 0;
  620.         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
  621.         {
  622.             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
  623.             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
  624.         }
  625.         if( p_line->i_width < i_width )
  626.         {
  627.             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
  628.             {
  629.                 i_align_offset = i_width - p_line->i_width;
  630.             }
  631.             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
  632.             {
  633.                 i_align_offset = ( i_width - p_line->i_width ) / 2;
  634.             }
  635.         }
  636.         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
  637.         {
  638.             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
  639.             i_offset = ( p_line->p_glyph_pos[ i ].y +
  640.                 i_glyph_tmax - p_glyph->top + 2 ) *
  641.                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
  642.                 i_align_offset;
  643.             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
  644.             {
  645.                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
  646.                 {
  647.                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
  648.                         p_dst[i_offset+x] =
  649.                          ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
  650.                 }
  651.                 i_offset += i_pitch;
  652.             }
  653.         }
  654.     }
  655.     /* Outlining (find something better than nearest neighbour filtering ?) */
  656.     if( 1 )
  657.     {
  658.         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
  659.         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
  660.         uint8_t left, current;
  661.         for( y = 1; y < (int)fmt.i_height - 1; y++ )
  662.         {
  663.             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
  664.             p_dst += p_region->p_picture->Y_PITCH;
  665.             left = 0;
  666.             for( x = 1; x < (int)fmt.i_width - 1; x++ )
  667.             {
  668.                 current = p_dst[x];
  669.                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
  670.                              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;
  671.                 left = current;
  672.             }
  673.         }
  674.         memset( p_top, 0, fmt.i_width );
  675.     }
  676.     return VLC_SUCCESS;
  677. }
  678. static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
  679.                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
  680.                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
  681.                                 int i_glyph_tmax, int i_align_offset,
  682.                                 uint8_t i_y, uint8_t i_u, uint8_t i_v,
  683.                                 subpicture_region_t *p_region)
  684. {
  685.     int y, x, z;
  686.     int i_pitch;
  687.     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
  688.     p_dst_y = p_region->p_picture->Y_PIXELS;
  689.     p_dst_u = p_region->p_picture->U_PIXELS;
  690.     p_dst_v = p_region->p_picture->V_PIXELS;
  691.     p_dst_a = p_region->p_picture->A_PIXELS;
  692.     i_pitch = p_region->p_picture->A_PITCH;
  693.     int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
  694.                      p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;
  695.     for( y = 0; y < i_line_thickness; y++ )
  696.     {
  697.         int i_extra = p_this_glyph->bitmap.width;
  698.         if( b_ul_next_char )
  699.         {
  700.             i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
  701.                       (p_this_glyph_pos->x + p_this_glyph->left);
  702.         }
  703.         for( x = 0; x < i_extra; x++ )
  704.         {
  705.             bool b_ok = true;
  706.             /* break the underline around the tails of any glyphs which cross it */
  707.             for( z = x - i_line_thickness;
  708.                  z < x + i_line_thickness && b_ok;
  709.                  z++ )
  710.             {
  711.                 if( p_next_glyph && ( z >= i_extra ) )
  712.                 {
  713.                     int i_row = i_line_offset + p_next_glyph->top + y;
  714.                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
  715.                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
  716.                     {
  717.                         b_ok = false;
  718.                     }
  719.                 }
  720.                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
  721.                 {
  722.                     int i_row = i_line_offset + p_this_glyph->top + y;
  723.                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
  724.                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
  725.                     {
  726.                         b_ok = false;
  727.                     }
  728.                 }
  729.             }
  730.             if( b_ok )
  731.             {
  732.                 p_dst_y[i_offset+x] = (i_y * 255) >> 8;
  733.                 p_dst_u[i_offset+x] = i_u;
  734.                 p_dst_v[i_offset+x] = i_v;
  735.                 p_dst_a[i_offset+x] = 255;
  736.             }
  737.         }
  738.         i_offset += i_pitch;
  739.     }
  740. }
  741. static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
  742. {
  743.     uint8_t *p_dst = p_region->p_picture->A_PIXELS;
  744.     int i_pitch = p_region->p_picture->A_PITCH;
  745.     int x,y;
  746.     for( ; p_line != NULL; p_line = p_line->p_next )
  747.     {
  748.         int i_glyph_tmax=0, i = 0;
  749.         int i_bitmap_offset, i_offset, i_align_offset = 0;
  750.         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
  751.         {
  752.             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
  753.             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
  754.         }
  755.         if( p_line->i_width < i_width )
  756.         {
  757.             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
  758.             {
  759.                 i_align_offset = i_width - p_line->i_width;
  760.             }
  761.             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
  762.             {
  763.                 i_align_offset = ( i_width - p_line->i_width ) / 2;
  764.             }
  765.         }
  766.         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
  767.         {
  768.             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
  769.             i_offset = ( p_line->p_glyph_pos[ i ].y +
  770.                 i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
  771.                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
  772.                 i_align_offset +xoffset;
  773.             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
  774.             {
  775.                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
  776.                 {
  777.                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
  778.                         if( p_dst[i_offset+x] <
  779.                             ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
  780.                             p_dst[i_offset+x] =
  781.                                 ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
  782.                 }
  783.                 i_offset += i_pitch;
  784.             }
  785.         }
  786.     }
  787. }
  788. /*****************************************************************************
  789.  * Render: place string in picture
  790.  *****************************************************************************
  791.  * This function merges the previously rendered freetype glyphs into a picture
  792.  *****************************************************************************/
  793. static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
  794.                    line_desc_t *p_line, int i_width, int i_height )
  795. {
  796.     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
  797.     video_format_t fmt;
  798.     int i, x, y, i_pitch, i_alpha;
  799.     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
  800.     if( i_width == 0 || i_height == 0 )
  801.         return VLC_SUCCESS;
  802.     /* Create a new subpicture region */
  803.     memset( &fmt, 0, sizeof(video_format_t) );
  804.     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
  805.     fmt.i_aspect = 0;
  806.     fmt.i_width = fmt.i_visible_width = i_width + 6;
  807.     fmt.i_height = fmt.i_visible_height = i_height + 6;
  808.     if( p_region->fmt.i_visible_width > 0 )
  809.         fmt.i_visible_width = p_region->fmt.i_visible_width;
  810.     if( p_region->fmt.i_visible_height > 0 )
  811.         fmt.i_visible_height = p_region->fmt.i_visible_height;
  812.     fmt.i_x_offset = fmt.i_y_offset = 0;
  813.     p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
  814.     if( !p_region->p_picture )
  815.         return VLC_EGENERIC;
  816.     p_region->fmt = fmt;
  817.     /* Calculate text color components */
  818.     YUVFromRGB( (p_line->i_red   << 16) |
  819.                 (p_line->i_green <<  8) |
  820.                 (p_line->i_blue       ),
  821.                 &i_y, &i_u, &i_v);
  822.     i_alpha = p_line->i_alpha;
  823.     p_dst_y = p_region->p_picture->Y_PIXELS;
  824.     p_dst_u = p_region->p_picture->U_PIXELS;
  825.     p_dst_v = p_region->p_picture->V_PIXELS;
  826.     p_dst_a = p_region->p_picture->A_PIXELS;
  827.     i_pitch = p_region->p_picture->A_PITCH;
  828.     /* Initialize the region pixels */
  829.     if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
  830.     {
  831.         memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
  832.         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
  833.         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
  834.         memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
  835.     }
  836.     else
  837.     {
  838.         memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
  839.         memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
  840.         memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
  841.         memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
  842.     }
  843.     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
  844.         p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
  845.     {
  846.         DrawBlack( p_line, i_width, p_region,  0,  0);
  847.         DrawBlack( p_line, i_width, p_region, -1,  0);
  848.         DrawBlack( p_line, i_width, p_region,  0, -1);
  849.         DrawBlack( p_line, i_width, p_region,  1,  0);
  850.         DrawBlack( p_line, i_width, p_region,  0,  1);
  851.     }
  852.     if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
  853.     {
  854.         DrawBlack( p_line, i_width, p_region, -1, -1);
  855.         DrawBlack( p_line, i_width, p_region, -1,  1);
  856.         DrawBlack( p_line, i_width, p_region,  1, -1);
  857.         DrawBlack( p_line, i_width, p_region,  1,  1);
  858.         DrawBlack( p_line, i_width, p_region, -2,  0);
  859.         DrawBlack( p_line, i_width, p_region,  0, -2);
  860.         DrawBlack( p_line, i_width, p_region,  2,  0);
  861.         DrawBlack( p_line, i_width, p_region,  0,  2);
  862.         DrawBlack( p_line, i_width, p_region, -2, -2);
  863.         DrawBlack( p_line, i_width, p_region, -2,  2);
  864.         DrawBlack( p_line, i_width, p_region,  2, -2);
  865.         DrawBlack( p_line, i_width, p_region,  2,  2);
  866.         DrawBlack( p_line, i_width, p_region, -3,  0);
  867.         DrawBlack( p_line, i_width, p_region,  0, -3);
  868.         DrawBlack( p_line, i_width, p_region,  3,  0);
  869.         DrawBlack( p_line, i_width, p_region,  0,  3);
  870.     }
  871.     for( ; p_line != NULL; p_line = p_line->p_next )
  872.     {
  873.         int i_glyph_tmax = 0;
  874.         int i_bitmap_offset, i_offset, i_align_offset = 0;
  875.         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
  876.         {
  877.             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
  878.             i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
  879.         }
  880.         if( p_line->i_width < i_width )
  881.         {
  882.             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
  883.             {
  884.                 i_align_offset = i_width - p_line->i_width;
  885.             }
  886.             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
  887.             {
  888.                 i_align_offset = ( i_width - p_line->i_width ) / 2;
  889.             }
  890.         }
  891.         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
  892.         {
  893.             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
  894.             i_offset = ( p_line->p_glyph_pos[ i ].y +
  895.                 i_glyph_tmax - p_glyph->top + 3 ) *
  896.                 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
  897.                 i_align_offset;
  898.             if( p_line->b_new_color_mode )
  899.             {
  900.                 /* Every glyph can (and in fact must) have its own color */
  901.                 YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
  902.             }
  903.             for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
  904.             {
  905.                 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
  906.                 {
  907.                     uint8_t i_y_local = i_y;
  908.                     uint8_t i_u_local = i_u;
  909.                     uint8_t i_v_local = i_v;
  910.                     if( p_line->p_fg_bg_ratio != 0x00 )
  911.                     {
  912.                         int i_split = p_glyph->bitmap.width *
  913.                                       p_line->p_fg_bg_ratio[ i ] / 0x7f;
  914.                         if( x > i_split )
  915.                         {
  916.                             YUVFromRGB( p_line->p_bg_rgb[ i ],
  917.                                         &i_y_local, &i_u_local, &i_v_local );
  918.                         }
  919.                     }
  920.                     if( p_glyph->bitmap.buffer[i_bitmap_offset] )
  921.                     {
  922.                         p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
  923.                                               i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
  924.                         p_dst_u[i_offset+x] = i_u;
  925.                         p_dst_v[i_offset+x] = i_v;
  926.                         if( p_filter->p_sys->i_effect == EFFECT_BACKGROUND )
  927.                             p_dst_a[i_offset+x] = 0xff;
  928.                     }
  929.                 }
  930.                 i_offset += i_pitch;
  931.             }
  932.             if( p_line->pi_underline_thickness[ i ] )
  933.             {
  934.                 UnderlineGlyphYUVA( p_line->pi_underline_thickness[ i ],
  935.                                     p_line->pi_underline_offset[ i ],
  936.                                    (p_line->pp_glyphs[i+1] && (p_line->pi_underline_thickness[ i + 1] > 0)),
  937.                                     p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
  938.                                     p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
  939.                                     i_glyph_tmax, i_align_offset,
  940.                                     i_y, i_u, i_v,
  941.                                     p_region);
  942.             }
  943.         }
  944.     }
  945.     /* Apply the alpha setting */
  946.     for( i = 0; i < (int)fmt.i_height * i_pitch; i++ )
  947.         p_dst_a[i] = p_dst_a[i] * (255 - i_alpha) / 255;
  948.     return VLC_SUCCESS;
  949. }
  950. /**
  951.  * This function renders a text subpicture region into another one.
  952.  * It also calculates the size needed for this string, and renders the
  953.  * needed glyphs into memory. It is used as pf_add_string callback in
  954.  * the vout method by this module
  955.  */
  956. static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
  957.                        subpicture_region_t *p_region_in )
  958. {
  959.     filter_sys_t *p_sys = p_filter->p_sys;
  960.     line_desc_t  *p_lines = NULL, *p_line = NULL, *p_next = NULL, *p_prev = NULL;
  961.     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
  962.     uint32_t *psz_unicode, *psz_unicode_orig = NULL, i_char, *psz_line_start;
  963.     int i_string_length;
  964.     char *psz_string;
  965.     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
  966.     int i_font_color, i_font_alpha, i_font_size, i_red, i_green, i_blue;
  967.     vlc_value_t val;
  968.     int i_scale = 1000;
  969.     FT_BBox line;
  970.     FT_BBox glyph_size;
  971.     FT_Vector result;
  972.     FT_Glyph tmp_glyph;
  973.     /* Sanity check */
  974.     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
  975.     psz_string = p_region_in->psz_text;
  976.     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
  977.     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
  978.         i_scale = val.i_int;
  979.     if( p_region_in->p_style )
  980.     {
  981.         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
  982.         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
  983.         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
  984.     }
  985.     else
  986.     {
  987.         i_font_color = p_sys->i_font_color;
  988.         i_font_alpha = 255 - p_sys->i_font_opacity;
  989.         i_font_size  = p_sys->i_default_font_size * i_scale / 1000;
  990.     }
  991.     if( i_font_color == 0xFFFFFF ) i_font_color = p_sys->i_font_color;
  992.     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
  993.     SetFontSize( p_filter, i_font_size );
  994.     i_red   = ( i_font_color & 0x00FF0000 ) >> 16;
  995.     i_green = ( i_font_color & 0x0000FF00 ) >>  8;
  996.     i_blue  =   i_font_color & 0x000000FF;
  997.     result.x =  result.y = 0;
  998.     line.xMin = line.xMax = line.yMin = line.yMax = 0;
  999.     psz_unicode = psz_unicode_orig =
  1000.         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
  1001.     if( psz_unicode == NULL )
  1002.         goto error;
  1003. #if defined(WORDS_BIGENDIAN)
  1004.     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
  1005. #else
  1006.     iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
  1007. #endif
  1008.     if( iconv_handle == (vlc_iconv_t)-1 )
  1009.     {
  1010.         msg_Warn( p_filter, "unable to do conversion" );
  1011.         goto error;
  1012.     }
  1013.     {
  1014.         char *p_out_buffer;
  1015.         const char *p_in_buffer = psz_string;
  1016.         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
  1017.         i_in_bytes = strlen( psz_string );
  1018.         i_out_bytes = i_in_bytes * sizeof( uint32_t );
  1019.         i_out_bytes_left = i_out_bytes;
  1020.         p_out_buffer = (char *)psz_unicode;
  1021.         i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
  1022.                            &i_in_bytes,
  1023.                            &p_out_buffer, &i_out_bytes_left );
  1024.         vlc_iconv_close( iconv_handle );
  1025.         if( i_in_bytes )
  1026.         {
  1027.             msg_Warn( p_filter, "failed to convert string to unicode (%m), "
  1028.                       "bytes left %u", (unsigned)i_in_bytes );
  1029.             goto error;
  1030.         }
  1031.         *(uint32_t*)p_out_buffer = 0;
  1032.         i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
  1033.     }
  1034. #if defined(HAVE_FRIBIDI)
  1035.     {
  1036.         uint32_t *p_fribidi_string;
  1037.         int32_t start_pos, pos = 0;
  1038.         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
  1039.         if( !p_fribidi_string )
  1040.             goto error;
  1041.         /* Do bidi conversion line-by-line */
  1042.         while( pos < i_string_length )
  1043.         {
  1044.             while( pos < i_string_length ) 
  1045.             {
  1046.                 i_char = psz_unicode[pos];
  1047.                 if (i_char != 'r' && i_char != 'n')
  1048.                     break;
  1049.                 p_fribidi_string[pos] = i_char;
  1050.                 ++pos;
  1051.             }
  1052.             start_pos = pos;
  1053.             while( pos < i_string_length )
  1054.             {
  1055.                 i_char = psz_unicode[pos];
  1056.                 if (i_char == 'r' || i_char == 'n')
  1057.                     break;
  1058.                 ++pos;
  1059.             }
  1060.             if (pos > start_pos)
  1061.             {
  1062.                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
  1063.                 fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
  1064.                                 pos - start_pos,
  1065.                                 &base_dir,
  1066.                                 (FriBidiChar*)p_fribidi_string + start_pos,
  1067.                                 0, 0, 0);
  1068.             }
  1069.         }
  1070.         free( psz_unicode_orig );
  1071.         psz_unicode = psz_unicode_orig = p_fribidi_string;
  1072.         p_fribidi_string[ i_string_length ] = 0;
  1073.     }
  1074. #endif
  1075.     /* Calculate relative glyph positions and a bounding box for the
  1076.      * entire string */
  1077.     if( !(p_line = NewLine( strlen( psz_string ))) )
  1078.         goto error;
  1079.     p_lines = p_line;
  1080.     i_pen_x = i_pen_y = 0;
  1081.     i_previous = i = 0;
  1082.     psz_line_start = psz_unicode;
  1083. #define face p_sys->p_face
  1084. #define glyph face->glyph
  1085.     while( *psz_unicode )
  1086.     {
  1087.         i_char = *psz_unicode++;
  1088.         if( i_char == 'r' ) /* ignore CR chars wherever they may be */
  1089.         {
  1090.             continue;
  1091.         }
  1092.         if( i_char == 'n' )
  1093.         {
  1094.             psz_line_start = psz_unicode;
  1095.             if( !(p_next = NewLine( strlen( psz_string ))) )
  1096.                 goto error;
  1097.             p_line->p_next = p_next;
  1098.             p_line->i_width = line.xMax;
  1099.             p_line->i_height = face->size->metrics.height >> 6;
  1100.             p_line->pp_glyphs[ i ] = NULL;
  1101.             p_line->i_alpha = i_font_alpha;
  1102.             p_line->i_red = i_red;
  1103.             p_line->i_green = i_green;
  1104.             p_line->i_blue = i_blue;
  1105.             p_prev = p_line;
  1106.             p_line = p_next;
  1107.             result.x = __MAX( result.x, line.xMax );
  1108.             result.y += face->size->metrics.height >> 6;
  1109.             i_pen_x = 0;
  1110.             i_previous = i = 0;
  1111.             line.xMin = line.xMax = line.yMin = line.yMax = 0;
  1112.             i_pen_y += face->size->metrics.height >> 6;
  1113. #if 0
  1114.             msg_Dbg( p_filter, "Creating new line, i is %d", i );
  1115. #endif
  1116.             continue;
  1117.         }
  1118.         i_glyph_index = FT_Get_Char_Index( face, i_char );
  1119.         if( p_sys->i_use_kerning && i_glyph_index
  1120.             && i_previous )
  1121.         {
  1122.             FT_Vector delta;
  1123.             FT_Get_Kerning( face, i_previous, i_glyph_index,
  1124.                             ft_kerning_default, &delta );
  1125.             i_pen_x += delta.x >> 6;
  1126.         }
  1127.         p_line->p_glyph_pos[ i ].x = i_pen_x;
  1128.         p_line->p_glyph_pos[ i ].y = i_pen_y;
  1129.         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
  1130.         if( i_error )
  1131.         {
  1132.             msg_Err( p_filter, "unable to render text FT_Load_Glyph returned"
  1133.                                " %d", i_error );
  1134.             goto error;
  1135.         }
  1136.         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
  1137.         if( i_error )
  1138.         {
  1139.             msg_Err( p_filter, "unable to render text FT_Get_Glyph returned "
  1140.                                "%d", i_error );
  1141.             goto error;
  1142.         }
  1143.         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
  1144.         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, 0, 1);
  1145.         if( i_error )
  1146.         {
  1147.             FT_Done_Glyph( tmp_glyph );
  1148.             continue;
  1149.         }
  1150.         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
  1151.         /* Do rest */
  1152.         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
  1153.             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
  1154.         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
  1155.         {
  1156.             FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
  1157.             p_line->pp_glyphs[ i ] = NULL;
  1158.             FreeLine( p_line );
  1159.             p_line = NewLine( strlen( psz_string ));
  1160.             if( p_prev ) p_prev->p_next = p_line;
  1161.             else p_lines = p_line;
  1162.             uint32_t *psz_unicode_saved = psz_unicode;
  1163.             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
  1164.             {
  1165.                 psz_unicode--;
  1166.             }
  1167.             if( psz_unicode == psz_line_start )
  1168.             {   /* try harder to break that line */
  1169.                 psz_unicode = psz_unicode_saved;
  1170.                 while( psz_unicode > psz_line_start &&
  1171.                     *psz_unicode != '_'  && *psz_unicode != '/' &&
  1172.                     *psz_unicode != '\' && *psz_unicode != '.' )
  1173.                 {
  1174.                     psz_unicode--;
  1175.                 }
  1176.             }
  1177.             if( psz_unicode == psz_line_start )
  1178.             {
  1179.                 msg_Warn( p_filter, "unbreakable string" );
  1180.                 goto error;
  1181.             }
  1182.             else
  1183.             {
  1184.                 *psz_unicode = 'n';
  1185.             }
  1186.             psz_unicode = psz_line_start;
  1187.             i_pen_x = 0;
  1188.             i_previous = i = 0;
  1189.             line.xMin = line.xMax = line.yMin = line.yMax = 0;
  1190.             continue;
  1191.         }
  1192.         line.yMax = __MAX( line.yMax, glyph_size.yMax );
  1193.         line.yMin = __MIN( line.yMin, glyph_size.yMin );
  1194.         i_previous = i_glyph_index;
  1195.         i_pen_x += glyph->advance.x >> 6;
  1196.         i++;
  1197.     }
  1198.     p_line->i_width = line.xMax;
  1199.     p_line->i_height = face->size->metrics.height >> 6;
  1200.     p_line->pp_glyphs[ i ] = NULL;
  1201.     p_line->i_alpha = i_font_alpha;
  1202.     p_line->i_red = i_red;
  1203.     p_line->i_green = i_green;
  1204.     p_line->i_blue = i_blue;
  1205.     result.x = __MAX( result.x, line.xMax );
  1206.     result.y += line.yMax - line.yMin;
  1207. #undef face
  1208. #undef glyph
  1209.     p_region_out->i_x = p_region_in->i_x;
  1210.     p_region_out->i_y = p_region_in->i_y;
  1211.     if( config_GetInt( p_filter, "freetype-yuvp" ) )
  1212.         Render( p_filter, p_region_out, p_lines, result.x, result.y );
  1213.     else
  1214.         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
  1215.     free( psz_unicode_orig );
  1216.     FreeLines( p_lines );
  1217.     return VLC_SUCCESS;
  1218.  error:
  1219.     free( psz_unicode_orig );
  1220.     FreeLines( p_lines );
  1221.     return VLC_EGENERIC;
  1222. }
  1223. #ifdef HAVE_FONTCONFIG
  1224. static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
  1225.         uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
  1226.         bool b_italic, bool b_uline )
  1227. {
  1228.     ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
  1229.     if( p_style )
  1230.     {
  1231.         p_style->i_font_size        = i_font_size;
  1232.         p_style->i_font_color       = i_font_color;
  1233.         p_style->i_karaoke_bg_color = i_karaoke_bg_color;
  1234.         p_style->b_italic           = b_italic;
  1235.         p_style->b_bold             = b_bold;
  1236.         p_style->b_underline        = b_uline;
  1237.         p_style->psz_fontname = strdup( psz_fontname );
  1238.     }
  1239.     return p_style;
  1240. }
  1241. static void DeleteStyle( ft_style_t *p_style )
  1242. {
  1243.     if( p_style )
  1244.     {
  1245.         free( p_style->psz_fontname );
  1246.         free( p_style );
  1247.     }
  1248. }
  1249. static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
  1250. {
  1251.     if( !s1 || !s2 )
  1252.         return false;
  1253.     if( s1 == s2 )
  1254.         return true;
  1255.     if(( s1->i_font_size  == s2->i_font_size ) &&
  1256.        ( s1->i_font_color == s2->i_font_color ) &&
  1257.        ( s1->b_italic     == s2->b_italic ) &&
  1258.        ( s1->b_bold       == s2->b_bold ) &&
  1259.        ( s1->b_underline  == s2->b_underline ) &&
  1260.        ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
  1261.     {
  1262.         return true;
  1263.     }
  1264.     return false;
  1265. }
  1266. static void IconvText( filter_t *p_filter, const char *psz_string,
  1267.                        uint32_t *i_string_length, uint32_t **ppsz_unicode )
  1268. {
  1269.     vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
  1270.     /* If memory hasn't been allocated for our output string, allocate it here
  1271.      * - the calling function must now be responsible for freeing it.
  1272.      */
  1273.     if( !*ppsz_unicode )
  1274.         *ppsz_unicode = (uint32_t *)
  1275.             malloc( (strlen( psz_string ) + 1) * sizeof( uint32_t ));
  1276.     /* We don't need to handle a NULL pointer in *ppsz_unicode
  1277.      * if we are instead testing for a non NULL value like we are here */
  1278.     if( *ppsz_unicode )
  1279.     {
  1280. #if defined(WORDS_BIGENDIAN)
  1281.         iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
  1282. #else
  1283.         iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
  1284. #endif
  1285.         if( iconv_handle != (vlc_iconv_t)-1 )
  1286.         {
  1287.             char *p_in_buffer, *p_out_buffer;
  1288.             size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
  1289.             i_in_bytes = strlen( psz_string );
  1290.             i_out_bytes = i_in_bytes * sizeof( uint32_t );
  1291.             i_out_bytes_left = i_out_bytes;
  1292.             p_in_buffer = (char *) psz_string;
  1293.             p_out_buffer = (char *) *ppsz_unicode;
  1294.             i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
  1295.                     &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
  1296.             vlc_iconv_close( iconv_handle );
  1297.             if( i_in_bytes )
  1298.             {
  1299.                 msg_Warn( p_filter, "failed to convert string to unicode (%m), "
  1300.                           "bytes left %u", (unsigned)i_in_bytes );
  1301.             }
  1302.             else
  1303.             {
  1304.                 *(uint32_t*)p_out_buffer = 0;
  1305.                 *i_string_length =
  1306.                     (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
  1307.             }
  1308.         }
  1309.         else
  1310.         {
  1311.             msg_Warn( p_filter, "unable to do conversion" );
  1312.         }
  1313.     }
  1314. }
  1315. static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
  1316.         font_stack_t **p_fonts, bool b_bold, bool b_italic,
  1317.         bool b_uline )
  1318. {
  1319.     ft_style_t   *p_style = NULL;
  1320.     char       *psz_fontname = NULL;
  1321.     uint32_t    i_font_color = p_sys->i_font_color & 0x00ffffff;
  1322.     uint32_t    i_karaoke_bg_color = i_font_color;
  1323.     int         i_font_size  = p_sys->i_font_size;
  1324.     if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
  1325.                                  &i_font_color, &i_karaoke_bg_color ))
  1326.     {
  1327.         p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
  1328.                 i_karaoke_bg_color, b_bold, b_italic, b_uline );
  1329.     }
  1330.     return p_style;
  1331. }
  1332. static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
  1333.                       bool b_uline, int i_karaoke_bgcolor,
  1334.                       line_desc_t *p_line, uint32_t *psz_unicode,
  1335.                       int *pi_pen_x, int i_pen_y, int *pi_start,
  1336.                       FT_Vector *p_result )
  1337. {
  1338.     FT_BBox      line;
  1339.     int          i_yMin, i_yMax;
  1340.     int          i;
  1341.     bool   b_first_on_line = true;
  1342.     int          i_previous = 0;
  1343.     int          i_pen_x_start = *pi_pen_x;
  1344.     uint32_t *psz_unicode_start = psz_unicode;
  1345.     line.xMin = line.xMax = line.yMin = line.yMax = 0;
  1346.     /* Account for part of line already in position */
  1347.     for( i=0; i<*pi_start; i++ )
  1348.     {
  1349.         FT_BBox glyph_size;
  1350.         FT_Glyph_Get_CBox( (FT_Glyph) p_line->pp_glyphs[ i ],
  1351.                             ft_glyph_bbox_pixels, &glyph_size );
  1352.         line.xMax = p_line->p_glyph_pos[ i ].x + glyph_size.xMax -
  1353.             glyph_size.xMin + p_line->pp_glyphs[ i ]->left;
  1354.         line.yMax = __MAX( line.yMax, glyph_size.yMax );
  1355.         line.yMin = __MIN( line.yMin, glyph_size.yMin );
  1356.     }
  1357.     i_yMin = line.yMin;
  1358.     i_yMax = line.yMax;
  1359.     if( line.xMax > 0 )
  1360.         b_first_on_line = false;
  1361.     while( *psz_unicode && ( *psz_unicode != 'n' ) )
  1362.     {
  1363.         FT_BBox glyph_size;
  1364.         FT_Glyph tmp_glyph;
  1365.         int i_error;
  1366.         int i_glyph_index = FT_Get_Char_Index( p_face, *psz_unicode++ );
  1367.         if( FT_HAS_KERNING( p_face ) && i_glyph_index
  1368.             && i_previous )
  1369.         {
  1370.             FT_Vector delta;
  1371.             FT_Get_Kerning( p_face, i_previous, i_glyph_index,
  1372.                             ft_kerning_default, &delta );
  1373.             *pi_pen_x += delta.x >> 6;
  1374.         }
  1375.         p_line->p_glyph_pos[ i ].x = *pi_pen_x;
  1376.         p_line->p_glyph_pos[ i ].y = i_pen_y;
  1377.         i_error = FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT );
  1378.         if( i_error )
  1379.         {
  1380.             msg_Err( p_filter,
  1381.                    "unable to render text FT_Load_Glyph returned %d", i_error );
  1382.             p_line->pp_glyphs[ i ] = NULL;
  1383.             return VLC_EGENERIC;
  1384.         }
  1385.         i_error = FT_Get_Glyph( p_face->glyph, &tmp_glyph );
  1386.         if( i_error )
  1387.         {
  1388.             msg_Err( p_filter,
  1389.                     "unable to render text FT_Get_Glyph returned %d", i_error );
  1390.             p_line->pp_glyphs[ i ] = NULL;
  1391.             return VLC_EGENERIC;
  1392.         }
  1393.         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
  1394.         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
  1395.         if( i_error )
  1396.         {
  1397.             FT_Done_Glyph( tmp_glyph );
  1398.             continue;
  1399.         }
  1400.         if( b_uline )
  1401.         {
  1402.             float aOffset = FT_FLOOR(FT_MulFix(p_face->underline_position,
  1403.                                                p_face->size->metrics.y_scale));
  1404.             float aSize = FT_CEIL(FT_MulFix(p_face->underline_thickness,
  1405.                                             p_face->size->metrics.y_scale));
  1406.             p_line->pi_underline_offset[ i ]  =
  1407.                                        ( aOffset < 0 ) ? -aOffset : aOffset;
  1408.             p_line->pi_underline_thickness[ i ] =
  1409.                                        ( aSize < 0 ) ? -aSize   : aSize;
  1410.         }
  1411.         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
  1412.         p_line->p_fg_rgb[ i ] = i_font_color & 0x00ffffff;
  1413.         p_line->p_bg_rgb[ i ] = i_karaoke_bgcolor & 0x00ffffff;
  1414.         p_line->p_fg_bg_ratio[ i ] = 0x00;
  1415.         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax -
  1416.                     glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
  1417.         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
  1418.         {
  1419.             for( ; i >= *pi_start; i-- )
  1420.                 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
  1421.             i = *pi_start;
  1422.             while( psz_unicode > psz_unicode_start && *psz_unicode != ' ' )
  1423.             {
  1424.                 psz_unicode--;
  1425.             }
  1426.             if( psz_unicode == psz_unicode_start )
  1427.             {
  1428.                 if( b_first_on_line )
  1429.                 {
  1430.                     msg_Warn( p_filter, "unbreakable string" );
  1431.                     p_line->pp_glyphs[ i ] = NULL;
  1432.                     return VLC_EGENERIC;
  1433.                 }
  1434.                 *pi_pen_x = i_pen_x_start;
  1435.                 p_line->i_width = line.xMax;
  1436.                 p_line->i_height = __MAX( p_line->i_height,
  1437.                                           p_face->size->metrics.height >> 6 );
  1438.                 p_line->pp_glyphs[ i ] = NULL;
  1439.                 p_result->x = __MAX( p_result->x, line.xMax );
  1440.                 p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
  1441.                                                          i_yMax - i_yMin ) );
  1442.                 return VLC_SUCCESS;
  1443.             }
  1444.             else
  1445.             {
  1446.                 *psz_unicode = 'n';
  1447.             }
  1448.             psz_unicode = psz_unicode_start;
  1449.             *pi_pen_x = i_pen_x_start;
  1450.             i_previous = 0;
  1451.             line.yMax = i_yMax;
  1452.             line.yMin = i_yMin;
  1453.             continue;
  1454.         }
  1455.         line.yMax = __MAX( line.yMax, glyph_size.yMax );
  1456.         line.yMin = __MIN( line.yMin, glyph_size.yMin );
  1457.         i_previous = i_glyph_index;
  1458.         *pi_pen_x += p_face->glyph->advance.x >> 6;
  1459.         i++;
  1460.     }
  1461.     p_line->i_width = line.xMax;
  1462.     p_line->i_height = __MAX( p_line->i_height,
  1463.                               p_face->size->metrics.height >> 6 );
  1464.     p_line->pp_glyphs[ i ] = NULL;
  1465.     p_result->x = __MAX( p_result->x, line.xMax );
  1466.     p_result->y = __MAX( p_result->y, __MAX( p_line->i_height,
  1467.                          line.yMax - line.yMin ) );
  1468.     *pi_start = i;
  1469.     /* Get rid of any text processed - if necessary repositioning
  1470.      * at the start of a new line of text
  1471.      */
  1472.     if( !*psz_unicode )
  1473.     {
  1474.         *psz_unicode_start = '';
  1475.     }
  1476.     else if( psz_unicode > psz_unicode_start )
  1477.     {
  1478.         for( i=0; psz_unicode[ i ]; i++ )
  1479.             psz_unicode_start[ i ] = psz_unicode[ i ];
  1480.         psz_unicode_start[ i ] = '';
  1481.     }
  1482.     return VLC_SUCCESS;
  1483. }
  1484. static void SetupLine( filter_t *p_filter, const char *psz_text_in,
  1485.                        uint32_t **psz_text_out, uint32_t *pi_runs,
  1486.                        uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
  1487.                        ft_style_t *p_style )
  1488. {
  1489.     uint32_t      i_string_length = 0;
  1490.     IconvText( p_filter, psz_text_in, &i_string_length, psz_text_out );
  1491.     *psz_text_out += i_string_length;
  1492.     if( ppp_styles && ppi_run_lengths )
  1493.     {
  1494.         (*pi_runs)++;
  1495.         if( *ppp_styles )
  1496.         {
  1497.             *ppp_styles = (ft_style_t **)
  1498.                 realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
  1499.         }
  1500.         else if( *pi_runs == 1 )
  1501.         {
  1502.             *ppp_styles = (ft_style_t **)
  1503.                 malloc( *pi_runs * sizeof( ft_style_t * ) );
  1504.         }
  1505.         /* We have just malloc'ed this memory successfully -
  1506.          * *pi_runs HAS to be within the memory area of *ppp_styles */
  1507.         if( *ppp_styles )
  1508.         {
  1509.             (*ppp_styles)[ *pi_runs - 1 ] = p_style;
  1510.             p_style = NULL;
  1511.         }
  1512.         if( *ppi_run_lengths )
  1513.         {
  1514.             *ppi_run_lengths = (uint32_t *)
  1515.                 realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
  1516.         }
  1517.         else if( *pi_runs == 1 )
  1518.         {
  1519.             *ppi_run_lengths = (uint32_t *)
  1520.                 malloc( *pi_runs * sizeof( uint32_t ) );
  1521.         }
  1522.         /* same remarks here */
  1523.         if( *ppi_run_lengths )
  1524.         {
  1525.             (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
  1526.         }
  1527.     }
  1528.     /* If we couldn't use the p_style argument due to memory allocation
  1529.      * problems above, release it here.
  1530.      */
  1531.     if( p_style ) DeleteStyle( p_style );
  1532. }
  1533. static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style_t *p_style )
  1534. {
  1535.     int k;
  1536.     for( k=0; k < p_sys->i_font_attachments; k++ )
  1537.     {
  1538.         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
  1539.         int                 i_font_idx = 0;
  1540.         FT_Face             p_face = NULL;
  1541.         while( 0 == FT_New_Memory_Face( p_sys->p_library,
  1542.                                         p_attach->p_data,
  1543.                                         p_attach->i_data,
  1544.                                         i_font_idx,
  1545.                                         &p_face ))
  1546.         {
  1547.             if( p_face )
  1548.             {
  1549.                 bool match = !strcasecmp( p_face->family_name,
  1550.                                                 p_style->psz_fontname );
  1551.                 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
  1552.                     match = match && p_style->b_bold;
  1553.                 else
  1554.                     match = match && !p_style->b_bold;
  1555.                 if( p_face->style_flags & FT_STYLE_FLAG_ITALIC )
  1556.                     match = match && p_style->b_italic;
  1557.                 else
  1558.                     match = match && !p_style->b_italic;
  1559.                 if(  match )
  1560.                 {
  1561.                     *pp_face = p_face;
  1562.                     return VLC_SUCCESS;
  1563.                 }
  1564.                 FT_Done_Face( p_face );
  1565.             }
  1566.             i_font_idx++;
  1567.         }
  1568.     }
  1569.     return VLC_EGENERIC;
  1570. }
  1571. static int ProcessLines( filter_t *p_filter,
  1572.                          uint32_t *psz_text,
  1573.                          int i_len,
  1574.                          uint32_t i_runs,
  1575.                          uint32_t *pi_run_lengths,
  1576.                          ft_style_t **pp_styles,
  1577.                          line_desc_t **pp_lines,
  1578.                          FT_Vector *p_result,
  1579.                          bool b_karaoke,
  1580.                          uint32_t i_k_runs,
  1581.                          uint32_t *pi_k_run_lengths,
  1582.                          uint32_t *pi_k_durations )
  1583. {
  1584.     filter_sys_t   *p_sys = p_filter->p_sys;
  1585.     ft_style_t    **pp_char_styles;
  1586.     int            *p_new_positions = NULL;
  1587.     int8_t         *p_levels = NULL;
  1588.     uint8_t        *pi_karaoke_bar = NULL;
  1589.     uint32_t        i, j, k;
  1590.     int             i_prev;
  1591.     /* Assign each character in the text string its style explicitly, so that
  1592.      * after the characters have been shuffled around by Fribidi, we can re-apply
  1593.      * the styles, and to simplify the calculation of runs within a line.
  1594.      */
  1595.     pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
  1596.     if( !pp_char_styles )
  1597.         return VLC_ENOMEM;
  1598.     if( b_karaoke )
  1599.     {
  1600.         pi_karaoke_bar = (uint8_t *) malloc( i_len * sizeof( uint8_t ));
  1601.         /* If we can't allocate sufficient memory for karaoke, continue anyway -
  1602.          * we just won't be able to display the progress bar; at least we'll
  1603.          * get the text.
  1604.          */
  1605.     }
  1606.     i = 0;
  1607.     for( j = 0; j < i_runs; j++ )
  1608.         for( k = 0; k < pi_run_lengths[ j ]; k++ )
  1609.             pp_char_styles[ i++ ] = pp_styles[ j ];
  1610. #if defined(HAVE_FRIBIDI)
  1611.     {
  1612.         ft_style_t  **pp_char_styles_new;
  1613.         int         *p_old_positions;
  1614.         uint32_t    *p_fribidi_string;
  1615.         int start_pos, pos = 0;
  1616.         pp_char_styles_new  = (ft_style_t **)
  1617.             malloc( i_len * sizeof( ft_style_t * ));
  1618.         p_fribidi_string = (uint32_t *)
  1619.             malloc( (i_len + 1) * sizeof(uint32_t) );
  1620.         p_old_positions = (int *)
  1621.             malloc( (i_len + 1) * sizeof( int ) );
  1622.         p_new_positions = (int *)
  1623.             malloc( (i_len + 1) * sizeof( int ) );
  1624.         p_levels = (int8_t *)
  1625.             malloc( (i_len + 1) * sizeof( int8_t ) );
  1626.         if( ! pp_char_styles_new ||
  1627.             ! p_fribidi_string ||
  1628.             ! p_old_positions ||
  1629.             ! p_new_positions ||
  1630.             ! p_levels )
  1631.         {
  1632.             free( p_levels );
  1633.             free( p_old_positions );
  1634.             free( p_new_positions );
  1635.             free( p_fribidi_string );
  1636.             free( pp_char_styles_new );
  1637.             free( pi_karaoke_bar );
  1638.             free( pp_char_styles );
  1639.             return VLC_ENOMEM;
  1640.         }
  1641.         /* Do bidi conversion line-by-line */
  1642.         while(pos < i_len)
  1643.         {
  1644.             while(pos < i_len) {
  1645.                 if (psz_text[pos] != 'n')
  1646.                     break;
  1647.                 p_fribidi_string[pos] = psz_text[pos];
  1648.                 pp_char_styles_new[pos] = pp_char_styles[pos];
  1649.                 p_new_positions[pos] = pos;
  1650.                 p_levels[pos] = 0;
  1651.                 ++pos;
  1652.             }
  1653.             start_pos = pos;
  1654.             while(pos < i_len) {
  1655.                 if (psz_text[pos] == 'n')
  1656.                     break;
  1657.                 ++pos;
  1658.             }
  1659.             if (pos > start_pos)
  1660.             {
  1661.                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
  1662.                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
  1663.                         pos - start_pos, &base_dir,
  1664.                         (FriBidiChar*)p_fribidi_string + start_pos,
  1665.                         p_new_positions + start_pos,
  1666.                         p_old_positions,
  1667.                         p_levels + start_pos );
  1668.                 for( j = (uint32_t) start_pos; j < (uint32_t) pos; j++ )
  1669.                 {
  1670.                     pp_char_styles_new[ j ] = pp_char_styles[ start_pos +
  1671.                                                 p_old_positions[ j - start_pos ] ];
  1672.                     p_new_positions[ j ] += start_pos;
  1673.                 }
  1674.             }
  1675.         }
  1676.         free( p_old_positions );
  1677.         free( pp_char_styles );
  1678.         pp_char_styles = pp_char_styles_new;
  1679.         psz_text = p_fribidi_string;
  1680.         p_fribidi_string[ i_len ] = 0;
  1681.     }
  1682. #endif
  1683.     /* Work out the karaoke */
  1684.     if( pi_karaoke_bar )
  1685.     {
  1686.         int64_t i_last_duration = 0;
  1687.         int64_t i_duration = 0;
  1688.         int64_t i_start_pos = 0;
  1689.         int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
  1690.         for( k = 0; k< i_k_runs; k++ )
  1691.         {
  1692.              double fraction = 0.0;
  1693.              i_duration += pi_k_durations[ k ];
  1694.              if( i_duration < i_elapsed )
  1695.              {
  1696.                  /* Completely finished this run-length -
  1697.                   * let it render normally */
  1698.                  fraction = 1.0;
  1699.              }
  1700.              else if( i_elapsed < i_last_duration )
  1701.              {
  1702.                  /* Haven't got up to this segment yet -
  1703.                   * render it completely in karaoke BG mode */
  1704.                  fraction = 0.0;
  1705.              }
  1706.              else
  1707.              {
  1708.                  /* Partway through this run */
  1709.                  fraction = (double)(i_elapsed - i_last_duration) /
  1710.                             (double)pi_k_durations[ k ];
  1711.              }
  1712.              for( i = 0; i < pi_k_run_lengths[ k ]; i++ )
  1713.              {
  1714.                  double shade = pi_k_run_lengths[ k ] * fraction;
  1715.                  if( p_new_positions )
  1716.                      j = p_new_positions[ i_start_pos + i ];
  1717.                  else
  1718.                      j = i_start_pos + i;
  1719.                  if( i < (uint32_t)shade )
  1720.                      pi_karaoke_bar[ j ] = 0xff;
  1721.                  else if( (double)i > shade )
  1722.                      pi_karaoke_bar[ j ] = 0x00;
  1723.                  else
  1724.                  {
  1725.                      shade -= (int)shade;
  1726.                      pi_karaoke_bar[ j ] = ((int)(shade * 128.0) & 0x7f) |
  1727.                                    ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
  1728.                  }
  1729.              }
  1730.              i_last_duration = i_duration;
  1731.              i_start_pos += pi_k_run_lengths[ k ];
  1732.         }
  1733.     }
  1734.     free( p_levels );
  1735.     free( p_new_positions );
  1736.     FT_Vector tmp_result;
  1737.     line_desc_t *p_line = NULL;
  1738.     line_desc_t *p_prev = NULL;
  1739.     int i_pen_x = 0;
  1740.     int i_pen_y = 0;
  1741.     int i_posn  = 0;
  1742.     p_result->x = p_result->y = 0;
  1743.     tmp_result.x = tmp_result.y = 0;
  1744.     i_prev = 0;
  1745.     for( k = 0; k <= (uint32_t) i_len; k++ )
  1746.     {
  1747.         if( ( k == (uint32_t) i_len ) ||
  1748.           ( ( k > 0 ) &&
  1749.             !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] ) ) )
  1750.         {
  1751.             ft_style_t *p_style = pp_char_styles[ k - 1 ];
  1752.             /* End of the current style run */
  1753.             FT_Face p_face = NULL;
  1754.             int      i_idx = 0;
  1755.             /* Look for a match amongst our attachments first */
  1756.             CheckForEmbeddedFont( p_sys, &p_face, p_style );
  1757.             if( ! p_face )
  1758.             {
  1759.                 char *psz_fontfile = NULL;
  1760.                 vlc_mutex_lock( &fb_lock );
  1761.                 if( p_sys->b_fontconfig_ok )
  1762.                 {
  1763.                     /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
  1764.                      * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
  1765.                     psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
  1766.                                                       p_style->psz_fontname,
  1767.                                                       p_style->b_bold,
  1768.                                                       p_style->b_italic,
  1769.                                                       &i_idx );
  1770.                 }
  1771.                 vlc_mutex_unlock( &fb_lock );
  1772.                 if( psz_fontfile && ! *psz_fontfile )
  1773.                 {
  1774.                     msg_Warn( p_filter, "Fontconfig was unable to find a font: "%s" %s"
  1775.                         " so using default font", p_style->psz_fontname,
  1776.                         ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
  1777.                                                (p_style->b_bold ? "(Bold)" :
  1778.                                              (p_style->b_italic ? "(Italic)" : ""))) );
  1779.                     free( psz_fontfile );
  1780.                     psz_fontfile = NULL;
  1781.                 }
  1782.                 if( psz_fontfile )
  1783.                 {
  1784.                     if( FT_New_Face( p_sys->p_library,
  1785.                                 psz_fontfile, i_idx, &p_face ) )
  1786.                     {
  1787.                         free( psz_fontfile );
  1788.                         free( pp_char_styles );
  1789. #if defined(HAVE_FRIBIDI)
  1790.                         free( psz_text );
  1791. #endif
  1792.                         free( pi_karaoke_bar );
  1793.                         return VLC_EGENERIC;
  1794.                     }
  1795.                     free( psz_fontfile );
  1796.                 }
  1797.             }
  1798.             if( p_face &&
  1799.                 FT_Select_Charmap( p_face, ft_encoding_unicode ) )
  1800.             {
  1801.                 /* We've loaded a font face which is unhelpful for actually
  1802.                  * rendering text - fallback to the default one.
  1803.                  */
  1804.                  FT_Done_Face( p_face );
  1805.                  p_face = NULL;
  1806.             }
  1807.             if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face,
  1808.                         ft_encoding_unicode ) ||
  1809.                 FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0,
  1810.                     p_style->i_font_size ) )
  1811.             {
  1812.                 if( p_face ) FT_Done_Face( p_face );
  1813.                 free( pp_char_styles );
  1814. #if defined(HAVE_FRIBIDI)
  1815.                 free( psz_text );
  1816. #endif
  1817.                 free( pi_karaoke_bar );
  1818.                 return VLC_EGENERIC;
  1819.             }
  1820.             p_sys->i_use_kerning =
  1821.                         FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
  1822.             uint32_t *psz_unicode = (uint32_t *)
  1823.                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
  1824.             if( !psz_unicode )
  1825.             {
  1826.                 if( p_face ) FT_Done_Face( p_face );
  1827.                 free( pp_char_styles );
  1828.                 free( psz_unicode );
  1829. #if defined(HAVE_FRIBIDI)
  1830.                 free( psz_text );
  1831. #endif
  1832.                 free( pi_karaoke_bar );
  1833.                 return VLC_ENOMEM;
  1834.             }
  1835.             memcpy( psz_unicode, psz_text + i_prev,
  1836.                                         sizeof( uint32_t ) * ( k - i_prev ) );
  1837.             psz_unicode[ k - i_prev ] = 0;
  1838.             while( *psz_unicode )
  1839.             {
  1840.                 if( !p_line )
  1841.                 {
  1842.                     if( !(p_line = NewLine( i_len - i_prev)) )
  1843.                     {
  1844.                         if( p_face ) FT_Done_Face( p_face );
  1845.                         free( pp_char_styles );
  1846.                         free( psz_unicode );
  1847. #if defined(HAVE_FRIBIDI)
  1848.                         free( psz_text );
  1849. #endif
  1850.                         free( pi_karaoke_bar );
  1851.                         return VLC_ENOMEM;
  1852.                     }
  1853.                     /* New Color mode only works in YUVA rendering mode --
  1854.                      * (RGB mode has palette constraints on it). We therefore
  1855.                      * need to populate the legacy colour fields also.
  1856.                      */
  1857.                     p_line->b_new_color_mode = true;
  1858.                     p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
  1859.                     p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
  1860.                     p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
  1861.                     p_line->i_blue  = ( p_style->i_font_color & 0x000000ff );
  1862.                     p_line->p_next = NULL;
  1863.                     i_pen_x = 0;
  1864.                     i_pen_y += tmp_result.y;
  1865.                     tmp_result.x = 0;
  1866.                     tmp_result.y = 0;
  1867.                     i_posn = 0;
  1868.                     if( p_prev ) p_prev->p_next = p_line;
  1869.                     else *pp_lines = p_line;
  1870.                 }
  1871.                 if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face,
  1872.                                p_style->i_font_color, p_style->b_underline,
  1873.                                p_style->i_karaoke_bg_color,
  1874.                                p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn,
  1875.                                &tmp_result ) != VLC_SUCCESS )
  1876.                 {
  1877.                     if( p_face ) FT_Done_Face( p_face );
  1878.                     free( pp_char_styles );
  1879.                     free( psz_unicode );
  1880. #if defined(HAVE_FRIBIDI)
  1881.                     free( psz_text );
  1882. #endif
  1883.                     free( pi_karaoke_bar );
  1884.                     return VLC_EGENERIC;
  1885.                 }
  1886.                 if( *psz_unicode )
  1887.                 {
  1888.                     p_result->x = __MAX( p_result->x, tmp_result.x );
  1889.                     p_result->y += tmp_result.y;
  1890.                     p_prev = p_line;
  1891.                     p_line = NULL;
  1892.                     if( *psz_unicode == 'n')
  1893.                     {
  1894.                         uint32_t *c_ptr;
  1895.                         for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
  1896.                         {
  1897.                             *c_ptr = *(c_ptr+1);
  1898.                         }
  1899.                     }
  1900.                 }
  1901.             }
  1902.             free( psz_unicode );
  1903.             if( p_face ) FT_Done_Face( p_face );
  1904.             i_prev = k;
  1905.         }
  1906.     }
  1907.     free( pp_char_styles );
  1908. #if defined(HAVE_FRIBIDI)
  1909.     free( psz_text );
  1910. #endif
  1911.     if( p_line )
  1912.     {
  1913.         p_result->x = __MAX( p_result->x, tmp_result.x );
  1914.         p_result->y += tmp_result.y;
  1915.     }
  1916.     if( pi_karaoke_bar )
  1917.     {
  1918.         int i = 0;
  1919.         for( p_line = *pp_lines; p_line; p_line=p_line->p_next )
  1920.         {
  1921.             for( k = 0; p_line->pp_glyphs[ k ]; k++, i++ )
  1922.             {
  1923.                 if( (pi_karaoke_bar[ i ] & 0x7f) == 0x7f)
  1924.                 {
  1925.                     /* do nothing */
  1926.                 }
  1927.                 else if( (pi_karaoke_bar[ i ] & 0x7f) == 0x00)
  1928.                 {
  1929.                     /* 100% BG colour will render faster if we
  1930.                      * instead make it 100% FG colour, so leave
  1931.                      * the ratio alone and copy the value across
  1932.                      */
  1933.                     p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
  1934.                 }
  1935.                 else
  1936.                 {
  1937.                     if( pi_karaoke_bar[ i ] & 0x80 )
  1938.                     {
  1939.                         /* Swap Left and Right sides over for Right aligned
  1940.                          * language text (eg. Arabic, Hebrew)
  1941.                          */
  1942.                         uint32_t i_tmp = p_line->p_fg_rgb[ k ];
  1943.                         p_line->p_fg_rgb[ k ] = p_line->p_bg_rgb[ k ];
  1944.                         p_line->p_bg_rgb[ k ] = i_tmp;
  1945.                     }
  1946.                     p_line->p_fg_bg_ratio[ k ] = (pi_karaoke_bar[ i ] & 0x7f);
  1947.                 }
  1948.             }
  1949.             /* Jump over the 'n' at the line-end */
  1950.             i++;
  1951.         }
  1952.         free( pi_karaoke_bar );
  1953.     }
  1954.     return VLC_SUCCESS;
  1955. }
  1956. static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
  1957.                        subpicture_region_t *p_region_in )
  1958. {
  1959.     int          rv = VLC_SUCCESS;
  1960.     stream_t     *p_sub = NULL;
  1961.     xml_reader_t *p_xml_reader = NULL;
  1962.     if( !p_region_in || !p_region_in->psz_html )
  1963.         return VLC_EGENERIC;
  1964.     /* Reset the default fontsize in case screen metrics have changed */
  1965.     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
  1966.     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
  1967.                               (uint8_t *) p_region_in->psz_html,
  1968.                               strlen( p_region_in->psz_html ),
  1969.                               true );
  1970.     if( p_sub )
  1971.     {
  1972.         if( !p_filter->p_sys->p_xml ) p_filter->p_sys->p_xml = xml_Create( p_filter );
  1973.         if( p_filter->p_sys->p_xml )
  1974.         {
  1975.             bool b_karaoke = false;
  1976.             p_xml_reader = xml_ReaderCreate( p_filter->p_sys->p_xml, p_sub );
  1977.             if( p_xml_reader )
  1978.             {
  1979.                 /* Look for Root Node */
  1980.                 if( xml_ReaderRead( p_xml_reader ) == 1 )
  1981.                 {
  1982.                     char *psz_node = xml_ReaderName( p_xml_reader );
  1983.                     if( !strcasecmp( "karaoke", psz_node ) )
  1984.                     {
  1985.                         /* We're going to have to render the text a number
  1986.                          * of times to show the progress marker on the text.
  1987.                          */
  1988.                         var_SetBool( p_filter, "text-rerender", true );
  1989.                         b_karaoke = true;
  1990.                     }
  1991.                     else if( !strcasecmp( "text", psz_node ) )
  1992.                     {
  1993.                         b_karaoke = false;
  1994.                     }
  1995.                     else
  1996.                     {
  1997.                         /* Only text and karaoke tags are supported */
  1998.                         msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
  1999.                         xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
  2000.                         p_xml_reader = NULL;
  2001.                         rv = VLC_EGENERIC;
  2002.                     }
  2003.                     free( psz_node );
  2004.                 }
  2005.             }
  2006.             if( p_xml_reader )
  2007.             {
  2008.                 uint32_t   *psz_text;
  2009.                 int         i_len = 0;
  2010.                 uint32_t    i_runs = 0;
  2011.                 uint32_t    i_k_runs = 0;
  2012.                 uint32_t   *pi_run_lengths = NULL;
  2013.                 uint32_t   *pi_k_run_lengths = NULL;
  2014.                 uint32_t   *pi_k_durations = NULL;
  2015.                 ft_style_t  **pp_styles = NULL;
  2016.                 FT_Vector    result;
  2017.                 line_desc_t  *p_lines = NULL;
  2018.                 psz_text = (uint32_t *)malloc( strlen( p_region_in->psz_html ) *
  2019.                                                 sizeof( uint32_t ) );
  2020.                 if( psz_text )
  2021.                 {
  2022.                     uint32_t k;
  2023.                     rv = ProcessNodes( p_filter, p_xml_reader,
  2024.                                   p_region_in->p_style, psz_text, &i_len,
  2025.                                   &i_runs, &pi_run_lengths, &pp_styles,
  2026.                                   b_karaoke, &i_k_runs, &pi_k_run_lengths,
  2027.                                   &pi_k_durations );
  2028.                     p_region_out->i_x = p_region_in->i_x;
  2029.                     p_region_out->i_y = p_region_in->i_y;
  2030.                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
  2031.                     {
  2032.                         rv = ProcessLines( p_filter, psz_text, i_len, i_runs,
  2033.                                 pi_run_lengths, pp_styles, &p_lines, &result,
  2034.                                 b_karaoke, i_k_runs, pi_k_run_lengths,
  2035.                                 pi_k_durations );
  2036.                     }
  2037.                     for( k=0; k<i_runs; k++)
  2038.                         DeleteStyle( pp_styles[k] );
  2039.                     free( pp_styles );
  2040.                     free( pi_run_lengths );
  2041.                     free( psz_text );
  2042.                     /* Don't attempt to render text that couldn't be layed out
  2043.                      * properly.
  2044.                      */
  2045.                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
  2046.                     {
  2047.                         if( config_GetInt( p_filter, "freetype-yuvp" ) )
  2048.                         {
  2049.                             Render( p_filter, p_region_out, p_lines,
  2050.                                     result.x, result.y );
  2051.                         }
  2052.                         else
  2053.                         {
  2054.                             RenderYUVA( p_filter, p_region_out, p_lines,
  2055.                                     result.x, result.y );
  2056.                         }
  2057.                     }
  2058.                 }
  2059.                 FreeLines( p_lines );
  2060.                 xml_ReaderDelete( p_filter->p_sys->p_xml, p_xml_reader );
  2061.             }
  2062.         }
  2063.         stream_Delete( p_sub );
  2064.     }
  2065.     return rv;
  2066. }
  2067. static char* FontConfig_Select( FcConfig* priv, const char* family,
  2068.                           bool b_bold, bool b_italic, int *i_idx )
  2069. {
  2070.     FcResult result;
  2071.     FcPattern *pat, *p_pat;
  2072.     FcChar8* val_s;
  2073.     FcBool val_b;
  2074.     pat = FcPatternCreate();
  2075.     if (!pat) return NULL;
  2076.     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
  2077.     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
  2078.     FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
  2079.     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
  2080.     FcDefaultSubstitute( pat );
  2081.     if( !FcConfigSubstitute( priv, pat, FcMatchPattern ) )
  2082.     {
  2083.         FcPatternDestroy( pat );
  2084.         return NULL;
  2085.     }
  2086.     p_pat = FcFontMatch( priv, pat, &result );
  2087.     FcPatternDestroy( pat );
  2088.     if( !p_pat ) return NULL;
  2089.     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
  2090.         || ( val_b != FcTrue ) )
  2091.     {
  2092.         FcPatternDestroy( p_pat );
  2093.         return NULL;
  2094.     }
  2095.     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
  2096.     {
  2097.         *i_idx = 0;
  2098.     }
  2099.     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
  2100.     {
  2101.         FcPatternDestroy( p_pat );
  2102.         return NULL;
  2103.     }
  2104.     /*
  2105.     if( strcasecmp((const char*)val_s, family ) != 0 )
  2106.         msg_Warn( p_filter, "fontconfig: selected font family is not"
  2107.                             "the requested one: '%s' != '%s'n",
  2108.                             (const char*)val_s, family );
  2109.     */
  2110.     if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
  2111.     {
  2112.         FcPatternDestroy( p_pat );
  2113.         return NULL;
  2114.     }
  2115.     FcPatternDestroy( p_pat );
  2116.     return strdup( (const char*)val_s );
  2117. }
  2118. #else
  2119. static void SetupLine( filter_t *p_filter, const char *psz_text_in,
  2120.                        uint32_t **psz_text_out, uint32_t *pi_runs,
  2121.                        uint32_t **ppi_run_lengths, ft_style_t ***ppp_styles,
  2122.                        ft_style_t *p_style )
  2123. {
  2124.         VLC_UNUSED(p_filter);
  2125.         VLC_UNUSED(psz_text_in);
  2126.         VLC_UNUSED(psz_text_out);
  2127.         VLC_UNUSED(pi_runs);
  2128.         VLC_UNUSED(ppi_run_lengths);
  2129.         VLC_UNUSED(ppp_styles);
  2130.         VLC_UNUSED(p_style);
  2131. }
  2132. static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
  2133.         font_stack_t **p_fonts, bool b_bold, bool b_italic,
  2134.         bool b_uline )
  2135. {
  2136.         VLC_UNUSED(p_sys);
  2137.         VLC_UNUSED(p_fonts);
  2138.         VLC_UNUSED(b_bold);
  2139.         VLC_UNUSED(b_italic);
  2140.         VLC_UNUSED(b_uline);
  2141.         return NULL;
  2142. }
  2143. #endif
  2144. static void FreeLine( line_desc_t *p_line )
  2145. {
  2146.     unsigned int i;
  2147.     for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
  2148.     {
  2149.         FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
  2150.     }
  2151.     free( p_line->pp_glyphs );
  2152.     free( p_line->p_glyph_pos );
  2153.     free( p_line->p_fg_rgb );
  2154.     free( p_line->p_bg_rgb );
  2155.     free( p_line->p_fg_bg_ratio );
  2156.     free( p_line->pi_underline_offset );
  2157.     free( p_line->pi_underline_thickness );
  2158.     free( p_line );
  2159. }
  2160. static void FreeLines( line_desc_t *p_lines )
  2161. {
  2162.     line_desc_t *p_line, *p_next;
  2163.     if( !p_lines ) return;
  2164.     for( p_line = p_lines; p_line != NULL; p_line = p_next )
  2165.     {
  2166.         p_next = p_line->p_next;
  2167.         FreeLine( p_line );
  2168.     }
  2169. }
  2170. static line_desc_t *NewLine( int i_count )
  2171. {
  2172.     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
  2173.     if( !p_line ) return NULL;
  2174.     p_line->i_height = 0;
  2175.     p_line->i_width = 0;
  2176.     p_line->p_next = NULL;
  2177.     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
  2178.     p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
  2179.     p_line->p_fg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
  2180.     p_line->p_bg_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
  2181.     p_line->p_fg_bg_ratio = calloc( i_count + 1, sizeof( uint8_t ) );
  2182.     p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
  2183.     p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
  2184.     if( ( p_line->pp_glyphs == NULL ) ||
  2185.         ( p_line->p_glyph_pos == NULL ) ||
  2186.         ( p_line->p_fg_rgb == NULL ) ||
  2187.         ( p_line->p_bg_rgb == NULL ) ||
  2188.         ( p_line->p_fg_bg_ratio == NULL ) ||
  2189.         ( p_line->pi_underline_offset == NULL ) ||
  2190.         ( p_line->pi_underline_thickness == NULL ) )
  2191.     {
  2192.         free( p_line->pi_underline_thickness );
  2193.         free( p_line->pi_underline_offset );
  2194.         free( p_line->p_fg_rgb );
  2195.         free( p_line->p_bg_rgb );
  2196.         free( p_line->p_fg_bg_ratio );
  2197.         free( p_line->p_glyph_pos );
  2198.         free( p_line->pp_glyphs );
  2199.         free( p_line );
  2200.         return NULL;
  2201.     }
  2202.     p_line->pp_glyphs[0] = NULL;
  2203.     p_line->b_new_color_mode = false;
  2204.     return p_line;
  2205. }
  2206. static int GetFontSize( filter_t *p_filter )
  2207. {
  2208.     filter_sys_t *p_sys = p_filter->p_sys;
  2209.     vlc_value_t   val;
  2210.     int           i_size = 0;
  2211.     if( p_sys->i_default_font_size )
  2212.     {
  2213.         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
  2214.             i_size = p_sys->i_default_font_size * val.i_int / 1000;
  2215.         else
  2216.             i_size = p_sys->i_default_font_size;
  2217.     }
  2218.     else
  2219.     {
  2220.         var_Get( p_filter, "freetype-rel-fontsize", &val );
  2221.         if( val.i_int  > 0 )
  2222.         {
  2223.             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
  2224.             p_filter->p_sys->i_display_height =
  2225.                 p_filter->fmt_out.video.i_height;
  2226.         }
  2227.     }
  2228.     if( i_size <= 0 )
  2229.     {
  2230.         msg_Warn( p_filter, "invalid fontsize, using 12" );
  2231.         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
  2232.             i_size = 12 * val.i_int / 1000;
  2233.         else
  2234.             i_size = 12;
  2235.     }
  2236.     return i_size;
  2237. }
  2238. static int SetFontSize( filter_t *p_filter, int i_size )
  2239. {
  2240.     filter_sys_t *p_sys = p_filter->p_sys;
  2241.     if( !i_size )
  2242.     {
  2243.         i_size = GetFontSize( p_filter );
  2244.         msg_Dbg( p_filter, "using fontsize: %i", i_size );
  2245.     }
  2246.     p_sys->i_font_size = i_size;
  2247.     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
  2248.     {
  2249.         msg_Err( p_filter, "couldn't set font size to %d", i_size );
  2250.         return VLC_EGENERIC;
  2251.     }
  2252.     return VLC_SUCCESS;
  2253. }
  2254. static void YUVFromRGB( uint32_t i_argb,
  2255.                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
  2256. {
  2257.     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
  2258.     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
  2259.     int i_blue  = ( i_argb & 0x000000ff );
  2260.     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
  2261.                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
  2262.     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
  2263.                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
  2264.     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
  2265.                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
  2266. }