freetype.c 105 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
52
53
# define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
# define SYSTEM_DEFAULT_FAMILY "Arial Unicode MS"
# define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/System/Library/Fonts/Monaco.dfont"
# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monaco"
54
#elif defined( _WIN32 )
55
56
57
58
# define SYSTEM_DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
# define SYSTEM_DEFAULT_FAMILY "Arial"
# define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "cour.ttf"
# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Courier New"
59
#elif defined( __OS2__ )
60
61
62
63
# define SYSTEM_DEFAULT_FONT_FILE "/psfonts/tnrwt_k.ttf"
# define SYSTEM_DEFAULT_FAMILY "Times New Roman WT K"
# define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/psfonts/mtsansdk.ttf"
# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monotype Sans Duospace WT K"
64
#elif defined( __ANDROID__ )
65
66
67
68
# define SYSTEM_DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
# define SYSTEM_DEFAULT_FAMILY "Droid Sans Bold"
# define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/system/fonts/DroidSansMono.ttf"
# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Droid Sans Mono"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
69
#else
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# define SYSTEM_DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
# define SYSTEM_DEFAULT_FAMILY "Serif Bold"
# define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeMono.ttf"
# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monospace"
#endif

#ifndef DEFAULT_FONT_FILE
#define DEFAULT_FONT_FILE SYSTEM_DEFAULT_FONT_FILE
#endif

#ifndef DEFAULT_FAMILY
#define DEFAULT_FAMILY SYSTEM_DEFAULT_FAMILY
#endif

#ifndef DEFAULT_MONOSPACE_FONT_FILE
#define DEFAULT_MONOSPACE_FONT_FILE SYSTEM_DEFAULT_MONOSPACE_FONT_FILE
#endif

#ifndef DEFAULT_MONOSPACE_FAMILY
#define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
90
91
92
#endif

/* Freetype */
93
#include <freetype/ftsynth.h>
94
95
#include FT_FREETYPE_H
#include FT_GLYPH_H
96
97
#include FT_STROKER_H

98
99
#define FT_FLOOR(X)     ((X & -64) >> 6)
#define FT_CEIL(X)      (((X + 63) & -64) >> 6)
100
101
102
#ifndef FT_MulFix
 #define FT_MulFix(v, s) (((v)*(s))>>16)
#endif
103

104
105
/* apple stuff */
#ifdef __APPLE__
106
107
#include <TargetConditionals.h>
#if !TARGET_OS_IPHONE
108
#include <Carbon/Carbon.h>
109
#endif
110
#include <sys/param.h>                         /* for MAXPATHLEN */
111
112
113
114
#undef HAVE_FONTCONFIG
#define HAVE_STYLES
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
115
/* RTL */
116
#if defined(HAVE_FRIBIDI)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
117
# include <fribidi/fribidi.h>
118
119
#endif

120
/* Win32 GDI */
121
#ifdef _WIN32
122
123
124
125
126
127
# include <windows.h>
# include <shlobj.h>
# define HAVE_STYLES
# undef HAVE_FONTCONFIG
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
128
/* FontConfig */
129
#ifdef HAVE_FONTCONFIG
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
130
# include <fontconfig/fontconfig.h>
131
132
133
# define HAVE_STYLES
#endif

134
135
#include <assert.h>

136
137
138
139
140
141
142
143
144
145
146
147
#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

148
/*****************************************************************************
149
 * Module descriptor
150
 *****************************************************************************/
151
152
153
static int  Create ( vlc_object_t * );
static void Destroy( vlc_object_t * );

154
#define FONT_TEXT N_("Font")
155
#define MONOSPACE_FONT_TEXT N_("Monospace Font")
156

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

160
#define FONTSIZE_TEXT N_("Font size in pixels")
161
162
#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
163
    "If set to something different than 0 this option will override the " \
164
    "relative font size." )
165
#define OPACITY_TEXT N_("Text opacity")
166
167
168
169
170
171
172
173
174
175
176
#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
177
    "relative size will be overridden." )
178
#define BOLD_TEXT N_("Force bold")
179

180
181
182
#define BG_OPACITY_TEXT N_("Background opacity")
#define BG_COLOR_TEXT N_("Background color")

183
184
185
186
#define OUTLINE_OPACITY_TEXT N_("Outline opacity")
#define OUTLINE_COLOR_TEXT N_("Outline color")
#define OUTLINE_THICKNESS_TEXT N_("Outline thickness")

