freetype.c 87.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * freetype.c : Put text on the video, using freetype2
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002 - 2011 the VideoLAN team
5
 * $Id$
6
 *
7
 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8
 *          Gildas Bazin <gbazin@videolan.org>
9
 *          Bernie Purcell <bitmap@videolan.org>
10
 *          Jean-Baptiste Kempf <jb@videolan.org>
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
23
24
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
30
 *****************************************************************************/

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

31
32
33
34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35
#include <vlc_common.h>
36
#include <vlc_plugin.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
37
38
39
40
41
42
43
44
45
#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 */

/* Default fonts */
#ifdef __APPLE__
46
# define DEFAULT_FONT_FILE "/Library/Fonts/Arial Black.ttf"
47
# define DEFAULT_FAMILY "Arial Black"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
48
#elif defined( WIN32 )
49
# define DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
50
# define DEFAULT_FAMILY "Arial"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
51
#elif defined( HAVE_MAEMO )
52
# define DEFAULT_FONT_FILE "/usr/share/fonts/nokia/nosnb.ttf"
53
# define DEFAULT_FAMILY "Nokia Sans Bold"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
54
#else
55
# define DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
56
# define DEFAULT_FAMILY "Serif Bold"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
57
58
59
#endif

/* Freetype */
60
#include <freetype/ftsynth.h>
61
62
#include FT_FREETYPE_H
#include FT_GLYPH_H
63
64
#define FT_FLOOR(X)     ((X & -64) >> 6)
#define FT_CEIL(X)      (((X + 63) & -64) >> 6)
65
66
67
#ifndef FT_MulFix
 #define FT_MulFix(v, s) (((v)*(s))>>16)
#endif
68

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
69
/* RTL */
70
#if defined(HAVE_FRIBIDI)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
71
# include <fribidi/fribidi.h>
72
73
#endif

74
75
76
77
78
79
80
81
82
/* Win32 GDI */
#ifdef WIN32
# define _WIN32_IE 0x0500
# include <windows.h>
# include <shlobj.h>
# define HAVE_STYLES
# undef HAVE_FONTCONFIG
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
83
/* FontConfig */
84
#ifdef HAVE_FONTCONFIG
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
85
# include <fontconfig/fontconfig.h>
86
87
88
# define HAVE_STYLES
#endif

89
90
#include <assert.h>

91
/*****************************************************************************
92
 * Module descriptor
93
 *****************************************************************************/
94
95
96
static int  Create ( vlc_object_t * );
static void Destroy( vlc_object_t * );

97
#define FONT_TEXT N_("Font")
98

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

102
#define FONTSIZE_TEXT N_("Font size in pixels")
103
104
#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
105
    "If set to something different than 0 this option will override the " \
106
    "relative font size." )
107
108
109
110
111
112
113
114
115
116
117
118
#define OPACITY_TEXT N_("Opacity")
#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
119
    "relative size will be overridden." )
120

121
122
123
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") };
124
125
126
#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" )
127
#define EFFECT_TEXT N_("Font Effect")
128
#define EFFECT_LONGTEXT N_("It is possible to apply effects to the rendered " \
129
"text to improve its readability." )
130

131
132
133
134
135
enum { EFFECT_BACKGROUND  = 1,
       EFFECT_OUTLINE     = 2,
       EFFECT_OUTLINE_FAT = 3,
};
static int const pi_effects[] = { EFFECT_BACKGROUND, EFFECT_OUTLINE, EFFECT_OUTLINE_FAT };
136
137
static const char *const ppsz_effects_text[] = {
    N_("Background"),N_("Outline"), N_("Fat Outline") };
138

139
static const int pi_color_values[] = {
140
  0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
141
142
  0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
  0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
143

144
static const char *const ppsz_color_descriptions[] = {
145
146
147
  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") };
148

149
150
151
152
153
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
154

155
156
157
#ifdef HAVE_STYLES
    add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
#else
158
    add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
159
#endif
160

161
    add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
162
                 FONTSIZE_LONGTEXT, true )
163
        change_safe()
164

165
    /* opacity valid on 0..255, with default 255 = fully opaque */
166
    add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
167
        OPACITY_TEXT, OPACITY_LONGTEXT, false )
