~ubuntu-branches/debian/wheezy/vlc/wheezy

« back to all changes in this revision

Viewing changes to modules/misc/freetype.c

Tags: upstream-0.7.2.final
ImportĀ upstreamĀ versionĀ 0.7.2.final

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 * freetype.c : Put text on the video, using freetype2
 
3
 *****************************************************************************
 
4
 * Copyright (C) 2002, 2003 VideoLAN
 
5
 * $Id: freetype.c 6961 2004-03-05 17:34:23Z sam $
 
6
 *
 
7
 * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of the GNU General Public License as published by
 
11
 * the Free Software Foundation; either version 2 of the License, or
 
12
 * (at your option) any later version.
 
13
 *
 
14
 * This program is distributed in the hope that it will be useful,
 
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 * GNU General Public License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU General Public License
 
20
 * along with this program; if not, write to the Free Software
 
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 
22
 *****************************************************************************/
 
23
 
 
24
/*****************************************************************************
 
25
 * Preamble
 
26
 *****************************************************************************/
 
27
#include <stdlib.h>                                      /* malloc(), free() */
 
28
#include <string.h>
 
29
 
 
30
#ifdef HAVE_LINUX_LIMITS_H
 
31
#   include <linux/limits.h>
 
32
#endif
 
33
 
 
34
#include <vlc/vlc.h>
 
35
#include <vlc/vout.h>
 
36
#include <osd.h>
 
37
#include <math.h>
 
38
 
 
39
#ifdef HAVE_ERRNO_H
 
40
#   include <errno.h>
 
41
#endif
 
42
 
 
43
#include <ft2build.h>
 
44
#include FT_FREETYPE_H
 
45
#include FT_GLYPH_H
 
46
 
 
47
#ifdef SYS_DARWIN
 
48
#define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
 
49
#elif defined( SYS_BEOS )
 
50
#define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
 
51
#elif defined( WIN32 )
 
52
#define DEFAULT_FONT "" /* Default font found at run-time */
 
53
#else
 
54
#define DEFAULT_FONT "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
 
55
#endif
 
56
 
 
57
#if defined(HAVE_ICONV)
 
58
#include <iconv.h>
 
59
#endif
 
60
#if defined(HAVE_FRIBIDI)
 
61
#include <fribidi/fribidi.h>
 
62
#endif
 
63
 
 
64
typedef struct line_desc_t line_desc_t;
 
65
 
 
66
/*****************************************************************************
 
67
 * Local prototypes
 
68
 *****************************************************************************/
 
69
static int  Create    ( vlc_object_t * );
 
70
static void Destroy   ( vlc_object_t * );
 
71
 
 
72
static void Render    ( vout_thread_t *, picture_t *,
 
73
                        const subpicture_t * );
 
74
static void RenderI420( vout_thread_t *, picture_t *,
 
75
                        const subpicture_t * );
 
76
static void RenderYUY2( vout_thread_t *, picture_t *,
 
77
                        const subpicture_t * );
 
78
static void RenderRV32( vout_thread_t *, picture_t *,
 
79
                        const subpicture_t * );
 
80
static subpicture_t *AddText ( vout_thread_t *, char *, text_style_t *, int,
 
81
                               int, int, mtime_t, mtime_t );
 
82
 
 
83
static void FreeString( subpicture_t * );
 
84
 
 
85
#if !defined(HAVE_ICONV)
 
86
static int  GetUnicodeCharFromUTF8( byte_t ** );
 
87
#endif
 
88
 
 
89
static line_desc_t *NewLine( byte_t * );
 
90
 
 
91
/*****************************************************************************
 
92
 * Module descriptor
 
93
 *****************************************************************************/
 
94
#define FONT_TEXT N_("Font")
 
95
#define FONT_LONGTEXT N_("Font filename")
 
96
#define FONTSIZE_TEXT N_("Font size in pixels")
 
97
#define FONTSIZE_LONGTEXT N_("The size of the fonts used by the osd module. " \
 
98
    "If set to something different than 0 this option will override the " \
 
99
    "relative font size " )
 
100
#define FONTSIZER_TEXT N_("Font size")
 
101
#define FONTSIZER_LONGTEXT N_("The size of the fonts used by the osd module" )
 
102
 
 
103
static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
 
104
static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
 
105
                                   N_("Large"), N_("Larger") };
 
106
 
 
107
vlc_module_begin();
 
108
    set_description( _("freetype2 font renderer") );
 
109
 
 
110
    add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
 
111
              VLC_FALSE );
 
112
    add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
 
113
                 FONTSIZE_LONGTEXT, VLC_TRUE );
 
114
    add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
 
115
                 FONTSIZER_LONGTEXT, VLC_FALSE );
 
116
        change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
 
117
 
 
118
    set_capability( "text renderer", 100 );
 
