1 /*****************************************************************************
2 * osd_text.c : Filter to put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 VideoLAN
5 * $Id: osd_text.c,v 1.5 2003/06/11 20:36:22 gbazin Exp $
7 * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
35 #include "filter_common.h"
38 #include FT_FREETYPE_H
41 #define FT_RENDER_MODE_NORMAL 0 /* Why do we have to do that ? */
43 /*****************************************************************************
45 *****************************************************************************/
46 static int Create ( vlc_object_t * );
47 static void Destroy ( vlc_object_t * );
49 static int Init ( vout_thread_t * );
50 static void End ( vout_thread_t * );
51 static void Render ( vout_thread_t *, picture_t * );
53 static int SendEvents( vlc_object_t *, char const *,
54 vlc_value_t, vlc_value_t, void * );
55 static int SetMargin ( vlc_object_t *, char const *,
56 vlc_value_t, vlc_value_t, void * );
57 static int AddText ( vlc_object_t *, char const *,
58 vlc_value_t, vlc_value_t, void * );
61 /*****************************************************************************
63 *****************************************************************************/
64 #define FONT_TEXT N_("Font")
65 #define FONT_LONGTEXT N_("Filename of Font")
66 #define FONTSIZE_TEXT N_("Font size")
67 #define FONTSIZE_LONGTEXT N_("The size of the fonts used by the osd module" )
70 add_category_hint( N_("OSD"), NULL, VLC_FALSE );
71 add_file( "osd-font", "", NULL, FONT_TEXT, FONT_LONGTEXT, VLC_FALSE );
72 add_integer( "osd-fontsize", 16, NULL, FONTSIZE_TEXT, FONTSIZE_LONGTEXT, VLC_FALSE );
73 set_description( _("osd text filter") );
74 set_capability( "video filter", 0 );
75 add_shortcut( "text" );
76 set_callbacks( Create, Destroy );
80 Describes a string to be displayed on the video, or a linked list of
83 typedef struct string_info_s string_info_t;
86 string_info_t *p_next;
96 FT_Vector *p_glyph_pos;
99 /*****************************************************************************
100 * vout_sys_t: osd_text local data
101 *****************************************************************************
102 * This structure is part of the video output thread descriptor.
103 * It describes the osd-text specific properties of an output thread.
104 *****************************************************************************/
108 vout_thread_t *p_vout;
109 FT_Library p_library; /* handle to library */
110 FT_Face p_face; /* handle to face object */
111 string_info_t *p_strings;
116 mtime_t i_start_date;
119 vlc_bool_t i_use_kerning;
120 uint8_t pi_gamma[256];
122 /* more prototypes */
123 static void ComputeBoundingBox( string_info_t * );
124 static void FreeString( string_info_t * );
126 /*****************************************************************************
127 * Create: allocates osd-text video thread output method
128 *****************************************************************************
129 * This function allocates and initializes a Clone vout method.
130 *****************************************************************************/
131 #define gamma_value 2.0
132 static int Create( vlc_object_t *p_this )
134 vout_thread_t *p_vout = (vout_thread_t *)p_this;
138 double gamma_inv = 1.0f / gamma_value;
140 /* Allocate structure */
141 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
142 if( p_vout->p_sys == NULL )
144 msg_Err( p_vout, "out of memory" );
147 p_vout->p_sys->p_strings = NULL;
148 p_vout->p_sys->i_x_margin = 50;
149 p_vout->p_sys->i_y_margin = 50;
150 p_vout->p_sys->i_flags = 0;
152 p_vout->p_sys->i_duration = 2000000;
153 p_vout->p_sys->i_start_date = 0;
154 p_vout->p_sys->i_end_date = 0;
155 p_vout->p_sys->lock = malloc( sizeof(vlc_mutex_t));
156 if( p_vout->p_sys->lock == NULL )
158 msg_Err( p_vout, "out of memory" );
161 vlc_mutex_init( p_vout, p_vout->p_sys->lock);
163 for (i = 0; i < 256; i++) {
164 p_vout->p_sys->pi_gamma[i] =
165 (uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
166 msg_Dbg( p_vout, "%d", p_vout->p_sys->pi_gamma[i]);
168 p_vout->pf_init = Init;
169 p_vout->pf_end = End;
170 p_vout->pf_manage = NULL;
171 p_vout->pf_render = Render;
172 p_vout->pf_display = NULL;
174 /* Look what method was requested */
175 psz_fontfile = config_GetPsz( p_vout, "osd-font" );
176 i_error = FT_Init_FreeType( &p_vout->p_sys->p_library );
179 msg_Err( p_vout, "couldn't initialize freetype" );
180 free( p_vout->p_sys );
183 i_error = FT_New_Face( p_vout->p_sys->p_library, psz_fontfile, 0,
184 &p_vout->p_sys->p_face );
185 if( i_error == FT_Err_Unknown_File_Format )
187 msg_Err( p_vout, "file %s have unknown format", psz_fontfile );
188 free( p_vout->p_sys );
193 msg_Err( p_vout, "failed to load font file" );
194 free( p_vout->p_sys );
197 p_vout->p_sys->i_use_kerning = FT_HAS_KERNING(p_vout->p_sys->p_face);
199 i_error = FT_Set_Pixel_Sizes( p_vout->p_sys->p_face, 0, config_GetInt( p_vout, "osd-fontsize" ) );
202 msg_Err( p_vout, "couldn't set font size to %d",
203 config_GetInt( p_vout, "osd-fontsize" ) );
204 free( p_vout->p_sys );
207 var_Create( p_vout, "lock", VLC_VAR_MUTEX );
208 var_Create( p_vout, "flags", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
209 var_AddCallback( p_vout, "flags", SetMargin, NULL );
210 var_Create( p_vout, "x-margin", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
211 var_AddCallback( p_vout, "x-margin", SetMargin, NULL );
212 var_Create( p_vout, "y-margin", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
213 var_AddCallback( p_vout, "y-margin", SetMargin, NULL );
214 var_Create( p_vout, "duration", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
215 var_AddCallback( p_vout, "duration", SetMargin, NULL );
216 var_Create( p_vout, "start-date", VLC_VAR_TIME | VLC_VAR_ISCOMMAND );
217 var_AddCallback( p_vout, "start-date", SetMargin, NULL );
218 var_Create( p_vout, "end-date", VLC_VAR_TIME | VLC_VAR_ISCOMMAND );
219 var_AddCallback( p_vout, "end-date", SetMargin, NULL );
220 var_Create( p_vout, "string", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
221 var_AddCallback( p_vout, "string", AddText, NULL );
223 val.psz_string = "Videolan";
224 // var_Set( p_vout, "string", val );
225 // p_vout->p_sys->p_strings->i_end_date = 0xFFFFFFFFFFFFFFF;
230 /*****************************************************************************
231 * Init: initialize the video thread output method
232 *****************************************************************************/
233 static int Init( vout_thread_t *p_vout )
238 I_OUTPUTPICTURES = 0;
240 /* Initialize the output structure */
241 p_vout->output.i_chroma = p_vout->render.i_chroma;
242 p_vout->output.i_width = p_vout->render.i_width;
243 p_vout->output.i_height = p_vout->render.i_height;
244 p_vout->output.i_aspect = p_vout->render.i_aspect;
246 /* Try to open the real video output */
247 msg_Dbg( p_vout, "spawning the real video output" );
249 p_vout->p_sys->p_vout = vout_Create( p_vout,
250 p_vout->render.i_width, p_vout->render.i_height,
251 p_vout->render.i_chroma, p_vout->render.i_aspect );
252 if( p_vout->p_sys->p_vout == NULL )
254 msg_Err( p_vout, "failed to start vout" );
258 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
260 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
265 /*****************************************************************************
266 * End: terminate Clone video thread output method
267 *****************************************************************************/
268 static void End( vout_thread_t *p_vout )
272 /* Free the fake output buffers we allocated */
273 for( i_index = I_OUTPUTPICTURES ; i_index ; )
276 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
280 /*****************************************************************************
281 * Destroy: destroy Clone video thread output method
282 *****************************************************************************
283 * Terminate an output method created by CloneCreateOutputMethod
284 *****************************************************************************/
285 static void Destroy( vlc_object_t *p_this )
287 vout_thread_t *p_vout = (vout_thread_t *)p_this;
288 string_info_t *p_string1, *p_string2;
289 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
290 vlc_object_detach( p_vout->p_sys->p_vout );
291 vout_Destroy( p_vout->p_sys->p_vout );
292 vlc_mutex_destroy( p_vout->p_sys->lock );
293 p_string1 = p_vout->p_sys->p_strings;
296 p_string2 = p_string1->p_next;
297 FreeString( p_string1 );
298 p_string1 = p_string2;
300 FT_Done_Face( p_vout->p_sys->p_face );
301 FT_Done_FreeType( p_vout->p_sys->p_library );
302 free( p_vout->p_sys );
305 /*****************************************************************************
306 * SendEvents: forward mouse and keyboard events to the parent p_vout
307 *****************************************************************************/
308 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
309 vlc_value_t oldval, vlc_value_t newval, void *p_data )
311 var_Set( (vlc_object_t *)p_data, psz_var, newval );
317 /*****************************************************************************
318 * Render: displays previously rendered output
319 *****************************************************************************
320 * This function send the currently rendered image to Clone image, waits
321 * until it is displayed and switch the two rendering buffers, preparing next
323 *****************************************************************************/
324 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
326 picture_t *p_outpic = NULL;
327 int i_plane, i_error,x,y,pen_x, pen_y;
329 string_info_t *p_string;
333 vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 )
336 if( p_vout->b_die || p_vout->b_error )
339 p_vout->p_sys->p_vout, p_outpic );
343 msleep( VOUT_OUTMEM_SLEEP );
345 vout_DatePicture( p_vout->p_sys->p_vout,
346 p_outpic, p_pic->date );
347 if( p_vout->i_changes )
349 p_vout->p_sys->p_vout->i_changes = p_vout->i_changes;
350 p_vout->i_changes = 0;
354 /* trash old strings */
355 while( p_vout->p_sys->p_strings &&
356 p_vout->p_sys->p_strings->i_end_date < date )
358 p_string = p_vout->p_sys->p_strings;
359 p_vout->p_sys->p_strings = p_string->p_next;
360 msg_Dbg( p_vout, "trashing string "I64Fd" < "I64Fd, p_string->i_end_date, date );
361 FreeString( p_string );
365 vlc_mutex_lock( p_vout->p_sys->lock );
367 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
369 uint8_t *p_in, *p_in_end, *p_out;
370 int i_in_pitch = p_pic->p[i_plane].i_pitch;
371 const int i_out_pitch = p_outpic->p[i_plane].i_pitch;
372 const int i_copy_pitch = p_outpic->p[i_plane].i_visible_pitch;
374 p_in = p_pic->p[i_plane].p_pixels;
375 p_out = p_outpic->p[i_plane].p_pixels;
377 if( i_in_pitch == i_copy_pitch
378 && i_out_pitch == i_copy_pitch )
380 p_vout->p_vlc->pf_memcpy( p_out, p_in, i_in_pitch
381 * p_outpic->p[i_plane].i_lines );
385 p_in_end = p_in + i_in_pitch * p_outpic->p[i_plane].i_lines;
387 while( p_in < p_in_end )
389 p_vout->p_vlc->pf_memcpy( p_out, p_in, i_copy_pitch );
391 p_out += i_out_pitch;
398 for( p_string = p_vout->p_sys->p_strings; p_string != NULL;
399 p_string = p_string->p_next )
401 if( p_string->i_start_date > date )
405 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
407 pen_y = p_outpic->p[i_plane].i_lines - p_string->i_height -
408 p_string->i_y_margin;
412 pen_y = p_string->i_y_margin;
414 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
416 pen_x = i_out_pitch - p_string->i_width
417 - p_string->i_x_margin;
421 pen_x = p_string->i_x_margin;
424 for( i = 0; i < strlen( p_string->psz_text ); i++ )
426 if( p_string->pp_glyphs[i] )
428 FT_Glyph p_glyph = p_string->pp_glyphs[i];
429 FT_BitmapGlyph p_image;
430 i_error = FT_Glyph_To_Bitmap( &p_glyph,
431 FT_RENDER_MODE_NORMAL,
432 &p_string->p_glyph_pos[i],
434 if ( i_error ) continue;
435 p_image = (FT_BitmapGlyph)p_glyph;
436 #define alpha p_vout->p_sys->pi_gamma[p_image->bitmap.buffer[x+ y*p_image->bitmap.width]]
437 #define pixel p_out[(p_string->p_glyph_pos[i].y + pen_y + y - p_image->top)*i_out_pitch+x+pen_x+p_string->p_glyph_pos[i].x+p_image->left]
438 for(y = 0; y < p_image->bitmap.rows; y++ )
440 for( x = 0; x < p_image->bitmap.width; x++ )
443 // pixel = (pixel^alpha)^pixel;
444 pixel = ((pixel*(255-alpha))>>8) + (255*alpha>>8);
447 FT_Done_Glyph( p_glyph );
453 vlc_mutex_unlock( p_vout->p_sys->lock );
455 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
458 static int SetMargin ( vlc_object_t *p_this, char const *psz_command,
459 vlc_value_t oldval, vlc_value_t newval, void *p_data )
461 vout_thread_t *p_vout = (vout_thread_t*)p_this;
462 if( !strcmp( psz_command, "x-margin" ) )
464 p_vout->p_sys->i_x_margin = newval.i_int;
466 else if( !strcmp( psz_command, "y-margin" ) )
468 p_vout->p_sys->i_y_margin = newval.i_int;
470 else if( !strcmp( psz_command, "duration" ) )
472 p_vout->p_sys->i_duration = newval.i_int;
473 msg_Dbg( p_vout, "setting duration %d", p_vout->p_sys->i_duration );
475 else if( !strcmp( psz_command, "start-date" ) )
477 p_vout->p_sys->i_start_date = ( (mtime_t) newval.time.i_high << 32 )
480 else if( !strcmp( psz_command, "end-date" ) )
482 p_vout->p_sys->i_end_date = ( (mtime_t) newval.time.i_high << 32 )
485 else if( !strcmp( psz_command, "flags" ) )
487 p_vout->p_sys->i_flags = newval.i_int;
491 msg_Err( p_vout, "Invalid command" );
497 static int AddText ( vlc_object_t *p_this, char const *psz_command,
498 vlc_value_t oldval, vlc_value_t newval, void *p_data )
500 vout_thread_t *p_vout = (vout_thread_t*)p_this;
501 string_info_t **pp_string;
502 string_info_t *p_string;
504 int i, i_pen_x, i_error, i_glyph_index, i_previous;
506 p_string = malloc( sizeof(string_info_t) );
507 p_string->i_flags = p_vout->p_sys->i_flags;
508 p_string->i_x_margin = p_vout->p_sys->i_x_margin;
509 p_string->i_y_margin = p_vout->p_sys->i_y_margin;
510 if( p_vout->p_sys->i_start_date && p_vout->p_sys->i_end_date )
512 p_string->i_end_date = p_vout->p_sys->i_end_date;
513 p_string->i_start_date = p_vout->p_sys->i_start_date;
517 p_string->i_end_date = mdate() + p_vout->p_sys->i_duration;
518 p_string->i_start_date = 0;
520 p_vout->p_sys->i_end_date = 0;
521 p_vout->p_sys->i_start_date = 0;
522 p_string->psz_text = strdup( newval.psz_string );
523 p_string->pp_glyphs = malloc( sizeof(FT_GlyphSlot)
524 * strlen( p_string->psz_text ) );
525 if( p_string->pp_glyphs == NULL )
527 msg_Err( p_this, "Out of memory" );
530 p_string->p_glyph_pos = malloc( sizeof( FT_Vector )
531 * strlen( p_string->psz_text ) );
532 if( p_string->p_glyph_pos == NULL )
534 msg_Err( p_this, "Out of memory" );
538 /* Calculate relative glyph positions and a bounding box for the
542 psz_string = p_string->psz_text;
543 for( i = 0; *psz_string; i++, psz_string++ )
545 #define face p_vout->p_sys->p_face
546 #define glyph face->glyph
547 if ( *psz_string == '\n' )
550 p_string->pp_glyphs[ i ] = NULL;
553 i_glyph_index = FT_Get_Char_Index( face, *psz_string);
554 if ( p_vout->p_sys->i_use_kerning && i_glyph_index
558 FT_Get_Kerning( face, i_previous, i_glyph_index,
559 ft_kerning_default, &delta );
560 i_pen_x += delta.x >> 6;
563 p_string->p_glyph_pos[ i ].x = i_pen_x;
564 p_string->p_glyph_pos[ i ].y = 0;
565 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
568 msg_Err( p_this, "FT_Load_Glyph returned %d", i_error );
571 i_error = FT_Get_Glyph( glyph, &p_string->pp_glyphs[ i ] );
574 msg_Err( p_this, "FT_Get_Glyph returned %d", i_error );
578 i_previous = i_glyph_index;
579 i_pen_x += glyph->advance.x >> 6;
582 ComputeBoundingBox( p_string );
583 msg_Dbg( p_this, "string height is %d width is %d", p_string->i_height, p_string->i_width );
584 p_string->p_next = NULL;
585 msg_Dbg( p_this, "adding string \"%s\" at (%d,%d) start_date "I64Fd
586 " end_date" I64Fd, p_string->psz_text, p_string->i_x_margin,
587 p_string->i_y_margin, p_string->i_start_date,
588 p_string->i_end_date );
589 vlc_mutex_lock( p_vout->p_sys->lock );
590 pp_string = &p_vout->p_sys->p_strings;
591 while( *pp_string && (*pp_string)->i_end_date < p_string->i_end_date )
593 pp_string = &(*pp_string)->p_next;
595 p_string->p_next = (*pp_string);
596 *pp_string = p_string;
597 vlc_mutex_unlock( p_vout->p_sys->lock );
601 static void ComputeBoundingBox( string_info_t *p_string )
605 int i_firstline_height = 0;
616 for ( i = 0; i < strlen( p_string->psz_text ); i++ )
618 if ( p_string->psz_text[i] == '\n' )
620 result.x = __MAX( result.x, line.xMax );
621 result.y += line.yMax - line.yMin;
622 if ( !i_firstline_height )
624 i_firstline_height = result.y;
630 i_pen_y = result.y + 1;
633 p_string->p_glyph_pos[ i ].y = i_pen_y;
634 FT_Glyph_Get_CBox( p_string->pp_glyphs[i],
635 ft_glyph_bbox_pixels, &glyph_size );
637 line.xMax = p_string->p_glyph_pos[i].x + glyph_size.xMax - glyph_size.xMin;
638 line.yMax = __MAX( line.yMax, glyph_size.yMax );
639 line.yMin = __MIN( line.yMin, glyph_size.yMin );
641 result.x = __MAX( result.x, line.xMax );
642 result.y += line.yMax - line.yMin;
643 p_string->i_height = result.y;
644 p_string->i_width = result.x;
645 if ( !i_firstline_height )
647 i_firstline_height = result.y;
649 for ( i = 0; i < strlen( p_string->psz_text ); i++ )
651 p_string->p_glyph_pos[ i ].y += i_firstline_height;
656 static void FreeString( string_info_t *p_string )
659 for ( i = 0; i < strlen( p_string->psz_text ); i++ )
661 if ( p_string->pp_glyphs[ i ] )
663 FT_Done_Glyph( p_string->pp_glyphs[ i ] );
666 free( p_string->psz_text );
667 free( p_string->p_glyph_pos );
668 free( p_string->pp_glyphs );