168
        change_safe()
169

170
    /* hook to the color values list, with default 0x00ffffff = white */
171
    add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
172
                 COLOR_LONGTEXT, false )
173
        change_integer_list( pi_color_values, ppsz_color_descriptions )
174
        change_safe()
175

176
    add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
177
                 FONTSIZER_LONGTEXT, false )
178
        change_integer_list( pi_sizes, ppsz_sizes_text )
179
180
        change_safe()

181
    add_integer( "freetype-effect", 2, EFFECT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
182
                 EFFECT_LONGTEXT, false )
183
        change_integer_list( pi_effects, ppsz_effects_text )
184
        change_safe()
gbazin's avatar
   
gbazin committed
185

186
    add_bool( "freetype-yuvp", false, YUVP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
187
              YUVP_LONGTEXT, true )
188
189
190
191
    set_capability( "text renderer", 100 )
    add_shortcut( "text" )
    set_callbacks( Create, Destroy )
vlc_module_end ()
192

193
194
195
196
197
198
199
200

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

/* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
static int RenderText( filter_t *, subpicture_region_t *,
                       subpicture_region_t * );
201
202

#ifdef HAVE_STYLES
203
204
static int RenderHtml( filter_t *, subpicture_region_t *,
                       subpicture_region_t * );
205
206
#endif
#ifdef HAVE_FONTCONFIG
207
static void FontConfig_BuildCache( filter_t * );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
208
static char* FontConfig_Select( FcConfig *, const char *,
209
                                bool, bool, int, int * );
210
#endif
211
#ifdef WIN32
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
212
213
static char* Win32_Select( filter_t *, const char *,
                           bool, bool, int, int * );
214
#endif
215
216
217
218
219
220
221
222
223

static int LoadFontsFromAttachments( filter_t *p_filter );

static int GetFontSize( filter_t *p_filter );
static int SetFontSize( filter_t *, int );
static void YUVFromRGB( uint32_t i_argb,
                        uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );

typedef struct line_desc_t line_desc_t;
sigmunau's avatar
sigmunau committed
224
225
struct line_desc_t
{
226
    /** NULL-terminated list of glyphs making the string */
sigmunau's avatar
sigmunau committed
227
    FT_BitmapGlyph *pp_glyphs;
228
    /** list of relative positions for the glyphs */
sigmunau's avatar
sigmunau committed
229
    FT_Vector      *p_glyph_pos;
230
231
232
233
    /** list of RGB information for styled text
     * -- if the rendering mode supports it (RenderYUVA) and
     *  b_new_color_mode is set, then it becomes possible to
     *  have multicoloured text within the subtitles. */
234
235
236
    uint32_t       *p_fg_rgb;
    uint32_t       *p_bg_rgb;
    uint8_t        *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
237
    bool            b_new_color_mode;
238
    /** underline information -- only supplied if text should be underlined */
239
    int            *pi_underline_offset;
240
    uint16_t       *pi_underline_thickness;
241

sigmunau's avatar
sigmunau committed
242
243
    int             i_height;
    int             i_width;
244
245
246
    int             i_red, i_green, i_blue;
    int             i_alpha;

sigmunau's avatar
sigmunau committed
247
    line_desc_t    *p_next;
248
};
249
static line_desc_t *NewLine( int );
250

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
251
252
253
typedef struct
{
    int         i_font_size;
254
255
    uint32_t    i_font_color;         /* ARGB */
    uint32_t    i_karaoke_bg_color;   /* ARGB */
256
257
258
259
    bool        b_italic;
    bool        b_bold;
    bool        b_underline;
    bool        b_through;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
260
261
262
    char       *psz_fontname;
} ft_style_t;