119
    add_shortcut( "text" );
 
120
    set_callbacks( Create, Destroy );
 
121
vlc_module_end();
 
122
 
 
123
/**
 
124
 * Private data in a subpicture. Describes a string.
 
125
 */
 
126
struct subpicture_sys_t
 
127
{
 
128
    int            i_x_margin;
 
129
    int            i_y_margin;
 
130
    int            i_width;
 
131
    int            i_height;
 
132
    int            i_flags;
 
133
    /** The string associated with this subpicture */
 
134
    byte_t        *psz_text;
 
135
    line_desc_t   *p_lines;
 
136
};
 
137
 
 
138
struct line_desc_t
 
139
{
 
140
    /** NULL-terminated list of glyphs making the string */
 
141
    FT_BitmapGlyph *pp_glyphs;
 
142
    /** list of relative positions for the glyphs */
 
143
    FT_Vector      *p_glyph_pos;
 
144
    int             i_height;
 
145
    int             i_width;
 
146
    line_desc_t    *p_next;
 
147
};
 
148
 
 
149
/*****************************************************************************
 
150
 * text_renderer_sys_t: freetype local data
 
151
 *****************************************************************************
 
152
 * This structure is part of the video output thread descriptor.
 
153
 * It describes the freetype specific properties of an output thread.
 
154
 *****************************************************************************/
 
155
struct text_renderer_sys_t
 
156
{
 
157
    FT_Library     p_library;   /* handle to library     */
 
158
    FT_Face        p_face;      /* handle to face object */
 
159
    vlc_mutex_t   *lock;
 
160
    vlc_bool_t     i_use_kerning;
 
161
    uint8_t        pi_gamma[256];
 
162
};
 
163
 
 
164
/*****************************************************************************
 
165
 * Create: allocates osd-text video thread output method
 
166
 *****************************************************************************
 
167
 * This function allocates and initializes a Clone vout method.
 
168
 *****************************************************************************/
 
169
#define gamma_value 2.0
 
170
static int Create( vlc_object_t *p_this )
 
171
{
 
172
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
 
173
    char *psz_fontfile;
 
174
    int i, i_error;
 
175
    int i_fontsize = 0;
 
176
    double gamma_inv = 1.0f / gamma_value;
 
177
    vlc_value_t val;
 
178
 
 
179
    /* Allocate structure */
 
180
    p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) );
 
181
    if( p_vout->p_text_renderer_data == NULL )
 
182
    {
 
183
        msg_Err( p_vout, "out of memory" );
 
184
        return VLC_ENOMEM;
 
185
    }
 