187
188
189
190
191
192
#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")


193
194
195
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") };
196
197
198
#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" )
199

200
static const int pi_color_values[] = {
201
  0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
202
203
  0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
  0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
204

205
static const char *const ppsz_color_descriptions[] = {
206
207
208
  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") };
209

210
211
212
213
214
215
216
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"),
};

217
218
219
220
221
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
222

223
224
#ifdef HAVE_STYLES
    add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
225
    add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
226
#else
227
    add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
228
    add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
229
#endif
230

231
    add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
232
                 FONTSIZE_LONGTEXT, true )
VLC_help's avatar
VLC_help committed
233
        change_integer_range( 0, 4096)
234
        change_safe()
235

236
237
238
239
240
    add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
                 FONTSIZER_LONGTEXT, false )
        change_integer_list( pi_sizes, ppsz_sizes_text )
        change_safe()

241
    /* opacity valid on 0..255, with default 255 = fully opaque */
242
    add_integer_with_range( "freetype-opacity", 255, 0, 255,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
243
        OPACITY_TEXT, OPACITY_LONGTEXT, false )
244
        change_safe()
245

246
    /* hook to the color values list, with default 0x00ffffff = white */
247
    add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
248
                 COLOR_LONGTEXT, false )
249
        change_integer_list( pi_color_values, ppsz_color_descriptions )
250
        change_safe()
251

252
    add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
253
254
        change_safe()

255
    add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
256
                            BG_OPACITY_TEXT, NULL, false )
257
        change_safe()
258
    add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
259
             NULL, false )
260
261
262
        change_integer_list( pi_color_values, ppsz_color_descriptions )
        change_safe()

263
    add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
264
                            OUTLINE_OPACITY_TEXT, NULL, false )
265
        change_safe()
266
    add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
267
             NULL, false )
268
269
270
        change_integer_list( pi_color_values, ppsz_color_descriptions )
        change_safe()
    add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
271
             NULL, false )
272
273
274
        change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
        change_safe()

275
    add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
276
                            SHADOW_OPACITY_TEXT, NULL, false )
277
        change_safe()
278
    add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
279
             NULL, false )
280
281
282
        change_integer_list( pi_color_values, ppsz_color_descriptions )
        change_safe()
    add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
283
                          SHADOW_ANGLE_TEXT, NULL, false )
284
285
        change_safe()
    add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
286
                          SHADOW_DISTANCE_TEXT, NULL, false )
287
288
        change_safe()

289
    add_obsolete_integer( "freetype-effect" );
gbazin's avatar
   
gbazin committed
290

291
    add_bool( "freetype-yuvp", false, YUVP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
292
              YUVP_LONGTEXT, true )
293
294
295
296
    set_capability( "text renderer", 100 )
    add_shortcut( "text" )
    set_callbacks( Create, Destroy )
vlc_module_end ()
297

298
299
300
301
302

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

303
304
305
typedef struct
{
    FT_BitmapGlyph p_glyph;
306
    FT_BitmapGlyph p_outline;
307
    FT_BitmapGlyph p_shadow;
308
309
    uint32_t       i_color;             /* ARGB color */
    int            i_line_offset;       /* underline/strikethrough offset */
310
    int            i_line_thickness;    /* underline/strikethrough thickness */
311
312
} line_character_t;

313
typedef struct line_desc_t line_desc_t;
sigmunau's avatar
sigmunau committed
314
315
struct line_desc_t
{
316
    line_desc_t      *p_next;
317
318

    int              i_width;
319
    int              i_height;
320
    int              i_base_line;
321
322
    int              i_character_count;
    line_character_t *p_character;
323
324
};

325
326
327
328
329
330
331
332
333
334
335
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;
};

336
/*****************************************************************************
337
 * filter_sys_t: freetype local data
338
339
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
340
 * It describes the freetype specific properties of an output thread.
341
 *****************************************************************************/
