freetype.c 95.5 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
#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 */
43 44 45
#include <vlc_filter.h>                                      /* filter_sys_t */
#include <vlc_text_style.h>                                   /* text_style_t*/
#include <vlc_memory.h>                                   /* realloc_or_free */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
46 47 48

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

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

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

77 78 79 80 81 82 83 84
/* 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
85
/* FontConfig */
86
#ifdef HAVE_FONTCONFIG
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
87
# include <fontconfig/fontconfig.h>
88 89 90
# define HAVE_STYLES
#endif

91 92
#include <assert.h>

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

99
#define FONT_TEXT N_("Font")
100

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

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

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

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

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

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

151 152 153 154 155
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
156

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

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

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

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

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

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

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

195 196 197 198 199 200

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

typedef struct line_desc_t line_desc_t;
sigmunau's avatar
sigmunau committed
201 202
struct line_desc_t
{
203
    /** NULL-terminated list of glyphs making the string */
sigmunau's avatar
sigmunau committed
204
    FT_BitmapGlyph *pp_glyphs;
205
    /** list of relative positions for the glyphs */
sigmunau's avatar
sigmunau committed
206
    FT_Vector      *p_glyph_pos;
207
    /** list of RGB information for styled text */
208 209 210
    uint32_t       *p_fg_rgb;
    uint32_t       *p_bg_rgb;
    uint8_t        *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
211
    /** underline information -- only supplied if text should be underlined */
212
    int            *pi_underline_offset;
213
    uint16_t       *pi_underline_thickness;
214

sigmunau's avatar
sigmunau committed
215 216
    int             i_height;
    int             i_width;
217 218
    int             i_alpha;

sigmunau's avatar
sigmunau committed
219
    line_desc_t    *p_next;
220
};
221
static line_desc_t *NewLine( int );
Laurent Aimar's avatar
Laurent Aimar committed
222
static void FreeLines( line_desc_t * );
223

224 225 226 227 228 229 230 231 232 233 234
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;
};

235
/*****************************************************************************
236
 * filter_sys_t: freetype local data
237 238
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
239
 * It describes the freetype specific properties of an output thread.
240
 *****************************************************************************/
241
struct filter_sys_t
242 243 244
{
    FT_Library     p_library;   /* handle to library     */
    FT_Face        p_face;      /* handle to face object */
245
    bool           i_use_kerning;
246 247
    uint8_t        i_font_opacity;
    int            i_font_color;
248
    int            i_font_size;
249
    int            i_effect;
250 251 252

    int            i_default_font_size;
    int            i_display_height;
253
    char*          psz_fontfamily;
254
#ifdef HAVE_STYLES
255
    xml_reader_t  *p_xml;
256 257 258
#ifdef WIN32
    char*          psz_win_fonts_path;
#endif
259
#endif
260 261 262

    input_attachment_t **pp_font_attachments;
    int                  i_font_attachments;
263 264
};

265
/* */
Laurent Aimar's avatar
Laurent Aimar committed
266 267
static void YUVFromRGB( uint32_t i_argb,
                    uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
268
{
Laurent Aimar's avatar
Laurent Aimar committed
269 270 271
    int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
    int i_green = ( i_argb & 0x0000ff00 ) >>  8;
    int i_blue  = ( i_argb & 0x000000ff );
272

Laurent Aimar's avatar
Laurent Aimar committed
273 274 275 276 277 278
    *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);
279 280
}