263
264
static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
static void FreeLines( line_desc_t * );
265
static void FreeLine( line_desc_t * );
266

267
/*****************************************************************************
268
 * filter_sys_t: freetype local data
269
270
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
271
 * It describes the freetype specific properties of an output thread.
272
 *****************************************************************************/
273
struct filter_sys_t
274
275
276
{
    FT_Library     p_library;   /* handle to library     */
    FT_Face        p_face;      /* handle to face object */
277
    bool           i_use_kerning;
278
279
    uint8_t        i_font_opacity;
    int            i_font_color;
280
    int            i_font_size;
281
    int            i_effect;
282
283
284

    int            i_default_font_size;
    int            i_display_height;
285
#ifdef HAVE_STYLES
286
    char*          psz_fontfamily;
287
    xml_reader_t  *p_xml;
288
289
290
#ifdef WIN32
    char*          psz_win_fonts_path;
#endif
291
#endif
292
293
294

    input_attachment_t **pp_font_attachments;
    int                  i_font_attachments;
295
296
};

297
#define UCHAR uint32_t
298
#define TR_DEFAULT_FONT p_sys->psz_fontfamily
299
300
301
302
#define TR_FONT_STYLE_PTR ft_style_t *

#include "text_renderer.h"

303
304
305
306
307
308
309
/*****************************************************************************
 * Create: allocates osd-text video thread output method
 *****************************************************************************
 * This function allocates and initializes a Clone vout method.
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
310
311
    filter_t      *p_filter = (filter_t *)p_this;
    filter_sys_t  *p_sys;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
312
    char          *psz_fontfile   = NULL;
313
    char          *psz_fontfamily = NULL;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
314
    int            i_error = 0, fontindex = 0;
315

316
    /* Allocate structure */
317
    p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
318
    if( !p_sys )
319
        return VLC_ENOMEM;
320

321
#ifdef HAVE_STYLES
322
323
    p_sys->psz_fontfamily   = NULL;
    p_sys->p_xml            = NULL;
324
#endif
325
326
327
    p_sys->p_face           = 0;
    p_sys->p_library        = 0;
    p_sys->i_font_size      = 0;
328
    p_sys->i_display_height = 0;
329

330
    var_Create( p_filter, "freetype-rel-fontsize",
331
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
332

ivoire's avatar
ivoire committed
333
334
335
336
    psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
    p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
    p_sys->i_effect = var_InheritInteger( p_filter, "freetype-effect" );
    p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
337
    p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
ivoire's avatar
ivoire committed
338
    p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
339
    p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
340

341
342
343
344
345
346
347
348
349
350
351
#ifdef WIN32
    /* Get Windows Font folder */
    wchar_t wdir[MAX_PATH];
    if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
    {
        GetWindowsDirectoryW( wdir, MAX_PATH );
        wcscat( wdir, L"\\fonts" );
    }
    p_sys->psz_win_fonts_path = FromWide( wdir );
#endif

352
    /* Set default psz_fontfamily */
353
    if( !psz_fontfamily || !*psz_fontfamily )
gbazin's avatar
   
gbazin committed
354
    {
Pierre Ynard's avatar
Pierre Ynard committed
355
        free( psz_fontfamily );
356
#ifdef HAVE_STYLES
357
        psz_fontfamily = strdup( DEFAULT_FAMILY );
gbazin's avatar
   
gbazin committed
358
#else
359
# ifdef WIN32
360
        if( asprintf( &psz_fontfamily, "%s%s", p_sys->psz_win_fonts_path, DEFAULT_FONT_FILE ) == -1 )
ivoire's avatar
ivoire committed
361
            goto error;
362
# else
363
        psz_fontfamily = strdup( DEFAULT_FONT_FILE );
364
# endif
365
        msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
gbazin's avatar
   
gbazin committed
366
#endif
367
    }
gbazin's avatar
   
gbazin committed
368

369
    /* Set the current font file */
370
#ifdef HAVE_STYLES
371
    p_sys->psz_fontfamily = psz_fontfamily;
372
#ifdef HAVE_FONTCONFIG
373
374
375
376
377
    FontConfig_BuildCache( p_filter );

    /* */
    psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
                                      p_sys->i_default_font_size, &fontindex );