342
struct filter_sys_t
343
344
345
{
    FT_Library     p_library;   /* handle to library     */
    FT_Face        p_face;      /* handle to face object */
346
    FT_Stroker     p_stroker;
347
    uint8_t        i_font_opacity;
348
    int            i_font_size;
349
    bool           b_font_bold;
350

351
352
353
    uint8_t        i_outline_opacity;
    int            i_outline_color;

354
355
356
357
358
    float          f_shadow_vector_x;
    float          f_shadow_vector_y;
    uint8_t        i_shadow_opacity;
    int            i_shadow_color;

359
360
    int            i_default_font_size;
    int            i_display_height;
361
    char*          psz_fontfamily;
362
    char*          psz_monofontfamily;
363
    xml_reader_t  *p_xml;
364
#ifdef _WIN32
365
    char*          psz_win_fonts_path;
366
#endif
367
368
369

    input_attachment_t **pp_font_attachments;
    int                  i_font_attachments;
370
371
};

372
/* */
Laurent Aimar's avatar
Laurent Aimar committed
373
374
static void YUVFromRGB( uint32_t i_argb,
                    uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
375
{
Laurent Aimar's avatar
Laurent Aimar committed
376
377
378
    int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
    int i_green = ( i_argb & 0x0000ff00 ) >>  8;
    int i_blue  = ( i_argb & 0x000000ff );
379

Laurent Aimar's avatar
Laurent Aimar committed
380
381
382
383
384
385
    *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);
386
}
387
388
389
390
391
392
393
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 );
}
394
395
396
397
398
399
400
401
402
403
/*****************************************************************************
 * 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;

404
    if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
405
        return VLC_EGENERIC;
bitmap's avatar
bitmap committed
406

407
    p_sys->i_font_attachments = 0;
408
    p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
409
410
    if( !p_sys->pp_font_attachments )
        return VLC_ENOMEM;
411

412
    for( int k = 0; k < i_attachments_cnt; k++ )
413
414
415
    {
        input_attachment_t *p_attach = pp_attachments[k];

416
417
418
        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 )
419
        {
420
            p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
421
422
423
424
425
426
        }
        else
        {
            vlc_input_attachment_Delete( p_attach );
        }
    }
bitmap's avatar
bitmap committed
427
    free( pp_attachments );
428

429
    return VLC_SUCCESS;
430
431
}

Laurent Aimar's avatar
Laurent Aimar committed
432
static int GetFontSize( filter_t *p_filter )
433
{
Laurent Aimar's avatar
Laurent Aimar committed
434
435
    filter_sys_t *p_sys = p_filter->p_sys;
    int           i_size = 0;
436

Laurent Aimar's avatar
Laurent Aimar committed
437
438
    if( p_sys->i_default_font_size )
    {
439
        i_size = p_sys->i_default_font_size;
Laurent Aimar's avatar
Laurent Aimar committed
440
441
442
    }
    else
    {
443
        int i_ratio = var_InheritInteger( p_filter, "freetype-rel-fontsize" );
444
        if( i_ratio > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
445
        {
446
447
            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
448
449
450
451
452
        }
    }
    if( i_size <= 0 )
    {
        msg_Warn( p_filter, "invalid fontsize, using 12" );
453
        i_size = 12;
Laurent Aimar's avatar
Laurent Aimar committed
454
455
456
    }
    return i_size;
}
457

Laurent Aimar's avatar
Laurent Aimar committed
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
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();

489
490
491
492
#ifdef __OS2__
    FcInit();
#endif

493
#if defined( _WIN32 ) || defined( __APPLE__ )
Laurent Aimar's avatar
Laurent Aimar committed
494
495
496
497
498
499
500
501
502
503
504
505
    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 );
506
507
508
509
510
511
512
513
514
#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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
    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;
536
    char *ret = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
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
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592

    /* 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 );   */

593
594
    if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
        ret = strdup( (const char*)val_s );
Laurent Aimar's avatar
Laurent Aimar committed
595
596

    FcPatternDestroy( p_pat );
597
    return ret;
Laurent Aimar's avatar
Laurent Aimar committed
598
599
600
}
#endif

601
#ifdef _WIN32
602
#define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
Laurent Aimar's avatar
Laurent Aimar committed
603

604
static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
Laurent Aimar's avatar
Laurent Aimar committed
605
606
{
    HKEY hKey;
607
608
    TCHAR vbuffer[MAX_PATH];
    TCHAR dbuffer[256];
Laurent Aimar's avatar
Laurent Aimar committed
609

610
    if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
611
            != ERROR_SUCCESS )
Laurent Aimar's avatar
Laurent Aimar committed
612
613
        return 1;

614
    char *font_name_temp = FromT( font_name );