186
 
 
187
    for (i = 0; i < 256; i++) {
 
188
        p_vout->p_text_renderer_data->pi_gamma[i] =
 
189
            (uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
 
190
    }
 
191
 
 
192
    var_Create( p_vout, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
 
193
    var_Create( p_vout, "freetype-fontsize",
 
194
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
 
195
    var_Create( p_vout, "freetype-rel-fontsize",
 
196
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
 
197
 
 
198
    /* Look what method was requested */
 
199
    var_Get( p_vout, "freetype-font", &val );
 
200
    psz_fontfile = val.psz_string;
 
201
 
 
202
    if( !psz_fontfile || !*psz_fontfile )
 
203
    {
 
204
        if( psz_fontfile ) free( psz_fontfile );
 
205
        psz_fontfile = (char *)malloc( PATH_MAX + 1 );
 
206
#ifdef WIN32
 
207
        GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
 
208
        strcat( psz_fontfile, "\\fonts\\arial.ttf" );
 
209
#elif SYS_DARWIN
 
210
        strcpy( psz_fontfile, DEFAULT_FONT );
 
211
#else
 
212
        msg_Err( p_vout, "user didn't specify a font" );
 
213
        free( p_vout->p_text_renderer_data );
 
214
        return VLC_EGENERIC;
 
215
#endif
 
216
    }
 
217
 
 
218
    i_error = FT_Init_FreeType( &p_vout->p_text_renderer_data->p_library );
 
219
    if( i_error )
 
220
    {
 
221
        msg_Err( p_vout, "couldn't initialize freetype" );
 
222
        free( p_vout->p_text_renderer_data );
 
223
        return VLC_EGENERIC;
 
224
    }
 
225
 
 
226
    i_error = FT_New_Face( p_vout->p_text_renderer_data->p_library,
 
227
                           psz_fontfile ? psz_fontfile : "", 0,
 
228
                           &p_vout->p_text_renderer_data->p_face );
 
229
    if( i_error == FT_Err_Unknown_File_Format )
 
230
    {
 
231
        msg_Err( p_vout, "file %s have unknown format", psz_fontfile );
 
232
        FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
 
233
        free( p_vout->p_text_renderer_data );
 
234
        if( psz_fontfile ) free( psz_fontfile );
 
235
        return VLC_EGENERIC;
 
236
    }
 
237
    else if( i_error )
 
238
    {
 
239
        msg_Err( p_vout, "failed to load font file %s", psz_fontfile );
 
240
        FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
 
241
        free( p_vout->p_text_renderer_data );
 
242
        if( psz_fontfile ) free( psz_fontfile );
 
243
        return VLC_EGENERIC;
 
244
    }
 
245
    if( psz_fontfile ) free( psz_fontfile );
 
246
 
 
247
    i_error = FT_Select_Charmap( p_vout->p_text_renderer_data->p_face,
 
248
                                 ft_encoding_unicode );
 
249
    if( i_error )
 
250
    {
 
251
        msg_Err( p_vout, "Font has no unicode translation table" );
 
252
        FT_Done_Face( p_vout->p_text_renderer_data->p_face );
 
253
        FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
 
254
        free( p_vout->p_text_renderer_data );
 
255
        return VLC_EGENERIC;
 
256
    }
 
257
 
 
258
    p_vout->p_text_renderer_data->i_use_kerning =
 
259
        FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
 
260
 
 
261
    var_Get( p_vout, "freetype-fontsize", &val );
 
262
 
 
263
    if( val.i_int )
 
264
    {
 
265
        i_fontsize = val.i_int;
 
266
    }
 
267
    else
 
268
    {
 
269
        var_Get( p_vout, "freetype-rel-fontsize", &val );
 
270
        i_fontsize = (int) p_vout->render.i_height / val.i_int;
 
271
    }
 
272
    msg_Dbg( p_vout, "Using fontsize: %i", i_fontsize);
 
273
 
 
274
    i_error = FT_Set_Pixel_Sizes( p_vout->p_text_renderer_data->p_face, 0,
 
275
                                  i_fontsize );
 
276
    if( i_error )
 
277
    {
 
278
        msg_Err( p_vout, "couldn't set font size to %d", i_fontsize );
 
279
        free( p_vout->p_text_renderer_data );
 
280
        return VLC_EGENERIC;
 
281
    }
 
282
    p_vout->pf_add_string = AddText;
 
283
    return VLC_SUCCESS;
 
284
}
 
285
 
 
286
/*****************************************************************************
 
287
 * Destroy: destroy Clone video thread output method
 
288
 *****************************************************************************
 
289
 * Clean up all data and library connections
 
290
 *****************************************************************************/
 
291
static void Destroy( vlc_object_t *p_this )
 
292
{
 
293
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
 
294
    FT_Done_Face( p_vout->p_text_renderer_data->p_face );
 
295
    FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
 
296
    free( p_vout->p_text_renderer_data );
 
297
}
 
298
 
 
299
/*****************************************************************************
 
300
 * Render: place string in picture
 
301
 *****************************************************************************
 
302
 * This function merges the previously rendered freetype glyphs into a picture
 
303
 *****************************************************************************/
 
304
static void Render( vout_thread_t *p_vout, picture_t *p_pic,
 
305
                    const subpicture_t *p_subpic )
 
306
{
 
307
    switch( p_vout->output.i_chroma )
 
308
    {
 
309
        /* I420 target, no scaling */
 
310
        case VLC_FOURCC('I','4','2','0'):
 
311
        case VLC_FOURCC('I','Y','U','V'):
 
312
        case VLC_FOURCC('Y','V','1','2'):
 
313
            RenderI420( p_vout, p_pic, p_subpic );
 
314
            break;
 
315
#if 0
 
316
        /* RV16 target, scaling */
 
317
        case VLC_FOURCC('R','V','1','6'):
 
318
            RenderRV16( p_vout, p_pic, p_subpic );
 
319
            break;
 
320
#endif
 
321
        /* RV32 target, scaling */
 
322
        case VLC_FOURCC('R','V','2','4'):
 
323
        case VLC_FOURCC('R','V','3','2'):
 
324
            RenderRV32( p_vout, p_pic, p_subpic );
 
325
            break;
 
326
        /* NVidia or BeOS overlay, no scaling */
 
327
        case VLC_FOURCC('Y','U','Y','2'):
 
328
            RenderYUY2( p_vout, p_pic, p_subpic );
 
329
            break;
 
330
 
 
331
        default:
 
332
            msg_Err( p_vout, "unknown chroma, can't render SPU" );
 
333
            break;
 
334
    }
 
335
}
 
336
 
 
337
/**
 
338
 * Draw a string on a i420 (or similar) picture
 
339
 */
 
340
static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
 
341
                    const subpicture_t *p_subpic )
 
342
{
 
343
    subpicture_sys_t *p_string = p_subpic->p_sys;
 
344
    int i_plane, x, y, pen_x, pen_y;
 
345
    unsigned int i;
 
346
    line_desc_t *p_line;
 
347
 
 
348
    for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next )
 
349
    {
 
350
        for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
 
351
        {
 
352
            uint8_t *p_in;
 
353
            int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
 
354
            int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
 
355
 
 
356
            p_in = p_pic->p[ i_plane ].p_pixels;
 
357
 
 
358
            if ( i_plane == 0 )
 
359
            {
 
360
                if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
 
361
                {
 
362
                    pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
 
363
                        p_string->i_y_margin;
 
364
                }
 
365
                else
 
366
                {
 
367
                    pen_y = p_string->i_y_margin;
 
368
                }
 
369
                pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
 
370
                if ( p_string->i_flags & OSD_ALIGN_RIGHT )
 
371
                {
 
372
                    pen_x = i_pic_width - p_line->i_width
 
373
                        - p_string->i_x_margin;
 
374
                }
 
375
                else if ( p_string->i_flags & OSD_ALIGN_LEFT )
 
376
                {
 
377
                    pen_x = p_string->i_x_margin;
 
378
                }
 
379
                else
 
380
                {
 
381
                    pen_x = i_pic_width / 2 - p_line->i_width / 2
 
382
                        + p_string->i_x_margin;
 
383
                }
 
384
 
 
385
                for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
 
386
                {
 
387
                    FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
 
388
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
 
389
#define pixel p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ]
 
390
                    for(y = 0; y < p_glyph->bitmap.rows; y++ )
 
391
                    {
 
392
                        for( x = 0; x < p_glyph->bitmap.width; x++ )
 
393
                        {
 
394
                            pen_y--;
 
395
                            pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
396
                            pen_y++; pen_x--;
 
397
                            pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
398
                            pen_x += 2;
 
399
                            pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
400
                            pen_y++; pen_x--;
 
401
                            pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
402
                            pen_y--;
 
403
                        }
 
404
                    }
 
405
                    for(y = 0; y < p_glyph->bitmap.rows; y++ )
 
406
                    {
 
407
                        for( x = 0; x < p_glyph->bitmap.width; x++ )
 
408
                        {
 
409
                            pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
 
410
                                ( 255 * alpha >> 8 );
 
411
                        }
 
412
                    }
 
413
#undef alpha
 
414
#undef pixel
 
415
                }
 
416
            }
 
417
            else
 
418
            {
 
419
                if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
 
420
                {
 
421
                    pen_y = p_pic->p[i_plane].i_lines - ( p_string->i_height>>1) -
 
422
                        (p_string->i_y_margin>>1);
 
423
                }
 
424
                else
 
425
                {
 
426
                    pen_y = p_string->i_y_margin >> 1;
 
427
                }
 
428
                pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 7;
 
429
                if ( p_string->i_flags & OSD_ALIGN_RIGHT )
 
430
                {
 
431
                    pen_x = i_pic_width - ( p_line->i_width >> 1 )
 
432
                        - ( p_string->i_x_margin >> 1 );
 
433
                }
 
434
                else if ( p_string->i_flags & OSD_ALIGN_LEFT )
 
435
                {
 
436
                    pen_x = p_string->i_x_margin >> 1;
 
437
                }
 
438
                else
 
439
                {
 
440
                    pen_x = i_pic_width / 2 - p_line->i_width / 4
 
441
                        + p_string->i_x_margin / 2;
 
442
                }
 
443
 
 
444
                for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
 
445
                {
 
446
                    FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
 
447
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ ( x + y * p_glyph->bitmap.width ) ] ]
 
448
#define pixel p_in[ ( ( p_line->p_glyph_pos[ i ].y >> 1 ) + pen_y + ( y >> 1 ) -  ( p_glyph->top >> 1 ) ) * i_pic_pitch + ( x >> 1 ) + pen_x + ( p_line->p_glyph_pos[ i ].x >> 1 ) + ( p_glyph->left >>1) ]
 
449
                    for( y = 0; y < p_glyph->bitmap.rows; y+=2 )
 
450
                    {
 
451
                        for( x = 0; x < p_glyph->bitmap.width; x+=2 )
 
452
                        {
 
453
                            pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
 
454
                                ( 0x80 * alpha >> 8 );
 
455
#undef alpha
 
456
#undef pixel
 
457
                        }
 
458
                    }
 
459
                }
 
460
            }
 
461
        }
 
462
    }
 