378
379
380
#elif defined(WIN32)
    psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
                                 p_sys->i_default_font_size, &fontindex );
381

382
#endif
383
    msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
384
385

    /* If nothing is found, use the default family */
386
    if( !psz_fontfile )
ivoire's avatar
ivoire committed
387
        psz_fontfile = strdup( psz_fontfamily );
388

389
#else /* !HAVE_STYLES */
390
    /* Use the default file */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
391
    psz_fontfile = psz_fontfamily;
392
393
#endif

394
    /* */
395
396
397
398
399
400
    i_error = FT_Init_FreeType( &p_sys->p_library );
    if( i_error )
    {
        msg_Err( p_filter, "couldn't initialize freetype" );
        goto error;
    }
401

402
    i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
403
404
                           fontindex, &p_sys->p_face );

405
406
    if( i_error == FT_Err_Unknown_File_Format )
    {
407
408
        msg_Err( p_filter, "file %s have unknown format",
                 psz_fontfile ? psz_fontfile : "(null)" );
409
410
411
412
        goto error;
    }
    else if( i_error )
    {
413
414
        msg_Err( p_filter, "failed to load font file %s",
                 psz_fontfile ? psz_fontfile : "(null)" );
415
416
        goto error;
    }
ivoire's avatar
ivoire committed
417
418
419
#ifdef HAVE_STYLES
    free( psz_fontfile );
#endif
420
421
422
423
424
425
426
427

    i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
    if( i_error )
    {
        msg_Err( p_filter, "font has no unicode translation table" );
        goto error;
    }

428
    p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
429

430
    if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
431

432
433
434
435

    p_sys->pp_font_attachments = NULL;
    p_sys->i_font_attachments = 0;

436
    p_filter->pf_render_text = RenderText;
437
#ifdef HAVE_STYLES
438
439
440
    p_filter->pf_render_html = RenderHtml;
#else
    p_filter->pf_render_html = NULL;
441
#endif
442
443
444

    LoadFontsFromAttachments( p_filter );

445
    return VLC_SUCCESS;
446

447
error:
448
449
    if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
    if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
ivoire's avatar
ivoire committed
450
451
452
#ifdef HAVE_STYLES
    free( psz_fontfile );
#endif
ivoire's avatar
ivoire committed
453
    free( psz_fontfamily );
454
455
    free( p_sys );
    return VLC_EGENERIC;
456
457
458
459
460
461
462
463
}

/*****************************************************************************
 * Destroy: destroy Clone video thread output method
 *****************************************************************************
 * Clean up all data and library connections
 *****************************************************************************/
static void Destroy( vlc_object_t *p_this )
464
{
465
466
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys = p_filter->p_sys;
467

468
469
    if( p_sys->pp_font_attachments )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
470
        for( int k = 0; k < p_sys->i_font_attachments; k++ )
471
472
473
474
475
            vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );

        free( p_sys->pp_font_attachments );
    }

476
#ifdef HAVE_STYLES
477
    if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
478
    free( p_sys->psz_fontfamily );
479
#endif
480

481
482
483
    /* FcFini asserts calling the subfunction FcCacheFini()
     * even if no other library functions have been made since FcInit(),
     * so don't call it. */
484

485
486
487
    FT_Done_Face( p_sys->p_face );
    FT_Done_FreeType( p_sys->p_library );
    free( p_sys );
488
489
}