281 282 283 284 285 286 287 288 289 290
/*****************************************************************************
 * 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;

291
    if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
292
        return VLC_EGENERIC;
bitmap's avatar
bitmap committed
293

294 295
    p_sys->i_font_attachments = 0;
    p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
296 297
    if( !p_sys->pp_font_attachments )
        return VLC_ENOMEM;
298

299
    for( int k = 0; k < i_attachments_cnt; k++ )
300 301 302
    {
        input_attachment_t *p_attach = pp_attachments[k];

303 304 305
        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 )
306
        {
307
            p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
308 309 310 311 312 313
        }
        else
        {
            vlc_input_attachment_Delete( p_attach );
        }
    }
bitmap's avatar
bitmap committed
314
    free( pp_attachments );
315

316
    return VLC_SUCCESS;
317 318
}

Laurent Aimar's avatar
Laurent Aimar committed
319
static int GetFontSize( filter_t *p_filter )
320
{
Laurent Aimar's avatar
Laurent Aimar committed
321 322
    filter_sys_t *p_sys = p_filter->p_sys;
    int           i_size = 0;
323

Laurent Aimar's avatar
Laurent Aimar committed
324 325
    if( p_sys->i_default_font_size )
    {
326
        i_size = p_sys->i_default_font_size;
Laurent Aimar's avatar
Laurent Aimar committed
327 328 329
    }
    else
    {
330 331
        int i_ratio = var_GetInteger( p_filter, "freetype-rel-fontsize" );
        if( i_ratio > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
332
        {
333 334
            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
335 336 337 338 339
        }
    }
    if( i_size <= 0 )
    {
        msg_Warn( p_filter, "invalid fontsize, using 12" );
340
        i_size = 12;
Laurent Aimar's avatar
Laurent Aimar committed
341 342 343
    }
    return i_size;
}
344

Laurent Aimar's avatar
Laurent Aimar committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 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 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 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 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 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
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();

#ifdef WIN32
    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 );
    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;

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

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

    FcPatternDestroy( p_pat );
    return strdup( (const char*)val_s );
}
#endif

#ifdef WIN32
#define UNICODE
#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];

    if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
        return 1;

    for( int index = 0;; index++ )
    {
        DWORD vbuflen = MAX_PATH - 1;
        DWORD dbuflen = 255;

        if( RegEnumValueW( hKey, index, vbuffer, &vbuflen,
                           NULL, NULL, (LPBYTE)dbuffer, &dbuflen) != ERROR_SUCCESS )
            return 2;

        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, '&') ) {
            if( strcasestr( psz_value, font_name ) != NULL )
                break;
        }
        else {
            if( strcasecmp( psz_value, font_name ) == 0 )
                break;
        }
    }

    *psz_filename = FromWide( dbuffer );
    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 );
    // msg_Dbg( p_filter, "Here in Win32_Select, asking for %s", family );

    /* */
    LOGFONT lf;
    lf.lfCharSet = DEFAULT_CHARSET;
    if( b_italic )
        lf.lfItalic = true;
    if( b_bold )
        lf.lfWeight = FW_BOLD;
    strncpy( (LPSTR)&lf.lfFaceName, family, 32);

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

    if( psz_filename == NULL )
        return NULL;

    /* FIXME: increase i_idx, when concatenated strings  */
    i_idx = 0;

    /* */
    char *psz_tmp;
    if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
        return NULL;
    return psz_tmp;
}
#endif

#endif


/*****************************************************************************
 * 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,
                       line_desc_t *p_line, int i_width, int i_height )
{
    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 */
    memset( &fmt, 0, sizeof(video_format_t) );
588
    fmt.i_chroma = VLC_CODEC_YUVP;
589 590
    fmt.i_width = fmt.i_visible_width = i_width + 4;
    fmt.i_height = fmt.i_visible_height = i_height + 4;
591 592 593 594
    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;
595
    fmt.i_x_offset = fmt.i_y_offset = 0;
596 597
    fmt.i_sar_num = 1;
    fmt.i_sar_den = 1;
598

599
    assert( !p_region->p_picture );
600
    p_region->p_picture = picture_NewFromFormat( &fmt );
601 602
    if( !p_region->p_picture )
        return VLC_EGENERIC;
603
    fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
604
    p_region->fmt = fmt;
605

606 607 608
    /* Calculate text color components
     * Only use the first color */
    YUVFromRGB( p_line->p_fg_rgb[ 0 ], &i_y, &i_u, &i_v );
609 610

    /* Build palette */
611
    fmt.p_palette->i_entries = 16;
612 613 614 615 616 617 618 619 620 621
    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++ )
622
    {
623
        fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
624 625
        fmt.p_palette->palette[i][1] = i_u;
        fmt.p_palette->palette[i][2] = i_v;
626
        fmt.p_palette->palette[i][3] = pi_gamma[i];
627
        fmt.p_palette->palette[i][3] =
628
            (int)fmt.p_palette->palette[i][3] * (255 - p_line->i_alpha) / 255;
629 630
    }

631 632
    p_dst = p_region->p_picture->Y_PIXELS;
    i_pitch = p_region->p_picture->Y_PITCH;
633

634
    /* Initialize the region pixels */
635
    memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
636

637
    for( ; p_line != NULL; p_line = p_line->p_next )
638 639
    {
        int i_glyph_tmax = 0;
640
        int i_bitmap_offset, i_offset, i_align_offset = 0;
sigmunau's avatar
sigmunau committed
641 642 643
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
644
            i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
sigmunau's avatar
sigmunau committed
645
        }
646

647
        if( p_line->i_width < i_width )
648
        {
649
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
650 651 652
            {
                i_align_offset = i_width - p_line->i_width;
            }
653
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
654 655 656 657
            {
                i_align_offset = ( i_width - p_line->i_width ) / 2;
            }
        }
658

sigmunau's avatar
sigmunau committed
659 660 661
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
        {
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
662 663

            i_offset = ( p_line->p_glyph_pos[ i ].y +
664 665
                i_glyph_tmax - p_glyph->top + 2 ) *
                i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
666
                i_align_offset;
667 668

            for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
sigmunau's avatar
sigmunau committed
669
            {
670
                for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
sigmunau's avatar
sigmunau committed
671
                {
672 673 674
                    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
675
                }
676
                i_offset += i_pitch;
sigmunau's avatar
sigmunau committed
677
            }
678 679
        }
    }