463
}
 
464
 
 
465
/**
 
466
 * Draw a string on a YUY2 picture
 
467
 */
 
468
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
 
469
                        const subpicture_t *p_subpic )
 
470
{
 
471
    subpicture_sys_t *p_string = p_subpic->p_sys;
 
472
    int x, y, pen_x, pen_y;
 
473
    unsigned int i;
 
474
    line_desc_t *p_line;
 
475
 
 
476
    for( p_line = p_subpic->p_sys->p_lines; p_line != NULL;
 
477
         p_line = p_line->p_next )
 
478
    {
 
479
        uint8_t *p_in;
 
480
        int i_pic_pitch = p_pic->p[0].i_pitch;
 
481
        int i_pic_width = p_pic->p[0].i_visible_pitch;
 
482
 
 
483
        p_in = p_pic->p[0].p_pixels;
 
484
 
 
485
        if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
 
486
        {
 
487
            pen_y = p_pic->p[0].i_lines - p_string->i_height -
 
488
                p_string->i_y_margin;
 
489
        }
 
490
        else
 
491
        {
 
492
            pen_y = p_string->i_y_margin;
 
493
        }
 
494
        pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
 
495
        if ( p_string->i_flags & OSD_ALIGN_RIGHT )
 
496
        {
 
497
            pen_x = i_pic_width - p_line->i_width
 
498
                - p_string->i_x_margin;
 
499
        }
 
500
        else if ( p_string->i_flags & OSD_ALIGN_LEFT )
 
501
        {
 
502
            pen_x = p_string->i_x_margin;
 
503
        }
 
504
        else
 
505
        {
 
506
            pen_x = i_pic_width / 2 /2 - p_line->i_width / 2 + p_string->i_x_margin;
 
507
        }
 
508
 
 
509
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
 
510
        {
 
511
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
 
512
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
 
513
#define pixel p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + 2 * ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) ]
 
514
            for(y = 0; y < p_glyph->bitmap.rows; y++ )
 
515
            {
 
516
                for( x = 0; x < p_glyph->bitmap.width; x++ )
 
517
                {
 
518
                    pen_y--;
 
519
                    pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
520
                    pen_y++; pen_x--;
 
521
                    pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
522
                    pen_x += 2;
 
523
                    pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
524
                    pen_y++; pen_x--;
 
525
                    pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
 
526
                    pen_y--;
 
527
                }
 
528
            }
 
529
            for(y = 0; y < p_glyph->bitmap.rows; y++ )
 
530
            {
 
531
                for( x = 0; x < p_glyph->bitmap.width; x++ )
 
532
                {
 
533
                    pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
 
534
                        ( 255 * alpha >> 8 );
 
535
                }
 
536
            }
 
537
#undef alpha
 
538
#undef pixel
 
539
        }
 
540
    }
 
541
}
 