615
616
    size_t fontname_len = strlen( font_name_temp );

Laurent Aimar's avatar
Laurent Aimar committed
617
618
619
620
621
    for( int index = 0;; index++ )
    {
        DWORD vbuflen = MAX_PATH - 1;
        DWORD dbuflen = 255;

622
623
        LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
                                      NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
624
        if( i_result != ERROR_SUCCESS )
sebastien's avatar
sebastien committed
625
626
        {
            RegCloseKey( hKey );
627
            return i_result;
sebastien's avatar
sebastien committed
628
        }
Laurent Aimar's avatar
Laurent Aimar committed
629

630
        char *psz_value = FromT( vbuffer );
Laurent Aimar's avatar
Laurent Aimar committed
631
632
633
634
635
636

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

        /* Manage concatenated font names */
        if( strchr( psz_value, '&') ) {
637
638
639
            if( strcasestr( psz_value, font_name_temp ) != NULL )
            {
                free( psz_value );
Laurent Aimar's avatar
Laurent Aimar committed
640
                break;
641
            }
Laurent Aimar's avatar
Laurent Aimar committed
642
643
        }
        else {
644
645
646
            if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
            {
                free( psz_value );
Laurent Aimar's avatar
Laurent Aimar committed
647
                break;
648
            }
Laurent Aimar's avatar
Laurent Aimar committed
649
        }
650
651

        free( psz_value );
Laurent Aimar's avatar
Laurent Aimar committed
652
653
    }

654
    *psz_filename = FromT( dbuffer );
655
    free( font_name_temp );
sebastien's avatar
sebastien committed
656
    RegCloseKey( hKey );
Laurent Aimar's avatar
Laurent Aimar committed
657
658
659
660
661
662
663
664
665
666
667
    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

668
    return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
Laurent Aimar's avatar
Laurent Aimar committed
669
670
671
672
673
674
675
}

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 );

676
    if( !family || strlen( family ) < 1 )
677
678
        goto fail;

Laurent Aimar's avatar
Laurent Aimar committed
679
680
681
682
683
684
685
    /* */
    LOGFONT lf;
    lf.lfCharSet = DEFAULT_CHARSET;
    if( b_italic )
        lf.lfItalic = true;
    if( b_bold )
        lf.lfWeight = FW_BOLD;
686

687
688
    LPTSTR psz_fbuffer = ToT( family );
    _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
689
    free( psz_fbuffer );
Laurent Aimar's avatar
Laurent Aimar committed
690
691
692
693
694
695
696
697

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

    /* */
698
    if( psz_filename != NULL )
699
    {
700
701
702
703
704
705
706
        /* 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
707
        {
708
709
710
711
712
713
            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;
            }
714
            free( psz_filename );
715
            return psz_tmp;
716
        }
717
718
    }
    else /* Let's take any font we can */
719
fail:
720
721
722
723
724
725
    {
        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;
726
    }
Laurent Aimar's avatar
Laurent Aimar committed
727
}
728
#endif /* _WIN32 */
Laurent Aimar's avatar
Laurent Aimar committed
729

730
#ifdef __APPLE__
731
#if !TARGET_OS_IPHONE
732
733
734
735
736
737
738
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;
739
    unsigned char path[MAXPATHLEN];
740
741
742
743
744
745
746
    char * psz_path;

    CFStringRef  cf_fontName;
    ATSFontRef   ats_font_id;

    *i_idx = 0;

747
748
749
    if( psz_fontname == NULL )
        return NULL;

750
    msg_Dbg( p_filter, "looking for %s", psz_fontname );
751
752
    cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );

753
754
    ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );

755
    if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
756
757
    {
        msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
758
        ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
759
760
761

        if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
        {
762
763
764
765
766
767
768
769
770
            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;
            }
771
772
        }
    }
773
    CFRelease( cf_fontName );
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810

    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
811
#endif
812

813
#endif /* HAVE_STYLES */
Laurent Aimar's avatar
Laurent Aimar committed
814
815
816
817
818
819
820
821


/*****************************************************************************
 * 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,
822
823
                       line_desc_t *p_line,
                       FT_BBox *p_bbox )
Laurent Aimar's avatar
Laurent Aimar committed
824
825
826
827
828
829
830
831
832
833
834
835
{
    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 */
836
837
838
839
840
    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;
841

842
    assert( !p_region->p_picture );
