]> git.sesse.net Git - vlc/blob - modules/misc/freetype.c
improves OSD sytem
[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.38 2003/12/08 17:48:13 yoann 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_("Filename of Font")
96 #define FONTSIZE_TEXT N_("Font size in pixels")
97 #define FONTSIZE_LONGTEXT N_("The size of the fonts used by the osd module. If \
98  set to something different than 0 this option will override the relative font size " )
99 #define FONTSIZER_TEXT N_("Font size")
100 #define FONTSIZER_LONGTEXT N_("The size of the fonts used by the osd module" )
101
102 static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
103 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
104                                    N_("Large"), N_("Larger") };
105
106 vlc_module_begin();
107     add_category_hint( N_("Fonts"), NULL, VLC_FALSE );
108     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT, VLC_FALSE );
109     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT, FONTSIZE_LONGTEXT, VLC_TRUE );
110     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT, FONTSIZER_LONGTEXT,
111                  VLC_FALSE );
112         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
113     set_description( _("freetype2 font renderer") );
114     set_capability( "text renderer", 100 );
115     add_shortcut( "text" );
116     set_callbacks( Create, Destroy );
117 vlc_module_end();
118
119 /**
120  * Private data in a subpicture. Describes a string.
121  */
122 struct subpicture_sys_t
123 {
124     int            i_x_margin;
125     int            i_y_margin;
126     int            i_width;
127     int            i_height;
128     int            i_flags;
129     /** The string associated with this subpicture */
130     byte_t        *psz_text;
131     line_desc_t   *p_lines;
132 };
133
134 struct line_desc_t
135 {
136     /** NULL-terminated list of glyphs making the string */
137     FT_BitmapGlyph *pp_glyphs;
138     /** list of relative positions for the glyphs */
139     FT_Vector      *p_glyph_pos;
140     int             i_height;
141     int             i_width;
142     line_desc_t    *p_next;
143 };
144
145 /*****************************************************************************
146  * text_renderer_sys_t: freetype local data
147  *****************************************************************************
148  * This structure is part of the video output thread descriptor.
149  * It describes the freetype specific properties of an output thread.
150  *****************************************************************************/
151 struct text_renderer_sys_t
152 {
153     FT_Library     p_library;   /* handle to library     */
154     FT_Face        p_face;      /* handle to face object */
155     vlc_mutex_t   *lock;
156     vlc_bool_t     i_use_kerning;
157     uint8_t        pi_gamma[256];
158 };
159
160 /*****************************************************************************
161  * Create: allocates osd-text video thread output method
162  *****************************************************************************
163  * This function allocates and initializes a Clone vout method.
164  *****************************************************************************/
165 #define gamma_value 2.0
166 static int Create( vlc_object_t *p_this )
167 {
168     vout_thread_t *p_vout = (vout_thread_t *)p_this;
169     char *psz_fontfile;
170     int i, i_error;
171     int i_fontsize = 0;
172     double gamma_inv = 1.0f / gamma_value;
173     vlc_value_t val;
174
175     /* Allocate structure */
176     p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) );
177     if( p_vout->p_text_renderer_data == NULL )
178     {
179         msg_Err( p_vout, "out of memory" );
180         return VLC_ENOMEM;
181     }
182
183     for (i = 0; i < 256; i++) {
184         p_vout->p_text_renderer_data->pi_gamma[i] =
185             (uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
186     }
187
188     var_Create( p_vout, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
189     var_Create( p_vout, "freetype-fontsize",
190                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
191     var_Create( p_vout, "freetype-rel-fontsize",
192                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
193
194     /* Look what method was requested */
195     var_Get( p_vout, "freetype-font", &val );
196     psz_fontfile = val.psz_string;
197
198     if( !psz_fontfile || !*psz_fontfile )
199     {
200         if( psz_fontfile ) free( psz_fontfile );
201         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
202 #ifdef WIN32
203         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
204         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
205 #elif SYS_DARWIN
206         strcpy( psz_fontfile, DEFAULT_FONT );
207 #else
208         msg_Err( p_vout, "user didn't specify a font" );
209         free( p_vout->p_text_renderer_data );
210         return VLC_EGENERIC;
211 #endif
212     }
213
214     i_error = FT_Init_FreeType( &p_vout->p_text_renderer_data->p_library );
215     if( i_error )
216     {
217         msg_Err( p_vout, "couldn't initialize freetype" );
218         free( p_vout->p_text_renderer_data );
219         return VLC_EGENERIC;
220     }
221
222     i_error = FT_New_Face( p_vout->p_text_renderer_data->p_library,
223                            psz_fontfile ? psz_fontfile : "", 0,
224                            &p_vout->p_text_renderer_data->p_face );
225     if( i_error == FT_Err_Unknown_File_Format )
226     {
227         msg_Err( p_vout, "file %s have unknown format", psz_fontfile );
228         FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
229         free( p_vout->p_text_renderer_data );
230         if( psz_fontfile ) free( psz_fontfile );
231         return VLC_EGENERIC;
232     }
233     else if( i_error )
234     {
235         msg_Err( p_vout, "failed to load font file %s", psz_fontfile );
236         FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
237         free( p_vout->p_text_renderer_data );
238         if( psz_fontfile ) free( psz_fontfile );
239         return VLC_EGENERIC;
240     }
241     if( psz_fontfile ) free( psz_fontfile );
242
243     i_error = FT_Select_Charmap( p_vout->p_text_renderer_data->p_face,
244                                  ft_encoding_unicode );
245     if( i_error )
246     {
247         msg_Err( p_vout, "Font has no unicode translation table" );
248         FT_Done_Face( p_vout->p_text_renderer_data->p_face );
249         FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
250         free( p_vout->p_text_renderer_data );
251         return VLC_EGENERIC;
252     }
253
254     p_vout->p_text_renderer_data->i_use_kerning =
255         FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
256
257     var_Get( p_vout, "freetype-fontsize", &val );
258
259     if( val.i_int )
260     {
261         i_fontsize = val.i_int;
262     }
263     else
264     {
265         var_Get( p_vout, "freetype-rel-fontsize", &val );
266         i_fontsize = (int) p_vout->render.i_height / val.i_int;
267     }
268     msg_Dbg( p_vout, "Using fontsize: %i", i_fontsize);
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_pic_pitch = p_pic->p[ i_plane ].i_pitch;
350             int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
351
352             p_in = p_pic->p[ i_plane ].p_pixels;
353
354             if ( i_plane == 0 )
355             {
356                 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
357                 {
358                     pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
359                         p_string->i_y_margin;
360                 }
361                 else
362                 {
363                     pen_y = p_string->i_y_margin;
364                 }
365                 pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
366                 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
367                 {
368                     pen_x = i_pic_width - p_line->i_width
369                         - p_string->i_x_margin;
370                 }
371                 else if ( p_string->i_flags & OSD_ALIGN_LEFT )
372                 {
373                     pen_x = p_string->i_x_margin;
374                 }
375                 else
376                 {
377                     pen_x = i_pic_width / 2 - p_line->i_width / 2
378                         + p_string->i_x_margin;
379                 }
380
381                 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
382                 {
383                     FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
384 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
385 #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 ]
386                     for(y = 0; y < p_glyph->bitmap.rows; y++ )
387                     {
388                         for( x = 0; x < p_glyph->bitmap.width; x++ )
389                         {
390                             pen_y--;
391                             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
392                             pen_y++; pen_x--;
393                             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
394                             pen_x += 2;
395                             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
396                             pen_y++; pen_x--;
397                             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
398                             pen_y--;
399                         }
400                     }
401                     for(y = 0; y < p_glyph->bitmap.rows; y++ )
402                     {
403                         for( x = 0; x < p_glyph->bitmap.width; x++ )
404                         {
405                             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
406                                 ( 255 * alpha >> 8 );
407                         }
408                     }
409 #undef alpha
410 #undef pixel
411                 }
412             }
413             else
414             {
415                 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
416                 {
417                     pen_y = p_pic->p[i_plane].i_lines - ( p_string->i_height>>1) -
418                         (p_string->i_y_margin>>1);
419                 }
420                 else
421                 {
422                     pen_y = p_string->i_y_margin >> 1;
423                 }
424                 pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 7;
425                 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
426                 {
427                     pen_x = i_pic_width - ( p_line->i_width >> 1 )
428                         - ( p_string->i_x_margin >> 1 );
429                 }
430                 else if ( p_string->i_flags & OSD_ALIGN_LEFT )
431                 {
432                     pen_x = p_string->i_x_margin >> 1;
433                 }
434                 else
435                 {
436                     pen_x = i_pic_width / 2 - p_line->i_width / 4
437                         + p_string->i_x_margin / 2;
438                 }
439
440                 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
441                 {
442                     FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
443 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ ( x + y * p_glyph->bitmap.width ) ] ]
444 #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) ]
445                     for( y = 0; y < p_glyph->bitmap.rows; y+=2 )
446                     {
447                         for( x = 0; x < p_glyph->bitmap.width; x+=2 )
448                         {
449                             pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
450                                 ( 0x80 * alpha >> 8 );
451 #undef alpha
452 #undef pixel
453                         }
454                     }
455                 }
456             }
457         }
458     }
459 }
460
461 /**
462  * Draw a string on a YUY2 picture
463  */
464 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
465                         const subpicture_t *p_subpic )
466 {
467     subpicture_sys_t *p_string = p_subpic->p_sys;
468     int x, y, pen_x, pen_y;
469     unsigned int i;
470     line_desc_t *p_line;
471
472     for( p_line = p_subpic->p_sys->p_lines; p_line != NULL;
473          p_line = p_line->p_next )
474     {
475         uint8_t *p_in;
476         int i_pic_pitch = p_pic->p[0].i_pitch;
477         int i_pic_width = p_pic->p[0].i_visible_pitch;
478
479         p_in = p_pic->p[0].p_pixels;
480
481         if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
482         {
483             pen_y = p_pic->p[0].i_lines - p_string->i_height -
484                 p_string->i_y_margin;
485         }
486         else
487         {
488             pen_y = p_string->i_y_margin;
489         }
490         pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
491         if ( p_string->i_flags & OSD_ALIGN_RIGHT )
492         {
493             pen_x = i_pic_width - p_line->i_width
494                 - p_string->i_x_margin;
495         }
496         else if ( p_string->i_flags & OSD_ALIGN_LEFT )
497         {
498             pen_x = p_string->i_x_margin;
499         }
500         else
501         {
502             pen_x = i_pic_width / 2 /2 - p_line->i_width / 2 + p_string->i_x_margin;
503         }
504
505         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
506         {
507             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
508 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
509 #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 ) ]
510             for(y = 0; y < p_glyph->bitmap.rows; y++ )
511             {
512                 for( x = 0; x < p_glyph->bitmap.width; x++ )
513                 {
514                     pen_y--;
515                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
516                     pen_y++; pen_x--;
517                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
518                     pen_x += 2;
519                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
520                     pen_y++; pen_x--;
521                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
522                     pen_y--;
523                 }
524             }
525             for(y = 0; y < p_glyph->bitmap.rows; y++ )
526             {
527                 for( x = 0; x < p_glyph->bitmap.width; x++ )
528                 {
529                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
530                         ( 255 * alpha >> 8 );
531                 }
532             }
533 #undef alpha
534 #undef pixel
535         }
536     }
537 }
538
539 /**
540  * Draw a string on a RV32 picture
541  */
542 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
543                     const subpicture_t *p_subpic )
544 {
545     subpicture_sys_t *p_string = p_subpic->p_sys;
546     int i_plane, x, y, pen_x, pen_y;
547     unsigned int i;
548     line_desc_t *p_line;
549
550     i_plane = 0;
551
552     for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next )
553     {
554         uint8_t *p_in;
555         int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
556         int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
557
558         p_in = p_pic->p[ i_plane ].p_pixels;
559
560         if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
561         {
562             pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
563                 p_string->i_y_margin;
564         }
565         else
566         {
567             pen_y = p_string->i_y_margin;
568         }
569         pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
570         if ( p_string->i_flags & OSD_ALIGN_RIGHT )
571         {
572             pen_x = i_pic_width - p_line->i_width
573                 - p_string->i_x_margin;
574         }
575         else if ( p_string->i_flags & OSD_ALIGN_LEFT )
576         {
577             pen_x = p_string->i_x_margin;
578         }
579         else
580         {
581             pen_x = i_pic_width / 2 / 4 - p_line->i_width / 2
582                 + p_string->i_x_margin;
583         }
584
585         for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
586         {
587             FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
588 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
589 #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 ]
590             for(y = 0; y < p_glyph->bitmap.rows; y++ )
591             {
592                 for( x = 0; x < p_glyph->bitmap.width; x++ )
593                 {
594                     pen_y--;
595                     pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
596                     pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
597                     pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
598                     pen_y++; pen_x--;
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_x += 2;
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_y++; pen_x--;
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--;
611                 }
612             }
613             for(y = 0; y < p_glyph->bitmap.rows; y++ )
614             {
615                 for( x = 0; x < p_glyph->bitmap.width; x++ )
616                 {
617                     pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
618                         ( 255 * alpha >> 8 );
619                     pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
620                         ( 255 * alpha >> 8 );
621                     pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
622                         ( 255 * alpha >> 8 );
623                 }
624             }
625 #undef alpha
626 #undef pixel
627         }
628     }
629 }
630
631 /**
632  * This function receives a string and creates a subpicture for it. It
633  * also calculates the size needed for this string, and renders the
634  * needed glyphs into memory. It is used as pf_add_string callback in
635  * the vout method by this module
636  */
637 static subpicture_t *AddText ( vout_thread_t *p_vout, char *psz_string,
638                      text_style_t *p_style, int i_flags, int i_hmargin,
639                      int i_vmargin, mtime_t i_start, mtime_t i_stop )
640 {
641     subpicture_sys_t *p_string;
642     int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
643     subpicture_t *p_subpic;
644     line_desc_t  *p_line,  *p_next;
645     uint32_t *p_unicode_string, i_char;
646     int i_string_length;
647     iconv_t iconv_handle;
648
649     FT_BBox line;
650     FT_BBox glyph_size;
651     FT_Vector result;
652     FT_Glyph tmp_glyph;
653
654     /* Sanity check */
655     if ( !psz_string || !*psz_string )
656     {
657         return NULL;
658     }
659
660     result.x = 0;
661     result.y = 0;
662     line.xMin = 0;
663     line.xMax = 0;
664     line.yMin = 0;
665     line.yMax = 0;
666
667     p_line = 0;
668     p_string = 0;
669     p_subpic = 0;
670
671     /* Create and initialize a subpicture */
672     p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
673     if ( p_subpic == NULL )
674     {
675         return NULL;
676     }
677     p_subpic->p_sys = 0;
678     p_subpic->pf_render = Render;
679     p_subpic->pf_destroy = FreeString;
680     p_subpic->i_start = i_start;
681     p_subpic->i_stop = i_stop;
682     if( i_stop == 0 )
683     {
684         p_subpic->b_ephemer = VLC_TRUE;
685     }
686     else
687     {
688         p_subpic->b_ephemer = VLC_FALSE;
689     }
690
691     /* Create and initialize private data for the subpicture */
692     p_string = malloc( sizeof(subpicture_sys_t) );
693     if ( p_string == NULL )
694     {
695         msg_Err( p_vout, "Out of memory" );
696         goto error;
697     }
698     p_subpic->p_sys = p_string;
699     p_string->i_flags = i_flags;
700     p_string->i_x_margin = i_hmargin;
701     p_string->i_y_margin = i_vmargin;
702     p_string->p_lines = 0;
703     p_string->psz_text = strdup( psz_string );
704
705 #if defined(HAVE_ICONV)
706     p_unicode_string = malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
707     if( p_unicode_string == NULL )
708     {
709         msg_Err( p_vout, "Out of memory" );
710         goto error;
711     }
712 #if defined(WORDS_BIGENDIAN)
713     iconv_handle = iconv_open( "UCS-4BE", "UTF-8" );
714 #else
715     iconv_handle = iconv_open( "UCS-4LE", "UTF-8" );
716 #endif
717     if( iconv_handle == (iconv_t)-1 )
718     {
719         msg_Warn( p_vout, "Unable to do convertion" );
720         goto error;
721     }
722
723     {
724         char *p_in_buffer, *p_out_buffer;
725         size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
726         i_in_bytes = strlen( psz_string );
727         i_out_bytes = i_in_bytes * sizeof( uint32_t );
728         i_out_bytes_left = i_out_bytes;
729         p_in_buffer = psz_string;
730         p_out_buffer = (char *)p_unicode_string;
731         i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
732         if( i_in_bytes )
733         {
734             msg_Warn( p_vout, "Failed to convert string to unicode (%s), bytes left %d", strerror(errno), i_in_bytes );
735             goto error;
736         }
737         *(uint32_t*)p_out_buffer = 0;
738         i_string_length = ( i_out_bytes - i_out_bytes_left ) / sizeof(uint32_t);
739     }
740
741 #if defined(HAVE_FRIBIDI)
742     {
743         uint32_t *p_fribidi_string;
744         FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
745         p_fribidi_string = malloc( ( i_string_length + 1 ) * sizeof(uint32_t) );
746         fribidi_log2vis( (FriBidiChar*)p_unicode_string, i_string_length,
747                          &base_dir, (FriBidiChar*)p_fribidi_string, NULL, NULL,
748                          NULL );
749         free( p_unicode_string );
750         p_unicode_string = p_fribidi_string;
751         p_fribidi_string[ i_string_length ] = 0;
752     }
753 #endif
754 #endif
755
756     /* Calculate relative glyph positions and a bounding box for the
757      * entire string */
758     p_line = NewLine( psz_string );
759     if( p_line == NULL )
760     {
761         msg_Err( p_vout, "Out of memory" );
762         goto error;
763     }
764     p_string->p_lines = p_line;
765     i_pen_x = 0;
766     i_pen_y = 0;
767     i_previous = 0;
768     i = 0;
769
770 #define face p_vout->p_text_renderer_data->p_face
771 #define glyph face->glyph
772
773     while( *p_unicode_string )
774     {
775         i_char = *p_unicode_string++;
776         if ( i_char == '\r' ) /* ignore CR chars wherever they may be */
777         {
778             continue;
779         }
780
781         if ( i_char == '\n' )
782         {
783             p_next = NewLine( psz_string );
784             if( p_next == NULL )
785             {
786                 msg_Err( p_vout, "Out of memory" );
787                 goto error;
788             }
789             p_line->p_next = p_next;
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             p_line = p_next;
794             result.x = __MAX( result.x, line.xMax );
795             result.y += face->size->metrics.height >> 6;
796             i_pen_x = 0;
797             line.xMin = 0;
798             line.xMax = 0;
799             line.yMin = 0;
800             line.yMax = 0;
801             i_pen_y += face->size->metrics.height >> 6;
802             msg_Dbg( p_vout, "Creating new line, i is %d", i );
803             i = 0;
804             continue;
805         }
806
807         i_glyph_index = FT_Get_Char_Index( face, i_char );
808         if ( p_vout->p_text_renderer_data->i_use_kerning && i_glyph_index
809             && i_previous )
810         {
811             FT_Vector delta;
812             FT_Get_Kerning( face, i_previous, i_glyph_index,
813                             ft_kerning_default, &delta );
814             i_pen_x += delta.x >> 6;
815
816         }
817         p_line->p_glyph_pos[ i ].x = i_pen_x;
818         p_line->p_glyph_pos[ i ].y = i_pen_y;
819         i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
820         if ( i_error )
821         {
822             msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error );
823             goto error;
824         }
825         i_error = FT_Get_Glyph( glyph, &tmp_glyph );
826         if ( i_error )
827         {
828             msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error );
829             goto error;
830         }
831         FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
832         i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal,
833                                       NULL, 1 );
834         if ( i_error ) continue;
835         p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
836
837         /* Do rest */
838         line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax - glyph_size.xMin;
839         line.yMax = __MAX( line.yMax, glyph_size.yMax );
840         line.yMin = __MIN( line.yMin, glyph_size.yMin );
841
842         i_previous = i_glyph_index;
843         i_pen_x += glyph->advance.x >> 6;
844         i++;
845     }
846     p_line->i_width = line.xMax;
847     p_line->i_height = face->size->metrics.height >> 6;
848     p_line->pp_glyphs[ i ] = NULL;
849     result.x = __MAX( result.x, line.xMax );
850     result.y += line.yMax - line.yMin;
851     p_string->i_height = result.y;
852     p_string->i_width = result.x;
853     vout_DisplaySubPicture( p_vout, p_subpic );
854     return p_subpic;
855
856 #undef face
857 #undef glyph
858
859  error:
860     FreeString( p_subpic );
861     vout_DestroySubPicture( p_vout, p_subpic );
862     return NULL;
863 }
864
865 static void FreeString( subpicture_t *p_subpic )
866 {
867     unsigned int i;
868     subpicture_sys_t *p_string = p_subpic->p_sys;
869     line_desc_t *p_line, *p_next;
870
871     if( p_subpic->p_sys == NULL ) return;
872
873     for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next )
874     {
875         p_next = p_line->p_next;
876         for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
877         {
878             FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
879         }
880         free( p_line->pp_glyphs );
881         free( p_line->p_glyph_pos );
882         free( p_line );
883     }
884
885     free( p_string->psz_text );
886     free( p_string );
887 }
888
889 #if !defined( HAVE_ICONV )
890 /* convert one or more utf8 bytes into a unicode character */
891 static int GetUnicodeCharFromUTF8( byte_t **ppsz_utf8_string )
892 {
893     int i_remaining_bytes, i_char = 0;
894     if( ( **ppsz_utf8_string & 0xFC ) == 0xFC )
895     {
896         i_char = **ppsz_utf8_string & 1;
897         i_remaining_bytes = 5;
898     }
899     else if( ( **ppsz_utf8_string & 0xF8 ) == 0xF8 )
900     {
901         i_char = **ppsz_utf8_string & 3;
902         i_remaining_bytes = 4;
903     }
904     else if( ( **ppsz_utf8_string & 0xF0 ) == 0xF0 )
905     {
906         i_char = **ppsz_utf8_string & 7;
907         i_remaining_bytes = 3;
908     }
909     else if( ( **ppsz_utf8_string & 0xE0 ) == 0xE0 )
910     {
911         i_char = **ppsz_utf8_string & 15;
912         i_remaining_bytes = 2;
913     }
914     else if( ( **ppsz_utf8_string & 0xC0 ) == 0xC0 )
915     {
916         i_char = **ppsz_utf8_string & 31;
917         i_remaining_bytes = 1;
918     }
919     else
920     {
921         i_char = **ppsz_utf8_string;
922         i_remaining_bytes = 0;
923     }
924     while( i_remaining_bytes )
925     {
926         (*ppsz_utf8_string)++;
927         i_remaining_bytes--;
928         i_char = ( i_char << 6 ) + ( **ppsz_utf8_string & 0x3F );
929     }
930     (*ppsz_utf8_string)++;
931     return i_char;
932 }
933 #endif
934
935 static line_desc_t *NewLine( byte_t *psz_string )
936 {
937     int i_count;
938     line_desc_t *p_line = malloc( sizeof(line_desc_t) );
939     if( !p_line )
940     {
941         return NULL;
942     }
943     p_line->i_height = 0;
944     p_line->i_width = 0;
945     p_line->p_next = NULL;
946
947     /* We don't use CountUtf8Characters() here because we are not acutally
948      * sure the string is utf8. Better be safe than sorry. */
949     i_count = strlen( psz_string );
950
951     p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph)
952                                 * ( i_count + 1 ) );
953     if( p_line->pp_glyphs == NULL )
954     {
955         free( p_line );
956         return NULL;
957     }
958     p_line->p_glyph_pos = malloc( sizeof( FT_Vector )
959                                   * i_count + 1 );
960     if( p_line->p_glyph_pos == NULL )
961     {
962         free( p_line->pp_glyphs );
963         free( p_line );
964         return NULL;
965     }
966
967     return p_line;
968 }