]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
Added and used text_style_* methods.
[vlc] / modules / gui / fbosd.c
1 /*****************************************************************************
2  * fbosd.c : framebuffer osd plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2007-2008, the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman
8  * Copied from modules/video_output/fb.c by Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <errno.h>
36 #include <stdlib.h>                                                /* free() */
37 #include <string.h>                                            /* strerror() */
38 #include <fcntl.h>                                                 /* open() */
39 #include <unistd.h>                                               /* close() */
40
41 #include <sys/ioctl.h>
42 #include <sys/mman.h>                                              /* mmap() */
43
44 #include <linux/fb.h>
45
46 #include <vlc_image.h>
47 #include <vlc_interface.h>
48 #include <vlc_input.h>
49 #include <vlc_vout.h>
50 #include <vlc_filter.h>
51 #include <vlc_osd.h>
52 #include <vlc_strings.h>
53
54 #undef FBOSD_BLENDING
55 #undef FBOSD_DEBUG
56
57 /*****************************************************************************
58  * Local prototypes
59  *****************************************************************************/
60 static int  Create    ( vlc_object_t * );
61 static void Destroy   ( vlc_object_t * );
62 static void Run       ( intf_thread_t * );
63
64 static int  Init      ( intf_thread_t * );
65 static void End       ( intf_thread_t * );
66
67 static int  OpenDisplay    ( intf_thread_t * );
68 static void CloseDisplay   ( intf_thread_t * );
69
70 /* Load modules needed for rendering and blending */
71 #if defined(FBOSD_BLENDING)
72 static int  OpenBlending     ( intf_thread_t * );
73 static void CloseBlending    ( intf_thread_t * );
74 #endif
75 static int  OpenTextRenderer ( intf_thread_t * );
76 static void CloseTextRenderer( intf_thread_t * );
77
78 /* Manipulate the overlay buffer */
79 static int  OverlayCallback( vlc_object_t *, char const *,
80                              vlc_value_t, vlc_value_t, void * );
81
82 static picture_t *AllocatePicture( video_format_t * );
83 static void DeAllocatePicture( picture_t *, video_format_t * );
84 static void SetOverlayTransparency( intf_thread_t *,
85                                     bool );
86 static picture_t *LoadImage( intf_thread_t *, video_format_t *,
87                              char * );
88
89 #if defined(FBOSD_BLENDING)
90 static int BlendPicture( intf_thread_t *, video_format_t *,
91                          video_format_t *, picture_t *, picture_t * );
92 #else
93 static picture_t *ConvertImage( intf_thread_t *, picture_t *,
94                                 video_format_t *, video_format_t * );
95 #endif
96 static int RenderPicture( intf_thread_t *, int, int,
97                           picture_t *, picture_t * );
98 static picture_t *RenderText( intf_thread_t *, const char *,
99                               text_style_t *, video_format_t * );
100
101 #define DEVICE_TEXT N_("Framebuffer device")
102 #define DEVICE_LONGTEXT N_( \
103     "Framebuffer device to use for rendering (usually /dev/fb0).")
104
105 #define ASPECT_RATIO_TEXT N_("Video aspect ratio")
106 #define ASPECT_RATIO_LONGTEXT N_( \
107     "Aspect ratio of the video image (4:3, 16:9). Default is square pixels." )
108
109 #define FBOSD_IMAGE_TEXT N_("Image file")
110 #define FBOSD_IMAGE_LONGTEXT N_( \
111     "Filename of image file to use on the overlay framebuffer." )
112
113 #define ALPHA_TEXT N_("Transparency of the image")
114 #define ALPHA_LONGTEXT N_( "Transparency value of the new image " \
115     "used in blending. By default it set to fully opaque (255). " \
116     "(from 0 for full transparency to 255 for full opacity)" )
117
118 #define FBOSD_TEXT N_("Text")
119 #define FBOSD_LONGTEXT N_( "Text to display on the overlay framebuffer." )
120
121 #define POSX_TEXT N_("X coordinate")
122 #define POSX_LONGTEXT N_("X coordinate of the rendered image")
123
124 #define POSY_TEXT N_("Y coordinate")
125 #define POSY_LONGTEXT N_("Y coordinate of the rendered image")
126
127 #define POS_TEXT N_("Position")
128 #define POS_LONGTEXT N_( \
129   "You can enforce the picture position on the overlay " \
130   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
131   "also use combinations of these values, e.g. 6=top-right).")
132
133 #define OPACITY_TEXT N_("Opacity")
134 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
135     "overlayed text. 0 = transparent, 255 = totally opaque. " )
136
137 #define SIZE_TEXT N_("Font size, pixels")
138 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
139     "font size)." )
140
141 #define COLOR_TEXT N_("Color")
142 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
143     "the video. This must be an hexadecimal (like HTML colors). The first two "\
144     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
145     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
146
147 #define CLEAR_TEXT N_( "Clear overlay framebuffer" )
148 #define CLEAR_LONGTEXT N_( "The displayed overlay images is cleared by " \
149     "making the overlay completely transparent. All previously rendered " \
150     "images and text will be cleared from the cache." )
151
152 #define RENDER_TEXT N_( "Render text or image" )
153 #define RENDER_LONGTEXT N_( "Render the image or text in current overlay " \
154     "buffer." )
155
156 #define DISPLAY_TEXT N_( "Display on overlay framebuffer" )
157 #define DISPLAY_LONGTEXT N_( "All rendered images and text will be " \
158     "displayed on the overlay framebuffer." )
159
160 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
161 static const char *const ppsz_pos_descriptions[] =
162 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
163   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
164
165 static const int pi_color_values[] = {
166                0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
167                0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
168                0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
169                0x00000080, 0x000000FF, 0x0000FFFF};
170 static const char *const ppsz_color_descriptions[] = {
171                N_("Default"), N_("Black"),
172                N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
173                N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
174                N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
175                N_("Aqua") };
176
177 vlc_module_begin ()
178     set_shortname( "fbosd" )
179     set_category( CAT_INTERFACE )
180     set_subcategory( SUBCAT_INTERFACE_MAIN )
181
182     add_file( "fbosd-dev", "/dev/fb0", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
183               false )
184     add_string( "fbosd-aspect-ratio", "", NULL, ASPECT_RATIO_TEXT,
185                 ASPECT_RATIO_LONGTEXT, true )
186
187     add_string( "fbosd-image", NULL, NULL, FBOSD_IMAGE_TEXT,
188                 FBOSD_IMAGE_LONGTEXT, true )
189     add_string( "fbosd-text", NULL, NULL, FBOSD_TEXT,
190                 FBOSD_LONGTEXT, true )
191
192     add_integer_with_range( "fbosd-alpha", 255, 0, 255, NULL, ALPHA_TEXT,
193                             ALPHA_LONGTEXT, true )
194
195     set_section( N_("Position"), NULL )
196     add_integer( "fbosd-x", 0, NULL, POSX_TEXT,
197                  POSX_LONGTEXT, false )
198     add_integer( "fbosd-y", 0, NULL, POSY_TEXT,
199                  POSY_LONGTEXT, false )
200     add_integer( "fbosd-position", 8, NULL, POS_TEXT, POS_LONGTEXT, true )
201         change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL );
202
203     set_section( N_("Font"), NULL )
204     add_integer_with_range( "fbosd-font-opacity", 255, 0, 255, NULL,
205         OPACITY_TEXT, OPACITY_LONGTEXT, false )
206     add_integer( "fbosd-font-color", 0x00FFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
207                  false )
208         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
209     add_integer( "fbosd-font-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
210                  false )
211
212     set_section( N_("Commands"), NULL )
213     add_bool( "fbosd-clear", false, NULL, CLEAR_TEXT, CLEAR_LONGTEXT, true )
214     add_bool( "fbosd-render", false, NULL, RENDER_TEXT, RENDER_LONGTEXT, true )
215     add_bool( "fbosd-display", false, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, true )
216
217     set_description( N_("GNU/Linux osd/overlay framebuffer interface") )
218     set_capability( "interface", 10 )
219     set_callbacks( Create, Destroy )
220 vlc_module_end ()
221
222 /*****************************************************************************
223  * fbosd_render_t: render descriptor
224  *****************************************************************************/
225 struct fbosd_render_t
226 {
227 #define FBOSD_RENDER_IMAGE 0
228 #define FBOSD_RENDER_TEXT  1
229     int             i_type;
230
231 #define FBOSD_STATE_FREE     0
232 #define FBOSD_STATE_RESERVED 1
233 #define FBOSD_STATE_RENDER   2
234     int             i_state;
235
236     /* Font style */
237     text_style_t    text_style;                              /* font control */
238     char            *psz_string;
239
240     /* Position */
241     bool            b_absolute;
242     int             i_x;
243     int             i_y;
244     int             i_pos;
245     int             i_alpha;                      /* transparency for images */
246 };
247 #define FBOSD_RENDER_MAX 10
248
249 /*****************************************************************************
250  * intf_sys_t: interface framebuffer method descriptor
251  *****************************************************************************/
252 struct intf_sys_t
253 {
254     /* Framebuffer information */
255     int                         i_fd;                       /* device handle */
256     struct fb_var_screeninfo    var_info;        /* current mode information */
257     bool                  b_pan;     /* does device supports panning ? */
258     struct fb_cmap              fb_cmap;                /* original colormap */
259     uint16_t                    *p_palette;              /* original palette */
260
261     /* Overlay framebuffer format */
262     video_format_t  fmt_out;
263     picture_t       *p_overlay;
264     size_t          i_page_size;                                /* page size */
265     int             i_width;
266     int             i_height;
267     int             i_aspect;
268     int             i_bytes_per_pixel;
269
270     /* Image and Picture rendering */
271     image_handler_t *p_image;
272 #if defined(FBOSD_BLENDING)
273     filter_t *p_blend;                              /* alpha blending module */
274 #endif
275     filter_t *p_text;                                /* text renderer module */
276
277     /* Render */
278     struct fbosd_render_t render[FBOSD_RENDER_MAX];
279
280     /* Font style */
281     text_style_t    *p_style;                                /* font control */
282
283     /* Position */
284     bool      b_absolute;
285     int       i_x;
286     int       i_y;
287     int       i_pos;
288
289     int       i_alpha;                      /* transparency for images */
290
291     /* commands control */
292     bool      b_need_update;    /* update display with \overlay buffer */
293     bool      b_clear;      /* clear overlay buffer make it tranparent */
294     bool      b_render;   /* render an image or text in overlay buffer */
295 };
296
297 /*****************************************************************************
298  * Create: allocates FB interface thread output method
299  *****************************************************************************/
300 static int Create( vlc_object_t *p_this )
301 {
302     intf_thread_t *p_intf = (intf_thread_t *)p_this;
303     intf_sys_t    *p_sys;
304     char          *psz_aspect;
305     char          *psz_tmp;
306     int i;
307
308     /* Allocate instance and initialize some members */
309     p_intf->p_sys = p_sys = calloc( 1, sizeof( intf_sys_t ) );
310     if( !p_intf->p_sys )
311         return VLC_ENOMEM;
312
313     p_sys->p_style = text_style_New();
314     if( !p_sys->p_style )
315     {
316         free( p_intf->p_sys );
317         return VLC_ENOMEM;
318     }
319
320     p_intf->pf_run = Run;
321
322     p_sys->p_image = image_HandlerCreate( p_this );
323     if( !p_sys->p_image )
324     {
325         text_style_Delete( p_sys->p_style );
326         free( p_sys );
327         return VLC_ENOMEM;
328     }
329
330     p_sys->i_alpha = var_CreateGetIntegerCommand( p_intf, "fbosd-alpha" );
331     var_AddCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
332
333     p_sys->i_aspect = -1;
334     psz_aspect =
335             var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
336     if( psz_aspect )
337     {
338         char *psz_parser = strchr( psz_aspect, ':' );
339
340         if( psz_parser )
341         {
342             *psz_parser++ = '\0';
343             p_sys->i_aspect = ( atoi( psz_aspect )
344                               * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
345             p_sys->fmt_out.i_aspect = p_sys->i_aspect;
346         }
347         msg_Dbg( p_intf, "using aspect ratio %d:%d",
348                   atoi( psz_aspect ), atoi( psz_parser ) );
349
350         free( psz_aspect );
351     }
352
353     /* Use PAL by default */
354     p_sys->i_width  = p_sys->fmt_out.i_width  = 704;
355     p_sys->i_height = p_sys->fmt_out.i_height = 576;
356
357     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
358     var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
359     if( psz_tmp && *psz_tmp )
360     {
361         p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
362         p_sys->render[0].i_state = FBOSD_STATE_RENDER;
363         p_sys->render[0].psz_string = strdup( psz_tmp );
364     }
365     free( psz_tmp );
366
367     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
368     var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
369     if( psz_tmp && *psz_tmp )
370     {
371         p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
372         p_sys->render[1].i_state = FBOSD_STATE_RENDER;
373         p_sys->render[1].psz_string = strdup( psz_tmp );
374     }
375     free( psz_tmp );
376
377     p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
378     p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
379     p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );
380
381     var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
382     var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
383     var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
384
385     p_sys->p_style->i_font_size =
386             var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
387     p_sys->p_style->i_font_color =
388             var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
389     p_sys->p_style->i_font_alpha = 255 -
390             var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );
391
392     var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
393     var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
394     var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
395
396     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
397     {
398         vlc_memcpy( &p_sys->render[i].text_style, &default_text_style,
399                     sizeof( text_style_t ) );
400     }
401
402     p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
403     p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
404     p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
405
406     var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
407     var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
408     var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
409
410     /* Check if picture position was overridden */
411     p_sys->b_absolute = true;
412     if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
413     {
414         p_sys->b_absolute = false;
415         p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
416                         p_sys->i_y : p_sys->i_height;
417         p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
418                         p_sys->i_x : p_sys->i_width;
419     }
420
421     p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
422     p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
423     p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
424     p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
425
426     /* Initialize framebuffer */
427     if( OpenDisplay( p_intf ) )
428     {
429         Destroy( VLC_OBJECT(p_intf) );
430         return VLC_EGENERIC;
431     }
432
433     Init( p_intf );
434
435 #if defined(FBOSD_BLENDING)
436     /* Load the blending module */
437     if( OpenBlending( p_intf ) )
438     {
439         msg_Err( p_intf, "Unable to load image blending module" );
440         Destroy( VLC_OBJECT(p_intf) );
441         return VLC_EGENERIC;
442     }
443 #endif
444
445     /* Load text renderer module */
446     if( OpenTextRenderer( p_intf ) )
447     {
448         msg_Err( p_intf, "Unable to load text rendering module" );
449         Destroy( VLC_OBJECT(p_intf) );
450         return VLC_EGENERIC;
451     }
452
453     p_sys->b_render = true;
454     p_sys->b_need_update = true;
455
456     return VLC_SUCCESS;
457 }
458
459 /*****************************************************************************
460  * Destroy: destroy FB interface thread output method
461  *****************************************************************************
462  * Terminate an output method created by Create
463  *****************************************************************************/
464 static void Destroy( vlc_object_t *p_this )
465 {
466     intf_thread_t *p_intf = (intf_thread_t *)p_this;
467     intf_sys_t *p_sys = p_intf->p_sys;
468     int i;
469
470
471     p_sys->b_need_update = false;
472     p_sys->b_render = false;
473     p_sys->b_clear = false;
474
475     var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
476     var_Destroy( p_intf, "fbosd-alpha" );
477
478     var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
479     var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
480     var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
481     var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
482     var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
483     var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
484     var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
485     var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
486     var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
487     var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
488     var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
489
490     var_Destroy( p_intf, "fbosd-x" );
491     var_Destroy( p_intf, "fbosd-y" );
492     var_Destroy( p_intf, "fbosd-position" );
493     var_Destroy( p_intf, "fbosd-image" );
494     var_Destroy( p_intf, "fbosd-text" );
495     var_Destroy( p_intf, "fbosd-font-size" );
496     var_Destroy( p_intf, "fbosd-font-color" );
497     var_Destroy( p_intf, "fbosd-font-opacity" );
498     var_Destroy( p_intf, "fbosd-clear" );
499     var_Destroy( p_intf, "fbosd-render" );
500     var_Destroy( p_intf, "fbosd-display" );
501
502     var_Destroy( p_intf, "fbosd-aspect-ratio" );
503
504     CloseDisplay( p_intf );
505
506     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
507     {
508         free( p_sys->render[i].psz_string );
509         p_sys->render[i].i_state = FBOSD_STATE_FREE;
510     }
511
512 #if defined(FBOSD_BLENDING)
513     if( p_sys->p_blend ) CloseBlending( p_intf );
514 #endif
515     if( p_sys->p_text )  CloseTextRenderer( p_intf );
516
517     if( p_sys->p_image )
518         image_HandlerDelete( p_sys->p_image );
519     if( p_sys->p_overlay )
520         picture_Release( p_sys->p_overlay );
521
522     text_style_Delete( p_sys->p_style );
523     free( p_sys );
524 }
525
526 #if defined(FBOSD_BLENDING)
527 static int OpenBlending( intf_thread_t *p_intf )
528 {
529     if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
530
531     p_intf->p_sys->p_blend =
532             vlc_object_create( p_intf, sizeof(filter_t) );
533     vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
534     p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
535         p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
536     p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
537             p_intf->p_sys->fmt_out.i_aspect;
538     p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
539             p_intf->p_sys->fmt_out.i_chroma;
540     if( config_GetInt( p_intf, "freetype-yuvp" ) )
541         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
542                 VLC_FOURCC('Y','U','V','P');
543     else
544         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
545                 VLC_FOURCC('Y','U','V','A');
546
547     p_intf->p_sys->p_blend->p_module =
548         module_need( p_intf->p_sys->p_blend, "video blending", NULL, false );
549
550     if( !p_intf->p_sys->p_blend->p_module )
551         return VLC_EGENERIC;
552
553     return VLC_SUCCESS;
554 }
555
556 static void CloseBlending( intf_thread_t *p_intf )
557 {
558     if( p_intf->p_sys->p_blend )
559     {
560         if( p_intf->p_sys->p_blend->p_module )
561             module_unneed( p_intf->p_sys->p_blend,
562                            p_intf->p_sys->p_blend->p_module );
563
564         vlc_object_detach( p_intf->p_sys->p_blend );
565         vlc_object_release( p_intf->p_sys->p_blend );
566     }
567 }
568 #endif
569
570 static int OpenTextRenderer( intf_thread_t *p_intf )
571 {
572     char *psz_modulename = NULL;
573
574     if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
575
576     p_intf->p_sys->p_text =
577             vlc_object_create( p_intf, sizeof(filter_t) );
578     vlc_object_attach( p_intf->p_sys->p_text, p_intf );
579
580     p_intf->p_sys->p_text->fmt_out.video.i_width =
581         p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
582         p_intf->p_sys->i_width;
583     p_intf->p_sys->p_text->fmt_out.video.i_height =
584         p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
585         p_intf->p_sys->i_height;
586
587     psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
588     if( psz_modulename && *psz_modulename )
589     {
590         p_intf->p_sys->p_text->p_module =
591             module_need( p_intf->p_sys->p_text, "text renderer",
592                             psz_modulename, true );
593     }
594     if( !p_intf->p_sys->p_text->p_module )
595     {
596         p_intf->p_sys->p_text->p_module =
597             module_need( p_intf->p_sys->p_text, "text renderer", NULL, false );
598     }
599     free( psz_modulename );
600
601     if( !p_intf->p_sys->p_text->p_module )
602         return VLC_EGENERIC;
603
604     return VLC_SUCCESS;
605 }
606
607 static void CloseTextRenderer( intf_thread_t *p_intf )
608 {
609     if( p_intf->p_sys->p_text )
610     {
611         if( p_intf->p_sys->p_text->p_module )
612             module_unneed( p_intf->p_sys->p_text,
613                            p_intf->p_sys->p_text->p_module );
614
615         vlc_object_detach( p_intf->p_sys->p_text );
616         vlc_object_release( p_intf->p_sys->p_text );
617     }
618 }
619
620 /*****************************************************************************
621  * AllocatePicture:
622  * allocate a picture buffer for use with the overlay fb.
623  *****************************************************************************/
624 static picture_t *AllocatePicture( video_format_t *p_fmt )
625 {
626     picture_t *p_picture = picture_New( p_fmt->i_chroma,
627                                         p_fmt->i_width, p_fmt->i_height,
628                                         p_fmt->i_aspect );
629     if( !p_picture )
630         return NULL;
631
632     if( !p_fmt->p_palette &&
633         ( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) )
634     {
635         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
636         if( !p_fmt->p_palette )
637         {
638             picture_Release( p_picture );
639             return NULL;
640         }
641     }
642     else
643     {
644         p_fmt->p_palette = NULL;
645     }
646
647     return p_picture;
648 }
649
650 /*****************************************************************************
651  * DeAllocatePicture:
652  * Deallocate a picture buffer and free all associated memory.
653  *****************************************************************************/
654 static void DeAllocatePicture( picture_t *p_pic, video_format_t *p_fmt )
655 {
656     if( p_fmt )
657     {
658         free( p_fmt->p_palette );
659         p_fmt->p_palette = NULL;
660     }
661
662     if( p_pic )
663         picture_Release( p_pic );
664 }
665
666 /*****************************************************************************
667  * SetOverlayTransparency: Set the transparency for this overlay fb,
668  * - true is make transparent
669  * - false is make non tranparent
670  *****************************************************************************/
671 static void SetOverlayTransparency( intf_thread_t *p_intf,
672                                     bool b_transparent )
673 {
674     intf_sys_t *p_sys = p_intf->p_sys;
675     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
676                         * p_sys->i_bytes_per_pixel;
677     size_t i_page_size = (p_sys->i_page_size > i_size) ?
678                             i_size : p_sys->i_page_size;
679
680     if( p_sys->p_overlay )
681     {
682         msg_Dbg( p_intf, "Make overlay %s",
683                  b_transparent ? "transparent" : "opaque" );
684         if( b_transparent )
685             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
686         else
687             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
688     }
689 }
690
691 #if defined(FBOSD_BLENDING)
692 /*****************************************************************************
693  * BlendPicture: Blend two pictures together..
694  *****************************************************************************/
695 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
696                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
697                          picture_t *p_pic_dst )
698 {
699     intf_sys_t *p_sys = p_intf->p_sys;
700     if( p_sys->p_blend && p_sys->p_blend->p_module )
701     {
702         int i_x_offset = p_sys->i_x;
703         int i_y_offset = p_sys->i_y;
704
705         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
706
707         /* Update the output picture size */
708         p_sys->p_blend->fmt_out.video.i_width =
709             p_sys->p_blend->fmt_out.video.i_visible_width =
710                 p_fmt_dst->i_width;
711         p_sys->p_blend->fmt_out.video.i_height =
712             p_sys->p_blend->fmt_out.video.i_visible_height =
713                 p_fmt_dst->i_height;
714
715         i_x_offset = __MAX( i_x_offset, 0 );
716         i_y_offset = __MAX( i_y_offset, 0 );
717
718         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
719             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
720             p_sys->i_alpha );
721
722         return VLC_SUCCESS;
723     }
724     return VLC_EGENERIC;
725 }
726
727 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
728 {
729     uint8_t *p_begin = NULL, *p_end = NULL;
730     uint8_t i_skip = 0;
731
732     if( *p_pic && ((*p_pic)->i_planes != 1) )
733     {
734         msg_Err( p_intf,
735                  "cannot invert alpha channel too many planes %d (only 1 supported)",
736                  (*p_pic)->i_planes );
737         return VLC_EGENERIC;
738     }
739
740     switch( fmt.i_chroma )
741     {
742         case VLC_FOURCC('R','V','2','4'):
743             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
744             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
745                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
746             i_skip = 3;
747             break;
748         case VLC_FOURCC('R','V','3','2'):
749             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
750             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
751                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
752             i_skip = 4;
753             break;
754         default:
755             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
756                     (char *)&fmt.i_chroma );
757             return VLC_EGENERIC;
758     }
759
760     for( ; p_begin < p_end; p_begin += i_skip )
761     {
762         uint8_t i_opacity = 0;
763
764         if( *p_begin != 0xFF )
765             i_opacity = 255 - *p_begin;
766         *p_begin = i_opacity;
767     }
768     /* end of kludge */
769     return VLC_SUCCESS;
770 }
771 #endif
772
773 /*****************************************************************************
774  * RenderPicture: Render the picture into the p_dest buffer.
775  * We don't take transparent pixels into account, so we don't have to blend
776  * the two images together.
777  *****************************************************************************/
778 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
779                           picture_t *p_src, picture_t *p_dest )
780 {
781     int i;
782     VLC_UNUSED( p_intf );
783
784     if( !p_dest && !p_src ) return VLC_EGENERIC;
785
786     for( i = 0; i < p_src->i_planes ; i++ )
787     {
788         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
789         {
790             /* There are margins, but with the same width : perfect ! */
791             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
792                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
793         }
794         else
795         {
796             /* We need to proceed line by line */
797             uint8_t *p_in  = p_src->p[i].p_pixels;
798             uint8_t *p_out = p_dest->p[i].p_pixels;
799
800             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
801             int i_x_clip, i_y_clip;
802
803             /* Check boundaries, clip the image if necessary */
804             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
805             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
806
807             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
808             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
809 #if defined(FBOSD_DEBUG)
810             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
811                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
812                      i_x_offset, i_y_offset, i_x, i_x_clip );
813 #endif
814             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
815                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
816             {
817                 int i_line;
818
819                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
820                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
821                 {
822                     vlc_memcpy( p_out + i_x, p_in,
823                                 p_src->p[i].i_visible_pitch - i_x_clip );
824                     p_in += p_src->p[i].i_pitch;
825                     p_out += p_dest->p[i].i_pitch;
826                 }
827             }
828         }
829     }
830     return VLC_SUCCESS;
831 }
832
833 /*****************************************************************************
834  * RenderText - Render text to the desired picture format
835  *****************************************************************************/
836 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
837                               text_style_t *p_style, video_format_t *p_fmt )
838 {
839     intf_sys_t *p_sys = p_intf->p_sys;
840     subpicture_region_t *p_region;
841     picture_t *p_dest = NULL;
842
843     if( !psz_string ) return p_dest;
844
845     if( p_sys->p_text && p_sys->p_text->p_module )
846     {
847         video_format_t fmt;
848
849         memset( &fmt, 0, sizeof(fmt) );
850         fmt.i_chroma = VLC_FOURCC('T','E','X','T');
851         fmt.i_aspect = 0;
852         fmt.i_width  = fmt.i_visible_width = 0;
853         fmt.i_height = fmt.i_visible_height = 0;
854         fmt.i_x_offset = 0;
855         fmt.i_y_offset = 0;
856
857         p_region = subpicture_region_New( &fmt );
858         if( !p_region )
859             return p_dest;
860
861         p_region->psz_text = strdup( psz_string );
862         if( !p_region->psz_text )
863         {
864             subpicture_region_Delete( p_region );
865             return NULL;
866         }
867         p_region->p_style = text_style_Duplicate( p_style );
868         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
869
870         if( p_sys->p_text->pf_render_text )
871         {
872             video_format_t fmt_out;
873
874             memset( &fmt_out, 0, sizeof(video_format_t) );
875
876             p_sys->p_text->pf_render_text( p_sys->p_text,
877                                            p_region, p_region );
878
879 #if defined(FBOSD_BLENDING)
880             fmt_out = p_region->fmt;
881             fmt_out.i_bits_per_pixel = 32;
882             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
883
884             /* FIXME not needed to copy the picture anymore no ? */
885             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
886             if( !p_dest )
887             {
888                 subpicture_region_Delete( p_region );
889                 return NULL;
890             }
891             picture_Copy( p_dest, p_region->p_picture );
892 #else
893             fmt_out.i_chroma = p_fmt->i_chroma;
894             p_dest = ConvertImage( p_intf, p_region->p_picture,
895                                    &p_region->fmt, &fmt_out );
896 #endif
897             subpicture_region_Delete( p_region );
898             return p_dest;
899         }
900         subpicture_region_Delete( p_region );
901     }
902     return p_dest;
903 }
904
905 /*****************************************************************************
906  * LoadImage: Load an image from file into a picture buffer.
907  *****************************************************************************/
908 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
909                              char *psz_file )
910 {
911     picture_t  *p_pic = NULL;
912
913     if( psz_file && p_intf->p_sys->p_image )
914     {
915         video_format_t fmt_in, fmt_out;
916
917         memset( &fmt_in, 0, sizeof(fmt_in) );
918         memset( &fmt_out, 0, sizeof(fmt_out) );
919
920         fmt_out.i_chroma = p_fmt->i_chroma;
921         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
922                                &fmt_in, &fmt_out );
923
924         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
925                  fmt_out.i_width, fmt_out.i_height,
926                  (char *)&p_fmt->i_chroma );
927     }
928     return p_pic;
929 }
930
931 #if ! defined(FBOSD_BLENDING)
932 /*****************************************************************************
933  * Convertmage: Convert image to another fourcc
934  *****************************************************************************/
935 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
936                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
937 {
938     intf_sys_t *p_sys = p_intf->p_sys;
939     picture_t  *p_old = NULL;
940
941     if( p_sys->p_image )
942     {
943         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
944
945         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
946                  p_fmt_out->i_width, p_fmt_out->i_height,
947                  (char *)&p_fmt_out->i_chroma );
948     }
949     return p_old;
950 }
951 #endif
952
953 /*****************************************************************************
954  * Init: initialize framebuffer video thread output method
955  *****************************************************************************/
956 static int Init( intf_thread_t *p_intf )
957 {
958     intf_sys_t *p_sys = p_intf->p_sys;
959
960     /* Initialize the output structure: RGB with square pixels, whatever
961      * the input format is, since it's the only format we know */
962     switch( p_sys->var_info.bits_per_pixel )
963     {
964     case 8: /* FIXME: set the palette */
965         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
966     case 15:
967         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
968     case 16:
969         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
970     case 24:
971         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
972     case 32:
973         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
974     default:
975         msg_Err( p_intf, "unknown screen depth %i",
976                  p_sys->var_info.bits_per_pixel );
977         return VLC_EGENERIC;
978     }
979
980     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
981     p_sys->fmt_out.i_width  = p_sys->i_width;
982     p_sys->fmt_out.i_height = p_sys->i_height;
983
984     /* Assume we have square pixels */
985     if( p_sys->i_aspect < 0 )
986     {
987         p_sys->fmt_out.i_aspect = ( p_sys->i_width
988                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
989     }
990     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
991
992     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
993
994     /* Allocate overlay buffer */
995     p_sys->p_overlay = AllocatePicture( &p_sys->fmt_out );
996     if( !p_sys->p_overlay ) return VLC_EGENERIC;
997
998     SetOverlayTransparency( p_intf, true );
999
1000     /* We know the chroma, allocate a buffer which will be used
1001      * to write to the overlay framebuffer */
1002     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1003     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1004     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1005
1006     if( p_sys->var_info.xres_virtual )
1007     {
1008         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1009                              * p_sys->i_bytes_per_pixel;
1010     }
1011     else
1012     {
1013         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1014                              * p_sys->i_bytes_per_pixel;
1015     }
1016
1017     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1018                                  * p_sys->i_bytes_per_pixel;
1019
1020     p_sys->p_overlay->i_planes = 1;
1021
1022     return VLC_SUCCESS;
1023 }
1024
1025 /*****************************************************************************
1026  * End: terminate framebuffer interface
1027  *****************************************************************************/
1028 static void End( intf_thread_t *p_intf )
1029 {
1030     intf_sys_t *p_sys = p_intf->p_sys;
1031
1032     /* CleanUp */
1033     SetOverlayTransparency( p_intf, false );
1034     if( p_sys->p_overlay )
1035     {
1036         int ret;
1037         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1038                      p_sys->i_page_size );
1039         if( ret < 0 )
1040             msg_Err( p_intf, "unable to clear overlay" );
1041     }
1042
1043     DeAllocatePicture( p_intf->p_sys->p_overlay,
1044                        &p_intf->p_sys->fmt_out );
1045     p_intf->p_sys->p_overlay = NULL;
1046 }
1047
1048 /*****************************************************************************
1049  * OpenDisplay: initialize framebuffer
1050  *****************************************************************************/
1051 static int OpenDisplay( intf_thread_t *p_intf )
1052 {
1053     intf_sys_t *p_sys = p_intf->p_sys;
1054     char *psz_device;                             /* framebuffer device path */
1055     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1056
1057     /* Open framebuffer device */
1058     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1059     {
1060         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1061         return VLC_EGENERIC;
1062     }
1063
1064     p_sys->i_fd = open( psz_device, O_RDWR );
1065     if( p_sys->i_fd == -1 )
1066     {
1067         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1068         free( psz_device );
1069         return VLC_EGENERIC;
1070     }
1071     free( psz_device );
1072
1073     /* Get framebuffer device information */
1074     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1075     {
1076         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1077         close( p_sys->i_fd );
1078         return VLC_EGENERIC;
1079     }
1080
1081     /* Get some info on the framebuffer itself */
1082     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1083     {
1084         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1085         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1086     }
1087
1088     /* FIXME: if the image is full-size, it gets cropped on the left
1089      * because of the xres / xres_virtual slight difference */
1090     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1091              p_sys->var_info.xres, p_sys->var_info.yres,
1092              p_sys->var_info.xres_virtual,
1093              p_sys->var_info.yres_virtual );
1094
1095     p_sys->fmt_out.i_width = p_sys->i_width;
1096     p_sys->fmt_out.i_height = p_sys->i_height;
1097
1098     p_sys->p_palette = NULL;
1099     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1100
1101     switch( p_sys->var_info.bits_per_pixel )
1102     {
1103     case 8:
1104         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1105         if( !p_sys->p_palette )
1106         {
1107             close( p_sys->i_fd );
1108             return VLC_ENOMEM;
1109         }
1110         p_sys->fb_cmap.start = 0;
1111         p_sys->fb_cmap.len = 256;
1112         p_sys->fb_cmap.red = p_sys->p_palette;
1113         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1114         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1115         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1116
1117         /* Save the colormap */
1118         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1119
1120         p_sys->i_bytes_per_pixel = 1;
1121         break;
1122
1123     case 15:
1124     case 16:
1125         p_sys->i_bytes_per_pixel = 2;
1126         break;
1127
1128     case 24:
1129         p_sys->i_bytes_per_pixel = 3;
1130         break;
1131
1132     case 32:
1133         p_sys->i_bytes_per_pixel = 4;
1134         break;
1135
1136     default:
1137         msg_Err( p_intf, "screen depth %d is not supported",
1138                          p_sys->var_info.bits_per_pixel );
1139
1140         close( p_sys->i_fd );
1141         return VLC_EGENERIC;
1142     }
1143
1144     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1145                          * p_sys->i_bytes_per_pixel;
1146
1147     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1148              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1149              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1150     return VLC_SUCCESS;
1151 }
1152
1153 /*****************************************************************************
1154  * CloseDisplay: terminate FB interface thread
1155  *****************************************************************************/
1156 static void CloseDisplay( intf_thread_t *p_intf )
1157 {
1158     intf_sys_t *p_sys = p_intf->p_sys;
1159
1160     /* Restore palette */
1161     if( p_sys->var_info.bits_per_pixel == 8 )
1162     {
1163         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1164         free( p_sys->p_palette );
1165         p_sys->p_palette = NULL;
1166     }
1167
1168     /* Close fb */
1169     close( p_sys->i_fd );
1170 }
1171
1172 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1173 {
1174     intf_sys_t *p_sys = p_intf->p_sys;
1175
1176     if( render->i_state != FBOSD_STATE_RENDER ) return;
1177     if( !render->psz_string ) return;
1178
1179     if( render->i_type == FBOSD_RENDER_IMAGE )
1180     {
1181         picture_t *p_pic;
1182         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1183         if( p_pic )
1184         {
1185             RenderPicture( p_intf, render->i_x, render->i_y,
1186                            p_pic, p_sys->p_overlay );
1187             picture_Release( p_pic );
1188         }
1189     }
1190     else if( render->i_type == FBOSD_RENDER_TEXT )
1191     {
1192         picture_t *p_text;
1193 #if defined(FBOSD_BLENDING)
1194         video_format_t fmt_in;
1195         memset( &fmt_in, 0, sizeof(video_format_t) );
1196         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1197                              &fmt_in );
1198         if( p_text )
1199         {
1200             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1201                           p_text, p_sys->p_overlay );
1202             msg_Dbg( p_intf, "releasing picture" );
1203             DeAllocatePicture( p_text, &fmt_in );
1204         }
1205 #else
1206         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1207                              &p_sys->fmt_out );
1208         if( p_text )
1209         {
1210             RenderPicture( p_intf, render->i_x, render->i_y,
1211                            p_text, p_sys->p_overlay );
1212             picture_Release( p_text );
1213         }
1214 #endif
1215     }
1216 }
1217
1218 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1219 {
1220     intf_sys_t *p_sys = p_intf->p_sys;
1221
1222     vlc_memcpy( &render->text_style, &default_text_style,
1223                 sizeof( text_style_t ) );
1224     free( render->psz_string );
1225     render->psz_string = NULL;
1226
1227     render->i_x = p_sys->i_x;
1228     render->i_y = p_sys->i_y;
1229     render->i_pos = p_sys->i_pos;
1230     render->i_alpha = p_sys->i_alpha;
1231     render->b_absolute = p_sys->b_absolute;
1232     render->i_state = FBOSD_STATE_FREE;
1233 }
1234
1235 static bool isRendererReady( intf_thread_t *p_intf )
1236 {
1237     intf_sys_t *p_sys = p_intf->p_sys;
1238     int i;
1239
1240     /* Check if there are more items to render */
1241     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1242     {
1243         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1244             return false;
1245     }
1246     return true;
1247 }
1248
1249 /*****************************************************************************
1250  * Run: thread
1251  *****************************************************************************
1252  * This part of the interface is in a separate thread so that we can call
1253  * exec() from within it without annoying the rest of the program.
1254  *****************************************************************************/
1255 static void Run( intf_thread_t *p_intf )
1256 {
1257     intf_sys_t *p_sys = p_intf->p_sys;
1258     int canc = vlc_savecancel();
1259
1260     while( vlc_object_alive( p_intf ) )
1261     {
1262         int i;
1263
1264         /* Is there somthing to render? */
1265         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1266         {
1267             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1268             {
1269                 Render( p_intf, &p_sys->render[i] );
1270                 RenderClear( p_intf, &p_sys->render[i] );
1271             }
1272         }
1273
1274         if( p_sys->b_clear )
1275         {
1276             SetOverlayTransparency( p_intf, true );
1277
1278             var_SetString( p_intf, "fbosd-image", "" );
1279             var_SetString( p_intf, "fbosd-text", "" );
1280
1281             p_sys->b_clear = false;
1282             p_sys->b_need_update = true;
1283         }
1284
1285         if( p_sys->b_need_update && p_sys->p_overlay &&
1286             isRendererReady( p_intf ) )
1287         {
1288             int ret;
1289 #if defined(FBOSD_BLENDING)
1290             /* Reverse alpha channel to work around FPGA bug */
1291             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1292 #endif
1293             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1294                          p_sys->i_page_size );
1295             if( ret < 0 )
1296                 msg_Err( p_intf, "unable to write to overlay" );
1297             lseek( p_sys->i_fd, 0, SEEK_SET );
1298
1299             /* clear the picture */
1300             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1301             p_sys->b_need_update = false;
1302         }
1303
1304         msleep( INTF_IDLE_SLEEP );
1305     }
1306
1307     End( p_intf );
1308     vlc_restorecancel( canc );
1309 }
1310
1311 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1312                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1313 {
1314     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1315     intf_sys_t *p_sys = p_intf->p_sys;
1316     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1317
1318     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1319     {
1320         p_sys->b_need_update = true;
1321     }
1322     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1323     {
1324         int i;
1325         /* Clear the entire render list */
1326         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1327         {
1328             RenderClear( p_intf, &p_sys->render[i] );
1329         }
1330         p_sys->b_clear = true;
1331     }
1332     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1333     {
1334         int i;
1335         /* Are we already busy with on slot ? */
1336         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1337         {
1338             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1339             {
1340                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1341                 break;
1342             }
1343         }
1344     }
1345     else
1346     {
1347         int i;
1348         /* Are we already busy with on slot ? */
1349         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1350         {
1351             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1352                 break;
1353         }
1354         /* No, then find first FREE slot */
1355         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1356         {
1357             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1358             {
1359                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1360                     break;
1361             }
1362             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1363             {
1364                 msg_Warn( p_this, "render space depleated" );
1365                 return VLC_SUCCESS;
1366             }
1367         }
1368         /* Found a free slot */
1369         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1370         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1371         {
1372             free( p_sys->render[i].psz_string );
1373             p_sys->render[i].psz_string = strdup( newval.psz_string );
1374             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1375         }
1376         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1377         {
1378             free( p_sys->render[i].psz_string );
1379             p_sys->render[i].psz_string = strdup( newval.psz_string );
1380             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1381         }
1382         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1383         {
1384             p_sys->render[i].b_absolute = false;
1385             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1386                                     newval.i_int : p_sys->i_width;
1387         }
1388         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1389         {
1390             p_sys->render[i].b_absolute = false;
1391             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1392                                     newval.i_int : p_sys->i_height;
1393         }
1394         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1395         {
1396             p_sys->render[i].b_absolute = true;
1397             p_sys->render[i].i_pos = newval.i_int;
1398         }
1399         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1400         {
1401             p_sys->render[i].text_style.i_font_size = newval.i_int;
1402         }
1403         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1404         {
1405             p_sys->render[i].text_style.i_font_color = newval.i_int;
1406         }
1407         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1408         {
1409             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1410         }
1411         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1412         {
1413             p_sys->render[i].i_alpha = newval.i_int;
1414         }
1415     }
1416     return VLC_SUCCESS;
1417 }