680

681 682 683
    /* Outlining (find something better than nearest neighbour filtering ?) */
    if( 1 )
    {
684
        uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
685 686
        uint8_t *p_top = p_dst; /* Use 1st line as a cache */
        uint8_t left, current;
687

688 689
        for( y = 1; y < (int)fmt.i_height - 1; y++ )
        {
690
            if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
691
            p_dst += p_region->p_picture->Y_PITCH;
692 693 694
            left = 0;

            for( x = 1; x < (int)fmt.i_width - 1; x++ )
sigmunau's avatar
sigmunau committed
695
            {
696
                current = p_dst[x];
697
                p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
698
                             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;
699
                left = current;
sigmunau's avatar
sigmunau committed
700 701
            }
        }
702
        memset( p_top, 0, fmt.i_width );
sigmunau's avatar
sigmunau committed
703
    }
704 705

    return VLC_SUCCESS;
sigmunau's avatar
sigmunau committed
706
}
707

708
static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
709 710 711
                                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
712
                                uint8_t i_y, uint8_t i_u, uint8_t i_v,
713 714 715 716 717
                                subpicture_region_t *p_region)
{
    int i_pitch;
    uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;

718 719 720 721 722
    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;
723 724 725 726

    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
727
    for( int y = 0; y < i_line_thickness; y++ )
728 729
    {
        int i_extra = p_this_glyph->bitmap.width;
730

731 732 733 734 735
        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
736
        for( int x = 0; x < i_extra; x++ )
737
        {
738
            bool b_ok = true;
739 740

            /* break the underline around the tails of any glyphs which cross it */
741
            /* Strikethrough doesn't get broken */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
742
            for( int z = x - i_line_thickness;
743
                 z < x + i_line_thickness && b_ok && (i_line_offset >= 0);
744 745 746 747 748 749 750 751 752
                 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] )
                    {
753
                        b_ok = false;
754 755 756 757 758 759 760 761 762
                    }
                }
                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] )
                    {
763
                        b_ok = false;
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
                    }
                }
            }

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

780 781
static void DrawBlack( line_desc_t *p_line, int i_width, subpicture_region_t *p_region, int xoffset, int yoffset )
{
782 783
    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
784
    int y;
785 786 787 788 789 790 791 792 793 794 795

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

796
        if( p_line->i_width < i_width )
797
        {
798
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
799 800 801
            {
                i_align_offset = i_width - p_line->i_width;
            }
802
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
            {
                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
819
                for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
820 821 822 823 824 825 826 827 828 829 830 831 832 833
                {
                    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;
            }
        }
    }
}

/*****************************************************************************
834
 * RenderYUVA: place string in picture
835 836 837 838 839 840 841 842
 *****************************************************************************
 * 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
843
    int i, y, i_pitch, i_alpha;
844

845 846 847
    if( i_width == 0 || i_height == 0 )
        return VLC_SUCCESS;

848 849
    /* Create a new subpicture region */
    memset( &fmt, 0, sizeof(video_format_t) );
850
    fmt.i_chroma = VLC_CODEC_YUVA;
851 852
    fmt.i_width = fmt.i_visible_width = i_width + 6;
    fmt.i_height = fmt.i_visible_height = i_height + 6;
853 854 855 856
    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;
857
    fmt.i_x_offset = fmt.i_y_offset = 0;
858 859
    fmt.i_sar_num = 1;
    fmt.i_sar_den = 1;
860

861
    p_region->p_picture = picture_NewFromFormat( &fmt );
862 863 864
    if( !p_region->p_picture )
        return VLC_EGENERIC;
    p_region->fmt = fmt;
865

866
    /* Save the alpha value */
867
    i_alpha = p_line->i_alpha;
868

869 870 871 872 873
    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;
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

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

900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
    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 );
        }

933
        if( p_line->i_width < i_width )
934
        {
935
            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
936 937 938
            {
                i_align_offset = i_width - p_line->i_width;
            }
939
            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
940 941 942 943 944 945 946 947 948 949 950 951 952 953
            {
                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;

954 955 956
            /* Every glyph can (and in fact must) have its own color */
            uint8_t i_y, i_u, i_v;
            YUVFromRGB( p_line->p_fg_rgb[ i ], &i_y, &i_u, &i_v );
957

958 959
            for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
            {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
960
                for( int x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
961
                {
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
                    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 );
                        }
                    }

978 979
                    if( p_glyph->bitmap.buffer[i_bitmap_offset] )
                    {
980 981
                        p_dst_y[i_offset+x] = ((p_dst_y[i_offset+x] *(255-(int)p_glyph->bitmap.buffer[i_bitmap_offset])) +
                                              i_y * ((int)p_glyph->bitmap.buffer[i_bitmap_offset])) >> 8;
982

983 984
                        p_dst_u[i_offset+x] = i_u;
                        p_dst_v[i_offset+x] = i_v;
985

986 987