490
491
492
493
494
495
496
497
498
499
/*****************************************************************************
 * 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;

500
    if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
501
        return VLC_EGENERIC;
bitmap's avatar
bitmap committed
502

503
504
    p_sys->i_font_attachments = 0;
    p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
505
506
    if( !p_sys->pp_font_attachments )
        return VLC_ENOMEM;
507

508
    for( int k = 0; k < i_attachments_cnt; k++ )
509
510
511
    {
        input_attachment_t *p_attach = pp_attachments[k];

512
513
514
        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 )
515
        {
516
            p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
517
518
519
520
521
522
        }
        else
        {
            vlc_input_attachment_Delete( p_attach );
        }
    }
bitmap's avatar
bitmap committed
523
    free( pp_attachments );
524

525
    return VLC_SUCCESS;
526
527
}

528
529
530
531
532
/*****************************************************************************
 * Render: place string in picture
 *****************************************************************************
 * This function merges the previously rendered freetype glyphs into a picture
 *****************************************************************************/
533
534
static int Render( filter_t *p_filter, subpicture_region_t *p_region,
                   line_desc_t *p_line, int i_width, int i_height )
535
{
Pierre's avatar
Pierre committed
536
    VLC_UNUSED(p_filter);
537
    static const uint8_t pi_gamma[16] =
538
539
        {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
          0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
540

541
    uint8_t *p_dst;
542
543
    video_format_t fmt;
    int i, x, y, i_pitch;
544
545
    uint8_t i_y; /* YUV values, derived from incoming RGB */
    int8_t i_u, i_v;
546
547
548

    /* Create a new subpicture region */
    memset( &fmt, 0, sizeof(video_format_t) );
549
    fmt.i_chroma = VLC_CODEC_YUVP;
550
551
    fmt.i_width = fmt.i_visible_width = i_width + 4;
    fmt.i_height = fmt.i_visible_height = i_height + 4;
552
553
554
555
    if( p_region->fmt.i_visible_width > 0 )
        fmt.i_visible_width = p_region->fmt.i_visible_width;
    if( p_region->fmt.i_visible_height > 0 )
        fmt.i_visible_height = p_region->fmt.i_visible_height;
556
    fmt.i_x_offset = fmt.i_y_offset = 0;
557
558
    fmt.i_sar_num = 1;
    fmt.i_sar_den = 1;
559

560
    assert( !p_region->p_picture );
561
    p_region->p_picture = picture_NewFromFormat( &fmt );
562
563
    if( !p_region->p_picture )
        return VLC_EGENERIC;
564
    fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
565
    p_region->fmt = fmt;
566
567
568
569
570
571
572
573
574
575

    /* Calculate text color components */
    i_y = (uint8_t)(( 66 * p_line->i_red  + 129 * p_line->i_green +
                      25 * p_line->i_blue + 128) >> 8) +  16;
    i_u = (int8_t)(( -38 * p_line->i_red  -  74 * p_line->i_green +
                     112 * p_line->i_blue + 128) >> 8) + 128;
    i_v = (int8_t)(( 112 * p_line->i_red  -  94 * p_line->i_green -
                      18 * p_line->i_blue + 128) >> 8) + 128;

    /* Build palette */
576
    fmt.p_palette->i_entries = 16;
577
578
579
580
581
582
583
584
585
586
    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] =
            (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
    }
    for( i = 8; i < fmt.p_palette->i_entries; i++ )
587
    {
588
        fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
589
590
        fmt.p_palette->palette[i][1] = i_u;
        fmt.p_palette->palette[i][2] = i_v;
591
        fmt.p_palette->palette[i][3] = pi_gamma[i];
592
        fmt.p_palette->palette[i][3] =
593
            (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
594
595
    }

596
597
    p_dst = p_region->p_picture->Y_PIXELS;
    i_pitch = p_region->p_picture->Y_PITCH;
598

599
    /* Initialize the region pixels */
600
    memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
601

602
    for( ; p_line != NULL; p_line = p_line->p_next )
603
604
    {
        int i_glyph_tmax = 0;
605
        int i_bitmap_offset, i_offset, i_align_offset = 0;
sigmunau's avatar
sigmunau committed
606
607
608
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
609
            i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
sigmunau's avatar
sigmunau committed
610
        }
611

612
        if( p_line->i_width < i_width )
613
        {
614
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
615
616
617
            {
                i_align_offset = i_width - p_line->i_width;
            }
618
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
619
620
621
622
            {
                i_align_offset = ( i_width - p_line->i_width ) / 2;
            }
        }
623

sigmunau's avatar
sigmunau committed
624
625
626
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
627
628

            i_offset = ( p_line->p_glyph_pos[ i ].y +
629
630
                i_glyph_tmax - p_glyph->top + 2 ) *
                i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
631
                i_align_offset;
632
633

            for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
sigmunau's avatar
sigmunau committed
634
            {
635
                for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
sigmunau's avatar
sigmunau committed
636
                {
637
638
639
                    if( p_glyph->bitmap.buffer[i_bitmap_offset] )
                        p_dst[i_offset+x] =
                         ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
sigmunau's avatar
sigmunau committed
640
                }
641
                i_offset += i_pitch;
sigmunau's avatar
sigmunau committed
642
            }
643
644
        }
    }