542
 
 
543
/**
 
544
 * Draw a string on a RV32 picture
 
545
 */
 
546
static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
 
547
                    const subpicture_t *p_subpic )
 
548
{
 
549
    subpicture_sys_t *p_string = p_subpic->p_sys;
 
550
    int i_plane, x, y, pen_x, pen_y;
 
551
    unsigned int i;
 
552
    line_desc_t *p_line;
 
553
 
 
554
    i_plane = 0;
 
555
 
 
556
    for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next )
 
557
    {
 
558
        uint8_t *p_in;
 
559
        int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
 
560
        int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
 
561
 
 
562
        p_in = p_pic->p[ i_plane ].p_pixels;
 
563
 
 
564
        if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
 
565
        {
 
566
            pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
 
567
                p_string->i_y_margin;
 
568
        }
 
569
        else
 
570
        {
 
571
            pen_y = p_string->i_y_margin;
 
572
        }
 
573
        pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
 
574
        if ( p_string->i_flags & OSD_ALIGN_RIGHT )
 
575
        {
 
576
            pen_x = i_pic_width - p_line->i_width
 
577
                - p_string->i_x_margin;
 
578
        }
 
579
        else if ( p_string->i_flags & OSD_ALIGN_LEFT )
 
580
        {
 
581
            pen_x = p_string->i_x_margin;
 
582
        }
 
583
        else
 
584
        {
 
585
            pen_x = i_pic_width / 2 / 4 - p_line->i_width / 2
 
586
                + p_string->i_x_margin;
 
587
        }
 
588
 
 
589
        for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
 
590
        {
 
591
            FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
 
592
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
 
593
#define pixel( c ) p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) * 4 + c ]
 
594
            for(y = 0; y < p_glyph->bitmap.rows; y++ )
 
595
            {
 
596
                for( x = 0; x < p_glyph->bitmap.width; x++ )
 
597
                {
 
598
                    pen_y--;
 
599
                    pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
 
600
                    pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
 
601
                    pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
 
602
                    pen_y++; pen_x--;
 
603
                    pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
 
604
                    pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
 
605
                    pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
 
606
                    pen_x += 2;
 
607
                    pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
 
608
                    pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
 
609
                    pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
 
610
                    pen_y++; pen_x--;
 
611
                    pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
 
612
                    pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
 
613
                    pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
 
614
                    pen_y--;
 
615
                }
 
616
            }
 
617
            for(y = 0; y < p_glyph->bitmap.rows; y++ )
 
618
            {
 
619
                for( x = 0; x < p_glyph->bitmap.width; x++ )
 
620
                {
 
621
                    pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
 
622
                        ( 255 * alpha >> 8 );
 
623
                    pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
 
624
                        ( 255 * alpha >> 8 );
 
625
                    pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
 
626
                        ( 255 * alpha >> 8 );
 
627
                }
 
628
            }
 
629
#undef alpha
 
630
#undef pixel
 
631
        }
 
632
    }
 
