freetype.c 97.2 KB
Newer Older
1
2
3
/*****************************************************************************
 * freetype.c : Put text on the video, using freetype2
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5
 * $Id$
6
 *
7
 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8
 *          Gildas Bazin <gbazin@videolan.org>
9
 *          Bernie Purcell <bitmap@videolan.org>
10
 *          Jean-Baptiste Kempf <jb@videolan.org>
11
 *          Felix Paul Kühne <fkuehne@videolan.org>
12
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
13
14
15
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
16
17
18
19
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
20
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
22
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
23
 * You should have received a copy of the GNU Lesser General Public License
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
24
 * along with this program; if not, write to the Free Software Foundation, Inc.,
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
25
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26
27
28
29
30
31
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

32
33
34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
35
#include <math.h>
36

37
#include <vlc_common.h>
38
#include <vlc_plugin.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
39
40
41
42
43
44
#include <vlc_stream.h>                        /* stream_MemoryNew */
#include <vlc_input.h>                         /* vlc_input_attachment_* */
#include <vlc_xml.h>                           /* xml_reader */
#include <vlc_strings.h>                       /* resolve_xml_special_chars */
#include <vlc_charset.h>                       /* ToCharset */
#include <vlc_dialog.h>                        /* FcCache dialog */
45
46
#include <vlc_filter.h>                                      /* filter_sys_t */
#include <vlc_text_style.h>                                   /* text_style_t*/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
47
48
49

/* Default fonts */
#ifdef __APPLE__
50
51
# define DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
# define DEFAULT_FAMILY "Arial Unicode MS"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
52
#elif defined( WIN32 )
53
# define DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
54
# define DEFAULT_FAMILY "Arial"
55
56
57
#elif defined( __OS2__ )
# define DEFAULT_FONT_FILE "/psfonts/tnrwt_k.ttf"
# define DEFAULT_FAMILY "Times New Roman WT K"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
58
#elif defined( HAVE_MAEMO )
59
# define DEFAULT_FONT_FILE "/usr/share/fonts/nokia/nosnb.ttf"
60
# define DEFAULT_FAMILY "Nokia Sans Bold"
61
62
63
#elif defined( __ANDROID__ )
# define DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
# define DEFAULT_FAMILY "Droid Sans Bold"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
64
#else
65
# define DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
66
# define DEFAULT_FAMILY "Serif Bold"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
67
68
69
#endif

/* Freetype */
70
#include <freetype/ftsynth.h>
71
72
#include FT_FREETYPE_H
#include FT_GLYPH_H
73
74
#include FT_STROKER_H

75
76
#define FT_FLOOR(X)     ((X & -64) >> 6)
#define FT_CEIL(X)      (((X + 63) & -64) >> 6)
77
78
79
#ifndef FT_MulFix
 #define FT_MulFix(v, s) (((v)*(s))>>16)
#endif
80

81
82
83
/* apple stuff */
#ifdef __APPLE__
#include <Carbon/Carbon.h>
84
#include <sys/param.h>                         /* for MAXPATHLEN */
85
86
87
88
#undef HAVE_FONTCONFIG
#define HAVE_STYLES
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
89
/* RTL */
90
#if defined(HAVE_FRIBIDI)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
91
# include <fribidi/fribidi.h>
92
93
#endif

94
95
96
97
98
99
100
101
/* Win32 GDI */
#ifdef WIN32
# include <windows.h>
# include <shlobj.h>
# define HAVE_STYLES
# undef HAVE_FONTCONFIG
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
102
/* FontConfig */
103
#ifdef HAVE_FONTCONFIG
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
104
# include <fontconfig/fontconfig.h>
105
106
107
# define HAVE_STYLES
#endif

108
109
#include <assert.h>

110
111
112
113
114
115
116
117
118
119
120
121
#ifdef __OS2__
typedef uint16_t uni_char_t;
# define FREETYPE_TO_UCS    "UCS-2LE"
#else
typedef uint32_t uni_char_t;
# if defined(WORDS_BIGENDIAN)
#  define FREETYPE_TO_UCS   "UCS-4BE"
# else
#  define FREETYPE_TO_UCS   "UCS-4LE"
# endif
#endif

122
/*****************************************************************************
123
 * Module descriptor
124
 *****************************************************************************/
125
126
127
static int  Create ( vlc_object_t * );
static void Destroy( vlc_object_t * );

128
#define FONT_TEXT N_("Font")
129

