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