]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
* ALL: removed a bunch of unused add_category_hint().
[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.40 2004/01/25 18:53:07 gbazin 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 #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     iconv_t iconv_handle;
652
653     FT_BBox line;
654     FT_BBox glyph_size;
655     FT_Vector result;
656     FT_Glyph tmp_glyph;
657
658     /* Sanity check */
659     if ( !psz_string || !*psz_string )
660     {
661         return NULL;
662     }
663
664     result.x = 0;
665     result.y = 0;
666     line.xMin = 0;
667     line.xMax = 0;
668     line.yMin = 0;
669     line.yMax = 0;
670
671     p_line = 0;
672     p_string = 0;
673     p_subpic = 0;
674
675     /* Create and initialize a subpicture */
676     p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
677     if ( p_subpic == NULL )
678     {
679         return NULL;
680     }
681     p_subpic->p_sys = 0;
682     p_subpic->pf_render = Render;
683     p_subpic->pf_destroy = FreeString;
684     p_subpic->i_start = i_start;
685     p_subpic->i_stop = i_stop;
686     if( i_stop == 0 )
687     {
688         p_subpic->b_ephemer = VLC_TRUE;
689     }
690     else
691     {
692         p_subpic->b_ephemer = VLC_FALSE;
693     }
694
695     /* Create and initialize private data for the subpicture */
696     p_string = malloc( sizeof(subpicture_sys_t) );
697     if ( p_string == NULL )
698     {
699         msg_Err( p_vout, "Out of memory" );
700         goto error;
701     }
702     p_subpic->p_sys = p_string;
703     p_string->i_flags = i_flags;
704     p_string->i_x_margin = i_hmargin;
705     p_string->i_y_margin = i_vmargin;
706     p_string->p_lines = 0;
707     p_string->psz_text = strdup( psz_string );
708
709 #if defined(HAVE_ICONV)
710     p_unicode_string = malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
711     if( p_unicode_string == NULL )
712     {
713         msg_Err( p_vout, "Out of memory" );
714         goto error;
715     }
716 #if defined(WORDS_BIGENDIAN)
717     iconv_handle = iconv_open( "UCS-4BE", "UTF-8" );
718 #else
719     iconv_handle = iconv_open( "UCS-4LE", "UTF-8" );
720 #endif
721     if( iconv_handle == (iconv_t)-1 )
722     {
723         msg_Warn( p_vout, "Unable to do convertion" );
724         goto error;
725     }
726
727     {
728         char *p_in_buffer, *p_out_buffer;
729         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
730         i_in_bytes = strlen( psz_string );
731         i_out_bytes = i_in_bytes * sizeof( uint32_t );
732         i_out_bytes_left = i_out_bytes;
733         p_in_buffer = psz_string;
734         p_out_buffer = (char *)p_unicode_string;
735         i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
736         if( i_in_bytes )
737         {
738             msg_Warn( p_vout, "Failed to convert string to unicode (%s), bytes left %d", strerror(errno), i_in_bytes );
739             goto error;
740         }
741         *(uint32_t*)p_out_buffer = 0;
742         i_string_length = ( i_out_bytes - i_out_bytes_left ) / sizeof(uint32_t);
743     }
744
745 #if defined(HAVE_FRIBIDI)
746     {
747         uint32_t *p_fribidi_string;
748         FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
749         p_fribidi_string = malloc( ( i_string_length + 1 ) * sizeof(uint32_t) );
750         fribidi_log2vis( (FriBidiChar*)p_unicode_string, i_string_length,
751                          &base_dir, (FriBidiChar*)p_fribidi_string, NULL, NULL,
752                          NULL );
753         free( p_unicode_string );
754         p_unicode_string = p_fribidi_string;
755         p_fribidi_string[ i_string_length ] = 0;
756     }
757 #endif
758 #endif
759
760     /* Calculate relative glyph positions and a bounding box for the
761      * entire string */
762     p_line = NewLine( psz_string );
763     if( p_line == NULL )
764     {
765         msg_Err( p_vout, "Out of memory" );
766         goto error;
767     }
768     p_string->p_lines = p_line;
769     i_pen_x = 0;
770     i_pen_y = 0;
771     i_previous = 0;
772     i = 0;
773
774 #define face p_vout->p_text_renderer_data->p_face
775 #define glyph face->glyph
776
777     while( *p_unicode_string )
778     {
779         i_char = *p_unicode_string++;
780         if ( i_char == '\r' ) /* ignore CR chars wherever they may be */
781         {
782             continue;
783         }
784
785         if ( i_char == '\n' )
786         {
787             p_next = NewLine( psz_string );
788             if( p_next == NULL )
789             {
790                 msg_Err( p_vout, "Out of memory" );
791                 goto error;
792             }
793             p_line->p_next = p_next;
794             p_line->i_width = line.xMax;
795             p_line->i_height = face->size->metrics.height >> 6;
796             p_line->pp_glyphs[ i ] = NULL;
797             p_line = p_next;
798             result.x = __MAX( result.x, line.xMax );
799             result.y += face->size->metrics.height >> 6;
800             i_pen_x = 0;
801             line.xMin = 0;
802             line.xMax = 0;
803             line.yMin = 0;
804             line.yMax = 0;
805             i_pen_y += face->size->metrics.height >> 6;
806             msg_Dbg( p_vout, "Creating new line, i is %d", i );
807             i = 0;
808             continue;
809         }
810
811         i_glyph_index = FT_Get_Char_Index( face, i_char );
812         if ( p_vout->p_text_renderer_data->i_use_kerning && i_glyph_index
813             && i_previous )
814         {
815             FT_Vector delta;
816             FT_Get_Kerning( face, i_previous, i_glyph_index,
817                             ft_kerning_default, &delta );
818             i_pen_x += delta.x >> 6;
819
820         }
821         p_line->p_glyph_pos[ i ].x = i_pen_x;
822         p_line->p_glyph_pos[ i ].y = i_pen_y;
823         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
824         if ( i_error )
825         {
826             msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error );
827             goto error;
828         }
829         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
830         if ( i_error )
831         {
832             msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error );
833             goto error;
834         }
835         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
836         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal,
837                                       NULL, 1 );
838         if ( i_error ) continue;
839         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
840
841         /* Do rest */
842         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax - glyph_size.xMin;
843         line.yMax = __MAX( line.yMax, glyph_size.yMax );
844         line.yMin = __MIN( line.yMin, glyph_size.yMin );
845
846         i_previous = i_glyph_index;
847         i_pen_x += glyph->advance.x >> 6;
848         i++;
849     }
850     p_line->i_width = line.xMax;
851     p_line->i_height = face->size->metrics.height >> 6;
852     p_line->pp_glyphs[ i ] = NULL;
853     result.x = __MAX( result.x, line.xMax );
854     result.y += line.yMax - line.yMin;
855     p_string->i_height = result.y;
856     p_string->i_width = result.x;
857     vout_DisplaySubPicture( p_vout, p_subpic );
858     return p_subpic;
859
860 #undef face
861 #undef glyph
862
863  error:
864     FreeString( p_subpic );
865     vout_DestroySubPicture( p_vout, p_subpic );
866     return NULL;
867 }
868
869 static void FreeString( subpicture_t *p_subpic )
870 {
871     unsigned int i;
872     subpicture_sys_t *p_string = p_subpic->p_sys;
873     line_desc_t *p_line, *p_next;
874
875     if( p_subpic->p_sys == NULL ) return;
876
877     for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next )
878     {
879         p_next = p_line->p_next;
880         for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
881         {
882             FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
883         }
884         free( p_line->pp_glyphs );
885         free( p_line->p_glyph_pos );
886         free( p_line );
887     }
888
889     free( p_string->psz_text );
890     free( p_string );
891 }
892
893 #if !defined( HAVE_ICONV )
894 /* convert one or more utf8 bytes into a unicode character */
895 static int GetUnicodeCharFromUTF8( byte_t **ppsz_utf8_string )
896 {
897     int i_remaining_bytes, i_char = 0;
898     if( ( **ppsz_utf8_string & 0xFC ) == 0xFC )
899     {
900         i_char = **ppsz_utf8_string & 1;
901         i_remaining_bytes = 5;
902     }
903     else if( ( **ppsz_utf8_string & 0xF8 ) == 0xF8 )
904     {
905         i_char = **ppsz_utf8_string & 3;
906         i_remaining_bytes = 4;
907     }
908     else if( ( **ppsz_utf8_string & 0xF0 ) == 0xF0 )
909     {
910         i_char = **ppsz_utf8_string & 7;
911         i_remaining_bytes = 3;
912     }
913     else if( ( **ppsz_utf8_string & 0xE0 ) == 0xE0 )
914     {
915         i_char = **ppsz_utf8_string & 15;
916         i_remaining_bytes = 2;
917     }
918     else if( ( **ppsz_utf8_string & 0xC0 ) == 0xC0 )
919     {
920         i_char = **ppsz_utf8_string & 31;
921         i_remaining_bytes = 1;
922     }
923     else
924     {
925         i_char = **ppsz_utf8_string;
926         i_remaining_bytes = 0;
927     }
928     while( i_remaining_bytes )
929     {
930         (*ppsz_utf8_string)++;
931         i_remaining_bytes--;
932         i_char = ( i_char << 6 ) + ( **ppsz_utf8_string & 0x3F );
933     }
934     (*ppsz_utf8_string)++;
935     return i_char;
936 }
937 #endif
938
939 static line_desc_t *NewLine( byte_t *psz_string )
940 {
941     int i_count;
942     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
943     if( !p_line )
944     {
945         return NULL;
946     }
947     p_line->i_height = 0;
948     p_line->i_width = 0;
949     p_line->p_next = NULL;
950
951     /* We don't use CountUtf8Characters() here because we are not acutally
952      * sure the string is utf8. Better be safe than sorry. */
953     i_count = strlen( psz_string );
954
955     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph)
956                                 * ( i_count + 1 ) );
957     if( p_line->pp_glyphs == NULL )
958     {
959         free( p_line );
960         return NULL;
961     }
962     p_line->p_glyph_pos = malloc( sizeof( FT_Vector )
963                                   * i_count + 1 );
964     if( p_line->p_glyph_pos == NULL )
965     {
966         free( p_line->pp_glyphs );
967         free( p_line );
968         return NULL;
969     }
970
971     return p_line;
972 }