130
#define FAMILY_LONGTEXT N_("Font family for the font you want to use")
Christophe Mutricy's avatar
Typos    
Christophe Mutricy committed
131
#define FONT_LONGTEXT N_("Font file for the font you want to use")
132

133
#define FONTSIZE_TEXT N_("Font size in pixels")
134
135
#define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
    "that will be rendered on the video. " \
Sam Hocevar's avatar
Sam Hocevar committed
136
    "If set to something different than 0 this option will override the " \
137
    "relative font size." )
138
#define OPACITY_TEXT N_("Text opacity")
139
140
141
142
143
144
145
146
147
148
149
#define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
    "text that will be rendered on the video. 0 = transparent, " \
    "255 = totally opaque. " )
#define COLOR_TEXT N_("Text default color")
#define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
    "the video. This must be an hexadecimal (like HTML colors). The first two "\
    "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
    " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
#define FONTSIZER_TEXT N_("Relative font size")
#define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
    "fonts that will be rendered on the video. If absolute font size is set, "\
Christophe Mutricy's avatar
Typos    
Christophe Mutricy committed
150
    "relative size will be overridden." )
151
#define BOLD_TEXT N_("Force bold")
152

153
154
155
#define BG_OPACITY_TEXT N_("Background opacity")
#define BG_COLOR_TEXT N_("Background color")

156
157
158
159
#define OUTLINE_OPACITY_TEXT N_("Outline opacity")
#define OUTLINE_COLOR_TEXT N_("Outline color")
#define OUTLINE_THICKNESS_TEXT N_("Outline thickness")

160
161
162
163
164
165
#define SHADOW_OPACITY_TEXT N_("Shadow opacity")
#define SHADOW_COLOR_TEXT N_("Shadow color")
#define SHADOW_ANGLE_TEXT N_("Shadow angle")
#define SHADOW_DISTANCE_TEXT N_("Shadow distance")


166
167
168
static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
static const char *const ppsz_sizes_text[] = {
    N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
169
170
171
#define YUVP_TEXT N_("Use YUVP renderer")
#define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
  "This option is only needed if you want to encode into DVB subtitles" )
172

173
static const int pi_color_values[] = {
174
  0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
175
176
  0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
  0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
177

178
static const char *const ppsz_color_descriptions[] = {
179
180
181
  N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
  N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
  N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
182

183
184
185
186
187
188
189
static const int pi_outline_thickness[] = {
    0, 2, 4, 6,
};
static const char *const ppsz_outline_thickness[] = {
    N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
};

190
191
192
193
194
vlc_module_begin ()
    set_shortname( N_("Text renderer"))
    set_description( N_("Freetype2 font renderer") )
    set_category( CAT_VIDEO )
    set_subcategory( SUBCAT_VIDEO_SUBPIC )
gbazin's avatar
   
gbazin committed
195

196
197
198
#ifdef HAVE_STYLES
    add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
#else
199
    add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
200
#endif
201

202
    add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
203
                 FONTSIZE_LONGTEXT, true )
VLC_help's avatar
VLC_help committed
204
        change_integer_range( 0, 4096)
205
        change_safe()
206

207
208
209
210
211
    add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
                 FONTSIZER_LONGTEXT, false )
        change_integer_list( pi_sizes, ppsz_sizes_text )
        change_safe()

212
    /* opacity valid on 0..255, with default 255 = fully opaque */
213
    add_integer_with_range( "freetype-opacity", 255, 0, 255,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
214
        OPACITY_TEXT, OPACITY_LONGTEXT, false )
215
        change_safe()
216

217
    /* hook to the color values list, with default 0x00ffffff = white */
218
    add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
219
                 COLOR_LONGTEXT, false )
220
        change_integer_list( pi_color_values, ppsz_color_descriptions )
221
        change_safe()
222

223
    add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
224
225
        change_safe()

226
    add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
227
                            BG_OPACITY_TEXT, NULL, false )
228
        change_safe()
229
    add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
230
             NULL, false )
231
232
233
        change_integer_list( pi_color_values, ppsz_color_descriptions )
        change_safe()

234
    add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
235
                            OUTLINE_OPACITY_TEXT, NULL, false )
236
        change_safe()
237
    add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
238
             NULL, false )
239
240
241
        change_integer_list( pi_color_values, ppsz_color_descriptions )
        change_safe()
    add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
242
             NULL, false )
243
244
245
        change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
        change_safe()

246
    add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
247
                            SHADOW_OPACITY_TEXT, NULL, false )