645

646
647
648
    /* Outlining (find something better than nearest neighbour filtering ?) */
    if( 1 )
    {
649
        uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
650
651
        uint8_t *p_top = p_dst; /* Use 1st line as a cache */
        uint8_t left, current;
652

653
654
        for( y = 1; y < (int)fmt.i_height - 1; y++ )
        {
655
            if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
656
            p_dst += p_region->p_picture->Y_PITCH;
657
658
659
            left = 0;

            for( x = 1; x < (int)fmt.i_width - 1; x++ )
sigmunau's avatar
sigmunau committed
660
            {
661
                current = p_dst[x];
662
                p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
663
                             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;
664
                left = current;
sigmunau's avatar
sigmunau committed
665
666
            }
        }
667
        memset( p_top, 0, fmt.i_width );
sigmunau's avatar
sigmunau committed
668
    }
669
670

    return VLC_SUCCESS;
sigmunau's avatar
sigmunau committed
671
}
672

673
static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
674
675
676
                                FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
                                FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
                                int i_glyph_tmax, int i_align_offset,
Rafaël Carré's avatar
Rafaël Carré committed
677
                                uint8_t i_y, uint8_t i_u, uint8_t i_v,
678
679
680
681
682
                                subpicture_region_t *p_region)
{
    int i_pitch;
    uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;

683
684
685
686
687
    p_dst_y = p_region->p_picture->Y_PIXELS;
    p_dst_u = p_region->p_picture->U_PIXELS;
    p_dst_v = p_region->p_picture->V_PIXELS;
    p_dst_a = p_region->p_picture->A_PIXELS;
    i_pitch = p_region->p_picture->A_PITCH;
688
689
690
691

    int i_offset = ( p_this_glyph_pos->y + i_glyph_tmax + i_line_offset + 3 ) * i_pitch +
                     p_this_glyph_pos->x + p_this_glyph->left + 3 + i_align_offset;

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
692
    for( int y = 0; y < i_line_thickness; y++ )
693
694
    {
        int i_extra = p_this_glyph->bitmap.width;
695

696
697
698
699
700
        if( b_ul_next_char )
        {
            i_extra = (p_next_glyph_pos->x + p_next_glyph->left) -
                      (p_this_glyph_pos->x + p_this_glyph->left);
        }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
701
        for( int x = 0; x < i_extra; x++ )
702
        {
703
            bool b_ok = true;
704
705

            /* break the underline around the tails of any glyphs which cross it */
706
            /* Strikethrough doesn't get broken */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
707
            for( int z = x - i_line_thickness;
708
                 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
709
710
711
712
713
714
715
716
717
                 z++ )
            {
                if( p_next_glyph && ( z >= i_extra ) )
                {
                    int i_row = i_line_offset + p_next_glyph->top + y;

                    if( ( p_next_glyph->bitmap.rows > i_row ) &&
                        p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
                    {
718
                        b_ok = false;
719
720
721
722
723
724
725
726
727
                    }
                }
                else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
                {
                    int i_row = i_line_offset + p_this_glyph->top + y;

                    if( ( p_this_glyph->bitmap.rows > i_row ) &&
                        p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
                    {
728
                        b_ok = false;
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
                    }
                }
            }

            if( b_ok )
            {
                p_dst_y[i_offset+x] = (i_y * 255) >> 8;
                p_dst_u[i_offset+x] = i_u;
                p_dst_v[i_offset+x] = i_v;
                p_dst_a[i_offset+x] = 255;
            }
        }
        i_offset += i_pitch;
    }
}

745
746
static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
{
747
748
    uint8_t *p_dst = p_region->p_picture->A_PIXELS;
    int i_pitch = p_region->p_picture->A_PITCH;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
749
    int y;
750
751
752
753
754
755
756
757
758
759
760

    for( ; p_line != NULL; p_line = p_line->p_next )
    {
        int i_glyph_tmax=0, i = 0;
        int i_bitmap_offset, i_offset, i_align_offset = 0;
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
            i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
        }

761
        if( p_line->i_width < i_width )
762
        {
763
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
764
765
766
            {
                i_align_offset = i_width - p_line->i_width;
            }
767
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
            {
                i_align_offset = ( i_width - p_line->i_width ) / 2;
            }
        }

        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];

            i_offset = ( p_line->p_glyph_pos[ i ].y +
                i_glyph_tmax - p_glyph->top + 3 + yoffset ) *
                i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
                i_align_offset +xoffset;

            for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
            {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
784
                for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
                {
                    if( p_glyph->bitmap.buffer[i_bitmap_offset] )
                        if( p_dst[i_offset+x] <
                            ((int)p_glyph->bitmap.buffer[i_bitmap_offset]) )
                            p_dst[i_offset+x] =
                                ((int)p_glyph->bitmap.buffer[i_bitmap_offset]);
                }
                i_offset += i_pitch;
            }
        }
    }
}