843
    p_region->p_picture = picture_NewFromFormat( &fmt );
844
845
    if( !p_region->p_picture )
        return VLC_EGENERIC;
846
    fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
847
    p_region->fmt = fmt;
848

849
850
    /* Calculate text color components
     * Only use the first color */
851
    int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
852
    YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
853
854

    /* Build palette */
855
    fmt.p_palette->i_entries = 16;
856
857
858
859
860
861
862
    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] =
863
            (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
864
865
    }
    for( i = 8; i < fmt.p_palette->i_entries; i++ )
866
    {
867
        fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
868
869
        fmt.p_palette->palette[i][1] = i_u;
        fmt.p_palette->palette[i][2] = i_v;
870
        fmt.p_palette->palette[i][3] = pi_gamma[i];
871
        fmt.p_palette->palette[i][3] =
872
            (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
873
874
    }

875
876
    p_dst = p_region->p_picture->Y_PIXELS;
    i_pitch = p_region->p_picture->Y_PITCH;
877

878
    /* Initialize the region pixels */
879
    memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
880

881
    for( ; p_line != NULL; p_line = p_line->p_next )
882
    {
883
        int i_align_left = 0;
884
        if( p_line->i_width < (int)fmt.i_visible_width )
885
        {
886
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
887
                i_align_left = ( fmt.i_visible_width - p_line->i_width );
888
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
889
                i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
890
        }
891
        int i_align_top = 0;
892

893
        for( i = 0; i < p_line->i_character_count; i++ )
sigmunau's avatar
sigmunau committed
894
        {
895
896
            const line_character_t *ch = &p_line->p_character[i];
            FT_BitmapGlyph p_glyph = ch->p_glyph;
897

898
899
            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;
900

901
            for( y = 0; y < p_glyph->bitmap.rows; y++ )
sigmunau's avatar
sigmunau committed
902
            {
903
                for( x = 0; x < p_glyph->bitmap.width; x++ )
sigmunau's avatar
sigmunau committed
904
                {
905
906
907
                    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
908
909
                }
            }
910
911
        }
    }
912

913
914
915
    /* Outlining (find something better than nearest neighbour filtering ?) */
    if( 1 )
    {
916
        uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
917
918
        uint8_t *p_top = p_dst; /* Use 1st line as a cache */
        uint8_t left, current;
919

920
921
        for( y = 1; y < (int)fmt.i_height - 1; y++ )
        {
922
            if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
923
            p_dst += p_region->p_picture->Y_PITCH;
924
925
926
            left = 0;

            for( x = 1; x < (int)fmt.i_width - 1; x++ )
sigmunau's avatar
sigmunau committed
927
            {
928
                current = p_dst[x];
929
                p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
930
                             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;
931
                left = current;
sigmunau's avatar
sigmunau committed
932
933
            }
        }
934
        memset( p_top, 0, fmt.i_width );
sigmunau's avatar
sigmunau committed
935
    }
936
937

    return VLC_SUCCESS;
sigmunau's avatar
sigmunau committed
938
}
939

940
941
942
943
944
/*****************************************************************************
 * RenderYUVA: place string in picture
 *****************************************************************************
 * This function merges the previously rendered freetype glyphs into a picture
 *****************************************************************************/
945
946
947
948
949
950
951
952
953
954
955
956
957
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 );
}

958
959
960
961
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 )
962
{
963
    int i_an = i_a * i_alpha / 255;
964

965
966
967
968
    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];
969

970
971
    int i_ao = *p_a;
    if( i_ao == 0 )
972
    {
973
974
975
976
977
978
979
980
981
        *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 )
982
        {
983
984
985
            *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;
986
        }
987
988
    }
}
989

990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
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,
1007
                                   int i_picture_x, int i_picture_y,
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
                                   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) )

1041
1042
1043
1044
{
    for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
    {
        for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
1045
1046
1047
            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] );
1048
1049
1050
    }
}

1051
static inline void BlendAXYZLine( picture_t *p_picture,
1052
                                  int i_picture_x, int i_picture_y,
1053
                                  int i_a, int i_x, int i_y, int i_z,
1054
                                  const line_character_t *p_current,
1055
1056
                                  const line_character_t *p_next,
                                  void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1057
{
1058
    int i_line_width = p_current->p_glyph->bitmap.width;
1059
1060
    if( p_next )
        i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
1061