248
        change_safe()
249
    add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
250
             NULL, false )
251
252
253
        change_integer_list( pi_color_values, ppsz_color_descriptions )
        change_safe()
    add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
254
                          SHADOW_ANGLE_TEXT, NULL, false )
255
256
        change_safe()
    add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
257
                          SHADOW_DISTANCE_TEXT, NULL, false )
258
259
        change_safe()

260
    add_obsolete_integer( "freetype-effect" );
gbazin's avatar
   
gbazin committed
261

262
    add_bool( "freetype-yuvp", false, YUVP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
263
              YUVP_LONGTEXT, true )
264
265
266
267
    set_capability( "text renderer", 100 )
    add_shortcut( "text" )
    set_callbacks( Create, Destroy )
vlc_module_end ()
268

269
270
271
272
273

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

274
275
276
typedef struct
{
    FT_BitmapGlyph p_glyph;
277
    FT_BitmapGlyph p_outline;
278
    FT_BitmapGlyph p_shadow;
279
280
    uint32_t       i_color;             /* ARGB color */
    int            i_line_offset;       /* underline/strikethrough offset */
281
    int            i_line_thickness;    /* underline/strikethrough thickness */
282
283
} line_character_t;

284
typedef struct line_desc_t line_desc_t;
sigmunau's avatar
sigmunau committed
285
286
struct line_desc_t
{
287
    line_desc_t      *p_next;
288
289

    int              i_width;
290
    int              i_base_line;
291
292
    int              i_character_count;
    line_character_t *p_character;
293
294
};

295
296
297
298
299
300
301
302
303
304
305
typedef struct font_stack_t font_stack_t;
struct font_stack_t
{
    char          *psz_name;
    int            i_size;
    uint32_t       i_color;            /* ARGB */
    uint32_t       i_karaoke_bg_color; /* ARGB */

    font_stack_t  *p_next;
};

306
/*****************************************************************************
307
 * filter_sys_t: freetype local data
308
309
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
310
 * It describes the freetype specific properties of an output thread.
311
 *****************************************************************************/
312
struct filter_sys_t
313
314
315
{
    FT_Library     p_library;   /* handle to library     */
    FT_Face        p_face;      /* handle to face object */
316
    FT_Stroker     p_stroker;
317
318
    uint8_t        i_font_opacity;
    int            i_font_color;
319
    int            i_font_size;
320
    bool           b_font_bold;
321

322
323
324
    uint8_t        i_background_opacity;
    int            i_background_color;

325
326
327
328
    double         f_outline_thickness;
    uint8_t        i_outline_opacity;
    int            i_outline_color;

329
330
331
332
333
    float          f_shadow_vector_x;
    float          f_shadow_vector_y;
    uint8_t        i_shadow_opacity;
    int            i_shadow_color;

334
335
    int            i_default_font_size;
    int            i_display_height;
336
    char*          psz_fontfamily;
337
    xml_reader_t  *p_xml;
338
339
#ifdef WIN32
    char*          psz_win_fonts_path;
340
#endif
341
342
343

    input_attachment_t **pp_font_attachments;
    int                  i_font_attachments;
344
345
};

346
/* */
Laurent Aimar's avatar
Laurent Aimar committed
347
348
static void YUVFromRGB( uint32_t i_argb,
                    uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
349
{
Laurent Aimar's avatar
Laurent Aimar committed
350
351
352
    int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
    int i_green = ( i_argb & 0x0000ff00 ) >>  8;
    int i_blue  = ( i_argb & 0x000000ff );
353

Laurent Aimar's avatar
Laurent Aimar committed
354
355
356
357
358
359
    *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
                      802 * i_blue + 4096 + 131072 ) >> 13, 235);
    *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
                     3598 * i_blue + 4096 + 1048576) >> 13, 240);
    *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
                      -585 * i_blue + 4096 + 1048576) >> 13, 240);