/*****************************************************************************
 * Render: place string in picture
 *****************************************************************************
 * This function merges the previously rendered freetype glyphs into a picture
 *****************************************************************************/
static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
                   line_desc_t *p_line, int i_width, int i_height )
{
    uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
    video_format_t fmt;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
808
    int i, y, i_pitch, i_alpha;
809
    uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
810

811
812
813
    if( i_width == 0 || i_height == 0 )
        return VLC_SUCCESS;

814
815
    /* Create a new subpicture region */
    memset( &fmt, 0, sizeof(video_format_t) );
816
    fmt.i_chroma = VLC_CODEC_YUVA;
817
818
    fmt.i_width = fmt.i_visible_width = i_width + 6;
    fmt.i_height = fmt.i_visible_height = i_height + 6;
819
820
821
822
    if( p_region->fmt.i_visible_width > 0 )
        fmt.i_visible_width = p_region->fmt.i_visible_width;
    if( p_region->fmt.i_visible_height > 0 )
        fmt.i_visible_height = p_region->fmt.i_visible_height;
823
    fmt.i_x_offset = fmt.i_y_offset = 0;
824
825
    fmt.i_sar_num = 1;
    fmt.i_sar_den = 1;
826

827
    p_region->p_picture = picture_NewFromFormat( &fmt );
828
829
830
    if( !p_region->p_picture )
        return VLC_EGENERIC;
    p_region->fmt = fmt;
831
832

    /* Calculate text color components */
833
834
835
836
    YUVFromRGB( (p_line->i_red   << 16) |
                (p_line->i_green <<  8) |
                (p_line->i_blue       ),
                &i_y, &i_u, &i_v);
837
    i_alpha = p_line->i_alpha;
838

839
840
841
842
843
    p_dst_y = p_region->p_picture->Y_PIXELS;
    p_dst_u = p_region->p_picture->U_PIXELS;
    p_dst_v = p_region->p_picture->V_PIXELS;
    p_dst_a = p_region->p_picture->A_PIXELS;
    i_pitch = p_region->p_picture->A_PITCH;
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868

    /* Initialize the region pixels */
    if( p_filter->p_sys->i_effect != EFFECT_BACKGROUND )
    {
        memset( p_dst_y, 0x00, i_pitch * p_region->fmt.i_height );
        memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
        memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
        memset( p_dst_a, 0, i_pitch * p_region->fmt.i_height );
    }
    else
    {
        memset( p_dst_y, 0x0, i_pitch * p_region->fmt.i_height );
        memset( p_dst_u, 0x80, i_pitch * p_region->fmt.i_height );
        memset( p_dst_v, 0x80, i_pitch * p_region->fmt.i_height );
        memset( p_dst_a, 0x80, i_pitch * p_region->fmt.i_height );
    }
    if( p_filter->p_sys->i_effect == EFFECT_OUTLINE ||
        p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
    {
        DrawBlack( p_line, i_width, p_region,  0,  0);
        DrawBlack( p_line, i_width, p_region, -1,  0);
        DrawBlack( p_line, i_width, p_region,  0, -1);
        DrawBlack( p_line, i_width, p_region,  1,  0);
        DrawBlack( p_line, i_width, p_region,  0,  1);
    }
869

870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
    if( p_filter->p_sys->i_effect == EFFECT_OUTLINE_FAT )
    {
        DrawBlack( p_line, i_width, p_region, -1, -1);
        DrawBlack( p_line, i_width, p_region, -1,  1);
        DrawBlack( p_line, i_width, p_region,  1, -1);
        DrawBlack( p_line, i_width, p_region,  1,  1);

        DrawBlack( p_line, i_width, p_region, -2,  0);
        DrawBlack( p_line, i_width, p_region,  0, -2);
        DrawBlack( p_line, i_width, p_region,  2,  0);
        DrawBlack( p_line, i_width, p_region,  0,  2);

        DrawBlack( p_line, i_width, p_region, -2, -2);
        DrawBlack( p_line, i_width, p_region, -2,  2);
        DrawBlack( p_line, i_width, p_region,  2, -2);
        DrawBlack( p_line, i_width, p_region,  2,  2);

        DrawBlack( p_line, i_width, p_region, -3,  0);
        DrawBlack( p_line, i_width, p_region,  0, -3);
        DrawBlack( p_line, i_width, p_region,  3,  0);
        DrawBlack( p_line, i_width, p_region,  0,  3);
    }

    for( ; p_line != NULL; p_line = p_line->p_next )
    {
        int i_glyph_tmax = 0;
        int i_bitmap_offset, i_offset, i_align_offset = 0;
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
            i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
        }

903
        if( p_line->i_width < i_width )
904
        {
905
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
906
907
908
            {
                i_align_offset = i_width - p_line->i_width;
            }
909
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
910
911
912
913
914
915
916
917
918
919
920
921
922
923
            {
                i_align_offset = ( i_width - p_line->i_width ) / 2;
            }
        }

        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];

            i_offset = ( p_line->p_glyph_pos[ i ].y +
                i_glyph_tmax - p_glyph->top + 3 ) *
                i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 3 +
                i_align_offset;

924
925
926
            if( p_line->b_new_color_mode )
            {
                /* Every glyph can (and in fact must) have its own color */
927
                YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
928
929
            }

930
931
            for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
            {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
932
                for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
933
                {
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
                    uint8_t i_y_local = i_y;
                    uint8_t i_u_local = i_u;
                    uint8_t i_v_local = i_v;

                    if( p_line->p_fg_bg_ratio != 0x00 )
                    {
                        int i_split = p_glyph->bitmap.width *
                                      p_line->p_fg_bg_ratio[ i ] / 0x7f;

                        if( x > i_split )
                        {
                            YUVFromRGB( p_line->p_bg_rgb[ i ],
                                        &i_y_local, &i_u_local, &i_v_local );
                        }
                    }