633
}
 
634
 
 
635
/**
 
636
 * This function receives a string and creates a subpicture for it. It
 
637
 * also calculates the size needed for this string, and renders the
 
638
 * needed glyphs into memory. It is used as pf_add_string callback in
 
639
 * the vout method by this module
 
640
 */
 
641
static subpicture_t *AddText ( vout_thread_t *p_vout, char *psz_string,
 
642
                     text_style_t *p_style, int i_flags, int i_hmargin,
 
643
                     int i_vmargin, mtime_t i_start, mtime_t i_stop )
 
644
{
 
645
    subpicture_sys_t *p_string;
 
646
    int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
 
647
    subpicture_t *p_subpic;
 
648
    line_desc_t  *p_line,  *p_next;
 
649
    uint32_t *p_unicode_string, i_char;
 
650
    int i_string_length;
 
651
 
 
652
#if defined(HAVE_ICONV)
 
653
    iconv_t iconv_handle;
 
654
#endif
 
655
 
 
656
    FT_BBox line;
 
657
    FT_BBox glyph_size;
 
658
    FT_Vector result;
 
659
    FT_Glyph tmp_glyph;
 
660
 
 
661
    /* Sanity check */
 
662
    if ( !psz_string || !*psz_string )
 
663
    {
 
664
        return NULL;
 
665
    }
 
666
 
 
667
    result.x = 0;
 
668
    result.y = 0;
 
669
    line.xMin = 0;
 
670
    line.xMax = 0;
 
671
    line.yMin = 0;
 
672
    line.yMax = 0;
 
673
 
 
674
    p_line = 0;
 
675
    p_string = 0;
 
676
    p_subpic = 0;
 
677
 
 
678
    /* Create and initialize a subpicture */
 
679
    p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
 
680
    if ( p_subpic == NULL )
 
681
    {
 
682
        return NULL;
 
683
    }
 
684
    p_subpic->p_sys = 0;
 
685
    p_subpic->pf_render = Render;
 
686
    p_subpic->pf_destroy = FreeString;
 
687
    p_subpic->i_start = i_start;
 
688
    p_subpic->i_stop = i_stop;
 
689
    if( i_stop == 0 )
 
690
    {
 
691
        p_subpic->b_ephemer = VLC_TRUE;
 
692
    }
 
693
    else
 
694
    {
 
695
        p_subpic->b_ephemer = VLC_FALSE;
 
696
    }
 
697
 
 
698
    /* Create and initialize private data for the subpicture */
 
699
    p_string = malloc( sizeof(subpicture_sys_t) );
 
700
    if ( p_string == NULL )
 
701
    {
 
702
        msg_Err( p_vout, "Out of memory" );
 
703
        goto error;
 
704
    }
 
705
    p_subpic->p_sys = p_string;
 
706
    p_string->i_flags = i_flags;
 
707
    p_string->i_x_margin = i_hmargin;
 
708
    p_string->i_y_margin = i_vmargin;
 
709
    p_string->p_lines = 0;
 
710
    p_string->psz_text = strdup( psz_string );
 
711
 
 
712
#if defined(HAVE_ICONV)
 
713
    p_unicode_string = malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
 
714
    if( p_unicode_string == NULL )
 
715
    {
 
716
        msg_Err( p_vout, "Out of memory" );
 
717
        goto error;
 
718
    }
 
719
#if defined(WORDS_BIGENDIAN)
 
720
    iconv_handle = iconv_open( "UCS-4BE", "UTF-8" );
 
721
#else
 
722
    iconv_handle = iconv_open( "UCS-4LE", "UTF-8" );
 
723
#endif
 
724
    if( iconv_handle == (iconv_t)-1 )
 
725
    {
 
726
        msg_Warn( p_vout, "Unable to do convertion" );
 
727
        goto error;
 
728
    }
 
729
 
 
730
    {
 
731
        char *p_in_buffer, *p_out_buffer;
 
732
        size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
 
733
        i_in_bytes = strlen( psz_string );
 
734
        i_out_bytes = i_in_bytes * sizeof( uint32_t );
 
735
        i_out_bytes_left = i_out_bytes;
 
736
        p_in_buffer = psz_string;
 
737
        p_out_buffer = (char *)p_unicode_string;
 
738
        i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
 
739
        if( i_in_bytes )
 
740
        {
 
741
            msg_Warn( p_vout, "Failed to convert string to unicode (%s), bytes left %d", strerror(errno), i_in_bytes );
 
742
            goto error;
 
743
        }
 
744
        *(uint32_t*)p_out_buffer = 0;
 
745
        i_string_length = ( i_out_bytes - i_out_bytes_left ) / sizeof(uint32_t);
 
746
    }
 
747
 
 
748
#if defined(HAVE_FRIBIDI)
 
749
    {
 
750
        uint32_t *p_fribidi_string;
 
751
        FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
 
752
        p_fribidi_string = malloc( ( i_string_length + 1 ) * sizeof(uint32_t) );
 
753
        fribidi_log2vis( (FriBidiChar*)p_unicode_string, i_string_length,
 
754
                         &base_dir, (FriBidiChar*)p_fribidi_string, NULL, NULL,
 
755
                         NULL );
 
756
        free( p_unicode_string );
 
757
        p_unicode_string = p_fribidi_string;
 
758
        p_fribidi_string[ i_string_length ] = 0;
 
759
    }
 
760
#endif
 
761
#endif
 
762
 
 
763
    /* Calculate relative glyph positions and a bounding box for the
 
764
     * entire string */
 
765
    p_line = NewLine( psz_string );
 
766
    if( p_line == NULL )
 
767
    {
 
768
        msg_Err( p_vout, "Out of memory" );
 
769
        goto error;
 
770
    }
 
771
    p_string->p_lines = p_line;
 
772
    i_pen_x = 0;
 
773
    i_pen_y = 0;
 
774
    i_previous = 0;
 
775
    i = 0;
 
776
 
 
777
#define face p_vout->p_text_renderer_data->p_face
 
778
#define glyph face->glyph
 
779
 
 
780
    while( *p_unicode_string )
 
781
    {
 
782
        i_char = *p_unicode_string++;
 
783
        if ( i_char == '\r' ) /* ignore CR chars wherever they may be */
 
784
        {
 
785
            continue;
 
786
        }
 
787
 
 
788
        if ( i_char == '\n' )
 
789
        {
 
790
            p_next = NewLine( psz_string );
 
791
            if( p_next == NULL )
 
792
            {
 
793
                msg_Err( p_vout, "Out of memory" );
 
794
                goto error;
 
795
            }
 
796
            p_line->p_next = p_next;
 
797
            p_line->i_width = line.xMax;
 
798
            p_line->i_height = face->size->metrics.height >> 6;
 
799
            p_line->pp_glyphs[ i ] = NULL;
 
800
            p_line = p_next;
 
801
            result.x = __MAX( result.x, line.xMax );
 
802
            result.y += face->size->metrics.height >> 6;
 
803
            i_pen_x = 0;
 
804
            line.xMin = 0;
 
805
            line.xMax = 0;
 
806
            line.yMin = 0;
 
807
            line.yMax = 0;
 
808
            i_pen_y += face->size->metrics.height >> 6;
 
809
            msg_Dbg( p_vout, "Creating new line, i is %d", i );
 
810
            i = 0;
 
811
            continue;
 
812
        }
 
813
 
 
814
        i_glyph_index = FT_Get_Char_Index( face, i_char );
 
815
        if ( p_vout->p_text_renderer_data->i_use_kerning && i_glyph_index
 
816
            && i_previous )
 
817
        {
 
818
            FT_Vector delta;
 
819
            FT_Get_Kerning( face, i_previous, i_glyph_index,
 
820
                            ft_kerning_default, &delta );
 
821
            i_pen_x += delta.x >> 6;
 
822
 
 
823
        }
 
824
        p_line->p_glyph_pos[ i ].x = i_pen_x;
 
825
        p_line->p_glyph_pos[ i ].y = i_pen_y;
 
826
        i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
 
827
        if ( i_error )
 
828
        {
 
829
            msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error );
 
830
            goto error;
 
831
        }
 
832
        i_error = FT_Get_Glyph( glyph, &tmp_glyph );
 
833
        if ( i_error )
 
834
        {
 
835
            msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error );
 
836
            goto error;
 
837
        }
 