360
}
361
362
363
364
365
366
367
static void RGBFromRGB( uint32_t i_argb,
                        uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
{
    *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
    *pi_g = ( i_argb & 0x0000ff00 ) >>  8;
    *pi_b = ( i_argb & 0x000000ff );
}
368
369
370
371
372
373
374
375
376
377
/*****************************************************************************
 * Make any TTF/OTF fonts present in the attachments of the media file
 * and store them for later use by the FreeType Engine
 *****************************************************************************/
static int LoadFontsFromAttachments( filter_t *p_filter )
{
    filter_sys_t         *p_sys = p_filter->p_sys;
    input_attachment_t  **pp_attachments;
    int                   i_attachments_cnt;

378
    if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
379
        return VLC_EGENERIC;
bitmap's avatar
bitmap committed
380

381
    p_sys->i_font_attachments = 0;
382
    p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
383
384
    if( !p_sys->pp_font_attachments )
        return VLC_ENOMEM;
385

386
    for( int k = 0; k < i_attachments_cnt; k++ )
387
388
389
    {
        input_attachment_t *p_attach = pp_attachments[k];

390
391
392
        if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
              !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
            p_attach->i_data > 0 && p_attach->p_data )
393
        {
394
            p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
395
396
397
398
399
400
        }
        else
        {
            vlc_input_attachment_Delete( p_attach );
        }
    }
bitmap's avatar
bitmap committed
401
    free( pp_attachments );
402

403
    return VLC_SUCCESS;
404
405
}

