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