838
        FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
 
839
        i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal,
 
840
                                      NULL, 1 );
 
841
        if ( i_error ) continue;
 
842
        p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
 
843
 
 
844
        /* Do rest */
 
845
        line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax - glyph_size.xMin;
 
846
        line.yMax = __MAX( line.yMax, glyph_size.yMax );
 
847
        line.yMin = __MIN( line.yMin, glyph_size.yMin );
 
848
 
 
849
        i_previous = i_glyph_index;
 
850
        i_pen_x += glyph->advance.x >> 6;
 
851
        i++;
 
852
    }
 
853
    p_line->i_width = line.xMax;
 
854
    p_line->i_height = face->size->metrics.height >> 6;
 
855
    p_line->pp_glyphs[ i ] = NULL;
 
856
    result.x = __MAX( result.x, line.xMax );
 
857
    result.y += line.yMax - line.yMin;
 
858
    p_string->i_height = result.y;
 
859
    p_string->i_width = result.x;
 
860
    vout_DisplaySubPicture( p_vout, p_subpic );
 
861
    return p_subpic;
 
862
 
 
863
#undef face
 
864
#undef glyph
 
865
 
 
866
 error:
 
867
    FreeString( p_subpic );
 
868
    vout_DestroySubPicture( p_vout, p_subpic );
 