Laurent Aimar's avatar
Laurent Aimar committed
406
static int GetFontSize( filter_t *p_filter )
407
{
Laurent Aimar's avatar
Laurent Aimar committed
408
409
    filter_sys_t *p_sys = p_filter->p_sys;
    int           i_size = 0;
410

Laurent Aimar's avatar
Laurent Aimar committed
411
412
    if( p_sys->i_default_font_size )
    {
413
        i_size = p_sys->i_default_font_size;
Laurent Aimar's avatar
Laurent Aimar committed
414
415
416
    }
    else
    {
417
418
        int i_ratio = var_GetInteger( p_filter, "freetype-rel-fontsize" );
        if( i_ratio > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
419
        {
420
421
            i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
            p_filter->p_sys->i_display_height = p_filter->fmt_out.video.i_height;
Laurent Aimar's avatar
Laurent Aimar committed
422
423
424
425
426
        }
    }
    if( i_size <= 0 )
    {
        msg_Warn( p_filter, "invalid fontsize, using 12" );
427
        i_size = 12;
Laurent Aimar's avatar
Laurent Aimar committed
428
429
430
    }
    return i_size;
}
431

Laurent Aimar's avatar
Laurent Aimar committed
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
static int SetFontSize( filter_t *p_filter, int i_size )
{
    filter_sys_t *p_sys = p_filter->p_sys;

    if( !i_size )
    {
        i_size = GetFontSize( p_filter );

        msg_Dbg( p_filter, "using fontsize: %i", i_size );
    }

    p_sys->i_font_size = i_size;

    if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
    {
        msg_Err( p_filter, "couldn't set font size to %d", i_size );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

#ifdef HAVE_STYLES
#ifdef HAVE_FONTCONFIG
static void FontConfig_BuildCache( filter_t *p_filter )
{
    /* */
    msg_Dbg( p_filter, "Building font databases.");
    mtime_t t1, t2;
    t1 = mdate();

463
464
465
466
#ifdef __OS2__
    FcInit();
#endif

467
#if defined( WIN32 ) || defined( __APPLE__ )
Laurent Aimar's avatar
Laurent Aimar committed
468
469
470
471
472
473
474
475
476
477
478
479
    dialog_progress_bar_t *p_dialog = NULL;
    FcConfig *fcConfig = FcInitLoadConfig();

    p_dialog = dialog_ProgressCreate( p_filter,
            _("Building font cache"),
            _("Please wait while your font cache is rebuilt.\n"
                "This should take less than a few minutes."), NULL );

/*    if( p_dialog )
        dialog_ProgressSet( p_dialog, NULL, 0.5 ); */

    FcConfigBuildFonts( fcConfig );
480
481
482
483
484
485
486
487
488
#if defined( __APPLE__ )
    // By default, scan only the directory /System/Library/Fonts.
    // So build the set of available fonts under another directories,
    // and add the set to the current configuration.
    FcConfigAppFontAddDir( NULL, "~/Library/Fonts" );
    FcConfigAppFontAddDir( NULL, "/Library/Fonts" );
    FcConfigAppFontAddDir( NULL, "/Network/Library/Fonts" );
    //FcConfigAppFontAddDir( NULL, "/System/Library/Fonts" );
#endif
Laurent Aimar's avatar
Laurent Aimar committed
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
    if( p_dialog )
    {
//        dialog_ProgressSet( p_dialog, NULL, 1.0 );
        dialog_ProgressDestroy( p_dialog );
        p_dialog = NULL;
    }
#endif
    t2 = mdate();
    msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
}

/***
 * \brief Selects a font matching family, bold, italic provided
 ***/
static char* FontConfig_Select( FcConfig* config, const char* family,
                          bool b_bold, bool b_italic, int i_size, int *i_idx )
{
    FcResult result = FcResultMatch;
    FcPattern *pat, *p_pat;
    FcChar8* val_s;
    FcBool val_b;
510
    char *ret = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566

    /* Create a pattern and fills it */
    pat = FcPatternCreate();
    if (!pat) return NULL;

    /* */
    FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
    FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
    FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
    FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
    if( i_size != -1 )
    {
        char *psz_fontsize;
        if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
        {
            FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
            free( psz_fontsize );
        }
    }

    /* */
    FcDefaultSubstitute( pat );
    if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
    {
        FcPatternDestroy( pat );
        return NULL;
    }

    /* Find the best font for the pattern, destroy the pattern */
    p_pat = FcFontMatch( config, pat, &result );
    FcPatternDestroy( pat );
    if( !p_pat || result == FcResultNoMatch ) return NULL;

    /* Check the new pattern */
    if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
        || ( val_b != FcTrue ) )
    {
        FcPatternDestroy( p_pat );
        return NULL;
    }
    if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
    {
        *i_idx = 0;
    }

    if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
    {
        FcPatternDestroy( p_pat );
        return NULL;
    }

    /* if( strcasecmp((const char*)val_s, family ) != 0 )
        msg_Warn( p_filter, "fontconfig: selected font family is not"
                            "the requested one: '%s' != '%s'\n",
                            (const char*)val_s, family );   */

567
568
    if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
        ret = strdup( (const char*)val_s );
Laurent Aimar's avatar
Laurent Aimar committed
569
570

    FcPatternDestroy( p_pat );
571
    return ret;
Laurent Aimar's avatar
Laurent Aimar committed
572
573
574
575
576
577
578
579
580
581
582
583
}
#endif

#ifdef WIN32
#define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"

static int GetFileFontByName( const char *font_name, char **psz_filename )
{
    HKEY hKey;
    wchar_t vbuffer[MAX_PATH];
    wchar_t dbuffer[256];

584
585
    if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
            != ERROR_SUCCESS )
Laurent Aimar's avatar
Laurent Aimar committed
586
587
        return 1;

588
589
590
591
    MultiByteToWideChar( CP_ACP, 0, font_name, -1, dbuffer, 256 );
    char *font_name_temp = FromWide( dbuffer );
    size_t fontname_len = strlen( font_name_temp );

Laurent Aimar's avatar
Laurent Aimar committed
592
593
594
595
596
    for( int index = 0;; index++ )
    {
        DWORD vbuflen = MAX_PATH - 1;
        DWORD dbuflen = 255;

597
598
599
        LONG i_result = RegEnumValueW( hKey, index, vbuffer, &vbuflen,
                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
        if( i_result != ERROR_SUCCESS )
sebastien's avatar
sebastien committed
600
601
        {
            RegCloseKey( hKey );
602
            return i_result;
sebastien's avatar
sebastien committed
603
        }
Laurent Aimar's avatar
Laurent Aimar committed
604
605
606
607
608
609
610
611

        char *psz_value = FromWide( vbuffer );

        char *s = strchr( psz_value,'(' );
        if( s != NULL && s != psz_value ) s[-1] = '\0';

        /* Manage concatenated font names */
        if( strchr( psz_value, '&') ) {
612
613
614
            if( strcasestr( psz_value, font_name_temp ) != NULL )
            {
                free( psz_value );
Laurent Aimar's avatar
Laurent Aimar committed
615
                break;
616
            }
Laurent Aimar's avatar
Laurent Aimar committed
617
618
        }
        else {
619
620
621
            if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
            {
                free( psz_value );
Laurent Aimar's avatar
Laurent Aimar committed
622
                break;
623
            }
Laurent Aimar's avatar
Laurent Aimar committed
624
        }
625
626

        free( psz_value );
Laurent Aimar's avatar
Laurent Aimar committed
627
628
629
    }

    *psz_filename = FromWide( dbuffer );
630
    free( font_name_temp );
sebastien's avatar
sebastien committed
631
    RegCloseKey( hKey );
Laurent Aimar's avatar
Laurent Aimar committed
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
    return 0;
}


static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
                                     DWORD type, LPARAM lParam)
{
    VLC_UNUSED( metric );
    if( (type & RASTER_FONTTYPE) ) return 1;
    // if( lpelfe->elfScript ) FIXME

    return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
}

static char* Win32_Select( filter_t *p_filter, const char* family,
                           bool b_bold, bool b_italic, int i_size, int *i_idx )
{
    VLC_UNUSED( i_size );

651
    if( !family || strlen( family ) < 1 )
652
653
        goto fail;

Laurent Aimar's avatar
Laurent Aimar committed
654
655
656
657
658
659
660
    /* */
    LOGFONT lf;
    lf.lfCharSet = DEFAULT_CHARSET;
    if( b_italic )
        lf.lfItalic = true;
    if( b_bold )
        lf.lfWeight = FW_BOLD;
661
662
663
664
665
666

    char facename[32];
    wchar_t* psz_fbuffer = ToWide( family );
    WideCharToMultiByte( CP_ACP, 0, psz_fbuffer, -1, facename, 32, " ", 0 );
    strncpy( (LPSTR)&lf.lfFaceName, facename, 32 );
    free( psz_fbuffer );
Laurent Aimar's avatar
Laurent Aimar committed
667
668
669
670
671
672
673
674

    /* */
    char *psz_filename = NULL;
    HDC hDC = GetDC( NULL );
    EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
    ReleaseDC(NULL, hDC);

    /* */
675
    if( psz_filename != NULL )
676
    {
677
678
679
680
681
682
683
        /* FIXME: increase i_idx, when concatenated strings  */
        i_idx = 0;

        /* Prepend the Windows Font path, when only a filename was provided */
        if( strchr( psz_filename, DIR_SEP_CHAR ) )
            return psz_filename;
        else
684
        {
685
686
687
688
689
690
            char *psz_tmp;
            if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
            {
                free( psz_filename );
                return NULL;
            }
691
            free( psz_filename );
692
            return psz_tmp;
693
        }
694
695
    }
    else /* Let's take any font we can */
696
fail:
697
698
699
700
701
702
    {
        char *psz_tmp;
        if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
            return NULL;
        else
            return psz_tmp;
703
    }
Laurent Aimar's avatar
Laurent Aimar committed
704
}
705
#endif /* HAVE_WIN32 */
Laurent Aimar's avatar
Laurent Aimar committed
706

707
708
709
710
711
712
713
714
#ifdef __APPLE__
static char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
                          bool b_bold, bool b_italic, int i_size, int *i_idx )
{
    VLC_UNUSED( b_bold );
    VLC_UNUSED( b_italic );
    VLC_UNUSED( i_size );
    FSRef ref;
715
    unsigned char path[MAXPATHLEN];
716
717
718
719
720
721
722
    char * psz_path;

    CFStringRef  cf_fontName;
    ATSFontRef   ats_font_id;

    *i_idx = 0;

723
724
725
    if( psz_fontname == NULL )
        return NULL;

726
    msg_Dbg( p_filter, "looking for %s", psz_fontname );
727
728
    cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );

729
730
    ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );

731
    if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
732
733
    {
        msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
734
        ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
735
736
737

        if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
        {
738
739
740
741
742
743
744
745
746
            msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
            ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );

            if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
            {
                msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
                CFRelease( cf_fontName );
                return NULL;
            }
747
748
        }
    }
749
    CFRelease( cf_fontName );
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787

    if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
    {
        msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
        return NULL;
    }

    /* i_idx calculation by searching preceding fontIDs */
    /* with same FSRef                                       */
    {
        ATSFontRef  id2 = ats_font_id - 1;
        FSRef       ref2;

        while ( id2 > 0 )
        {
            if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
                break;
            if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
                break;

            id2 --;
        }
        *i_idx = ats_font_id - ( id2 + 1 );
    }

    if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
    {
        msg_Err( p_filter, "failure when getting path from FSRef" );
        return NULL;
    }
    msg_Dbg( p_filter, "found %s", path );

    psz_path = strdup( (char *)path );

    return psz_path;
}
#endif

788
#endif /* HAVE_STYLES */
Laurent Aimar's avatar
Laurent Aimar committed
789
790
791
792
793
794
795
796


/*****************************************************************************
 * RenderYUVP: place string in picture
 *****************************************************************************
 * This function merges the previously rendered freetype glyphs into a picture
 *****************************************************************************/
static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
797
798
                       line_desc_t *p_line,
                       FT_BBox *p_bbox )