869
    return NULL;
 
870
}
 
871
 
 
872
static void FreeString( subpicture_t *p_subpic )
 
873
{
 
874
    unsigned int i;
 
875
    subpicture_sys_t *p_string = p_subpic->p_sys;
 
876
    line_desc_t *p_line, *p_next;
 
877
 
 
878
    if( p_subpic->p_sys == NULL ) return;
 
879
 
 
880
    for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next )
 
881
    {
 
882
        p_next = p_line->p_next;
 
883
        for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
 
884
        {
 
885
            FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
 
886
        }
 
887
        free( p_line->pp_glyphs );
 
888
        free( p_line->p_glyph_pos );
 
889
        free( p_line );
 
890
    }
 
891
 
 
892
    free( p_string->psz_text );
 
893
    free( p_string );
 
894
}
 
895
 
 
896
#if !defined( HAVE_ICONV )
 
897
/* convert one or more utf8 bytes into a unicode character */
 
898
static int GetUnicodeCharFromUTF8( byte_t **ppsz_utf8_string )
 
899
{
 
900
    int i_remaining_bytes, i_char = 0;
 
901
    if( ( **ppsz_utf8_string & 0xFC ) == 0xFC )
 
902
    {
 
903
        i_char = **ppsz_utf8_string & 1;
 
904
        i_remaining_bytes = 5;
 
905
    }
 
906
    else if( ( **ppsz_utf8_string & 0xF8 ) == 0xF8 )
 
907
    {
 
908
        i_char = **ppsz_utf8_string & 3;
 
909
        i_remaining_bytes = 4;
 
910
    }
 
911
    else if( ( **ppsz_utf8_string & 0xF0 ) == 0xF0 )
 
912
    {
 
913
        i_char = **ppsz_utf8_string & 7;
 
914
        i_remaining_bytes = 3;
 
915
    }
 
916
    else if( ( **ppsz_utf8_string & 0xE0 ) == 0xE0 )
 
917
    {
 
918
        i_char = **ppsz_utf8_string & 15;
 
919
        i_remaining_bytes = 2;
 
920
    }
 
921
    else if( ( **ppsz_utf8_string & 0xC0 ) == 0xC0 )
 
922
    {
 
923
        i_char = **ppsz_utf8_string & 31;
 
924
        i_remaining_bytes = 1;
 
925
    }
 
926
    else
 
927
    {
 
928
        i_char = **ppsz_utf8_string;
 
929
        i_remaining_bytes = 0;
 
930
    }
 
931
    while( i_remaining_bytes )
 
932
    {
 
933
        (*ppsz_utf8_string)++;
 
934
        i_remaining_bytes--;
 
935
        i_char = ( i_char << 6 ) + ( **ppsz_utf8_string & 0x3F );
 
936
    }
 
937
    (*ppsz_utf8_string)++;
 
938
    return i_char;
 
939
}
 
940
#endif
 
941
 
 
942
static line_desc_t *NewLine( byte_t *psz_string )
 
943
{
 
944
    int i_count;
 
945
    line_desc_t *p_line = malloc( sizeof(line_desc_t) );
 
946
    if( !p_line )
 
947
    {
 
948
        return NULL;
 
949
    }
 
950
    p_line->i_height = 0;
 
951
    p_line->i_width = 0;
 
952
    p_line->p_next = NULL;
 
953
 
 
954
    /* We don't use CountUtf8Characters() here because we are not acutally
 
955
     * sure the string is utf8. Better be safe than sorry. */
 
956
    i_count = strlen( psz_string );
 
957
 
 
958
    p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph)
 
959
                                * ( i_count + 1 ) );
 
960
    if( p_line->pp_glyphs == NULL )
 
961
    {
 
962
        free( p_line );
 
963
        return NULL;
 
964
    }
 
965
    p_line->p_glyph_pos = malloc( sizeof( FT_Vector )
 
966
                                  * i_count + 1 );
 
967
    if( p_line->p_glyph_pos == NULL )
 
968
    {
 
969
        free( p_line->pp_glyphs );
 
970
        free( p_line );
 
971
        return NULL;
 
972
    }
 
973
 
 
974
    return p_line;
 
975
}