Laurent Aimar's avatar
Laurent Aimar committed
799
800
801
802
803
804
805
806
807
808
809
810
{
    VLC_UNUSED(p_filter);
    static const uint8_t pi_gamma[16] =
        {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
          0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

    uint8_t *p_dst;
    video_format_t fmt;
    int i, x, y, i_pitch;
    uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */

    /* Create a new subpicture region */
811
812
813
814
815
    video_format_Init( &fmt, VLC_CODEC_YUVP );
    fmt.i_width          =
    fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
    fmt.i_height         =
    fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
816

817
    assert( !p_region->p_picture );
818
    p_region->p_picture = picture_NewFromFormat( &fmt );
819
820
    if( !p_region->p_picture )
        return VLC_EGENERIC;
821
    fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
822
    p_region->fmt = fmt;
823

824
825
    /* Calculate text color components
     * Only use the first color */
826
    int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
827
    YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
828
829

    /* Build palette */
830
    fmt.p_palette->i_entries = 16;
831
832
833
834
835
836
837
    for( i = 0; i < 8; i++ )
    {
        fmt.p_palette->palette[i][0] = 0;
        fmt.p_palette->palette[i][1] = 0x80;
        fmt.p_palette->palette[i][2] = 0x80;
        fmt.p_palette->palette[i][3] = pi_gamma[i];
        fmt.p_palette->palette[i][3] =
838
            (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
839
840
    }
    for( i = 8; i < fmt.p_palette->i_entries; i++ )
841
    {
842
        fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
843
844
        fmt.p_palette->palette[i][1] = i_u;
        fmt.p_palette->palette[i][2] = i_v;
845
        fmt.p_palette->palette[i][3] = pi_gamma[i];
846
        fmt.p_palette->palette[i][3] =
847
            (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
848
849
    }

850
851
    p_dst = p_region->p_picture->Y_PIXELS;
    i_pitch = p_region->p_picture->Y_PITCH;
852

853
    /* Initialize the region pixels */
854
    memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
855

856
    for( ; p_line != NULL; p_line = p_line->p_next )
857
    {
858
        int i_align_left = 0;
859
        if( p_line->i_width < (int)fmt.i_visible_width )
860
        {
861
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
862
                i_align_left = ( fmt.i_visible_width - p_line->i_width );
863
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
864
                i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
865
        }
866
        int i_align_top = 0;
867

868
        for( i = 0; i < p_line->i_character_count; i++ )
sigmunau's avatar
sigmunau committed
869
        {
870
871
            const line_character_t *ch = &p_line->p_character[i];
            FT_BitmapGlyph p_glyph = ch->p_glyph;
872

873
874
            int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
            int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
875

876
            for( y = 0; y < p_glyph->bitmap.rows; y++ )
sigmunau's avatar
sigmunau committed
877
            {
878
                for( x = 0; x < p_glyph->bitmap.width; x++ )
sigmunau's avatar
sigmunau committed
879
                {
880
881
882
                    if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
                        p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
                            (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
sigmunau's avatar
sigmunau committed
883
884
                }
            }
885
886
        }
    }
887

888
889
890
    /* Outlining (find something better than nearest neighbour filtering ?) */
    if( 1 )
    {
891
        uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
892
893
        uint8_t *p_top = p_dst; /* Use 1st line as a cache */
        uint8_t left, current;
894

895
896
        for( y = 1; y < (int)fmt.i_height - 1; y++ )
        {
897
            if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
898
            p_dst += p_region->p_picture->Y_PITCH;
899
900
901
            left = 0;

            for( x = 1; x < (int)fmt.i_width - 1; x++ )
sigmunau's avatar
sigmunau committed
902
            {
903
                current = p_dst[x];
904
                p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
905
                             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;
906
                left = current;
sigmunau's avatar
sigmunau committed
907
908
            }
        }
909
        memset( p_top, 0, fmt.i_width );
sigmunau's avatar
sigmunau committed
910
    }
911
912

    return VLC_SUCCESS;
sigmunau's avatar
sigmunau committed
913
}
914

915
916
917
918
919
/*****************************************************************************
 * RenderYUVA: place string in picture
 *****************************************************************************
 * This function merges the previously rendered freetype glyphs into a picture
 *****************************************************************************/
920
921
922
923
924
925
926
927
928
929
930
931
932
static void FillYUVAPicture( picture_t *p_picture,
                             int i_a, int i_y, int i_u, int i_v )
{
    memset( p_picture->p[0].p_pixels, i_y,
            p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
    memset( p_picture->p[1].p_pixels, i_u,
            p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
    memset( p_picture->p[2].p_pixels, i_v,
            p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
    memset( p_picture->p[3].p_pixels, i_a,
            p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
}

933
934
935
936
static inline void BlendYUVAPixel( picture_t *p_picture,
                                   int i_picture_x, int i_picture_y,
                                   int i_a, int i_y, int i_u, int i_v,
                                   int i_alpha )
937
{
938
    int i_an = i_a * i_alpha / 255;
939

940
941
942
943
    uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
    uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
    uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
    uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
944

945
946
    int i_ao = *p_a;
    if( i_ao == 0 )
947
    {
948
949
950
951
952
953
954
955
956
        *p_y = i_y;
        *p_u = i_u;
        *p_v = i_v;
        *p_a = i_an;
    }
    else
    {
        *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
        if( *p_a != 0 )
957
        {
958
959
960
            *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
            *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
            *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
961
        }
962
963
    }
}
964

965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
static void FillRGBAPicture( picture_t *p_picture,
                             int i_a, int i_r, int i_g, int i_b )
{
    for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
    {
        for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
        {
            uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
            p_rgba[0] = i_r;
            p_rgba[1] = i_g;
            p_rgba[2] = i_b;
            p_rgba[3] = i_a;
        }
    }
}

static inline void BlendRGBAPixel( picture_t *p_picture,
982
                                   int i_picture_x, int i_picture_y,
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
                                   int i_a, int i_r, int i_g, int i_b,
                                   int i_alpha )
{
    int i_an = i_a * i_alpha / 255;

    uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];

    int i_ao = p_rgba[3];
    if( i_ao == 0 )
    {
        p_rgba[0] = i_r;
        p_rgba[1] = i_g;
        p_rgba[2] = i_b;
        p_rgba[3] = i_an;
    }
    else
    {
        p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
        if( p_rgba[3] != 0 )
        {
            p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
            p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
            p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
        }
    }
}

static inline void BlendAXYZGlyph( picture_t *p_picture,
                                   int i_picture_x, int i_picture_y,
                                   int i_a, int i_x, int i_y, int i_z,
                                   FT_BitmapGlyph p_glyph,
                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )

1016
1017
1018
1019
{
    for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
    {
        for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
1020
1021
1022
            BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
                        i_a, i_x, i_y, i_z,
                        p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
1023
1024
1025
    }
}

1026
static inline void BlendAXYZLine( picture_t *p_picture,
1027
                                  int i_picture_x, int i_picture_y,
1028
                                  int i_a, int i_x, int i_y, int i_z,
1029
                                  const line_character_t *p_current,
1030
1031
                                  const line_character_t *p_next,
                                  void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1032
{
1033
    int i_line_width = p_current->p_glyph->bitmap.width;
1034
1035
    if( p_next )
        i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
1036

1037
    for( int dx = 0; dx < i_line_width; dx++ )
1038
    {
1039
        for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
1040
1041
1042
1043
            BlendPixel( p_picture,
                        i_picture_x + dx,
                        i_picture_y + p_current->i_line_offset + dy,
                        i_a, i_x, i_y, i_z, 0xff );
1044
1045
1046
    }
}

1047
1048
1049
1050
1051
1052
1053
1054
1055
static inline int RenderAXYZ( filter_t *p_filter,
                              subpicture_region_t *p_region,
                              line_desc_t *p_line_head,
                              FT_BBox *p_bbox,
                              int i_margin,
                              vlc_fourcc_t i_chroma,
                              void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
                              void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
                              void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1056
{
1057
    filter_sys_t *p_sys = p_filter->p_sys;
1058
1059

    /* Create a new subpicture region */
1060
1061
    const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
    const int i_text_height = p_bbox->yMax - p_bbox->yMin;
1062
    video_format_t fmt;
1063
    video_format_Init( &fmt, i_chroma );
1064
    fmt.i_width          =
1065
    fmt.i_visible_width  = i_text_width  + 2 * i_margin;
1066
    fmt.i_height         =
1067
    fmt.i_visible_height = i_text_height + 2 * i_margin;
1068

1069
    picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
1070
1071
1072
    if( !p_region->p_picture )
        return VLC_EGENERIC;
    p_region->fmt = fmt;
1073

1074
    /* Initialize the picture background */
1075
    uint8_t i_a = p_sys->i_background_opacity;
1076
1077
    uint8_t i_x, i_y, i_z;
    ExtractComponents( p_sys->i_background_color, &i_x, &i_y, &i_z );