]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
c74a8fe4a98d60c1a0be4554c8efe0f65e762e19
[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 #include <vlc_charset.h>
35
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*   p_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     /* Use PAL by default */
334     p_sys->i_width  = p_sys->fmt_out.i_width  = 704;
335     p_sys->i_height = p_sys->fmt_out.i_height = 576;
336
337     p_sys->i_aspect = -1;
338     psz_aspect =
339             var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
340     if( psz_aspect )
341     {
342         char *psz_parser = strchr( psz_aspect, ':' );
343
344         if( psz_parser )
345         {
346             *psz_parser++ = '\0';
347             p_sys->i_aspect = ( atoi( psz_aspect )
348                               * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
349             p_sys->fmt_out.i_sar_num = p_sys->i_aspect    * p_sys->i_height;
350             p_sys->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR * p_sys->i_width;
351         }
352         msg_Dbg( p_intf, "using aspect ratio %d:%d",
353                   atoi( psz_aspect ), atoi( psz_parser ) );
354
355         free( psz_aspect );
356     }
357
358     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
359     var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
360     if( psz_tmp && *psz_tmp )
361     {
362         p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
363         p_sys->render[0].i_state = FBOSD_STATE_RENDER;
364         p_sys->render[0].psz_string = strdup( psz_tmp );
365     }
366     free( psz_tmp );
367
368     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
369     var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
370     if( psz_tmp && *psz_tmp )
371     {
372         p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
373         p_sys->render[1].i_state = FBOSD_STATE_RENDER;
374         p_sys->render[1].psz_string = strdup( psz_tmp );
375     }
376     free( psz_tmp );
377
378     p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
379     p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
380     p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );
381
382     var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
383     var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
384     var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
385
386     p_sys->p_style->i_font_size =
387             var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
388     p_sys->p_style->i_font_color =
389             var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
390     p_sys->p_style->i_font_alpha = 255 -
391             var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );
392
393     var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
394     var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
395     var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
396
397     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
398         p_sys->render[i].p_text_style = text_style_New();
399
400     p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
401     p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
402     p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
403
404     var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
405     var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
406     var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
407
408     /* Check if picture position was overridden */
409     p_sys->b_absolute = true;
410     if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
411     {
412         p_sys->b_absolute = false;
413         p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
414                         p_sys->i_y : p_sys->i_height;
415         p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
416                         p_sys->i_x : p_sys->i_width;
417     }
418
419     p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
420     p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
421     p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
422     p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
423
424     /* Initialize framebuffer */
425     if( OpenDisplay( p_intf ) )
426     {
427         Destroy( VLC_OBJECT(p_intf) );
428         return VLC_EGENERIC;
429     }
430
431     Init( p_intf );
432
433 #if defined(FBOSD_BLENDING)
434     /* Load the blending module */
435     if( OpenBlending( p_intf ) )
436     {
437         msg_Err( p_intf, "Unable to load image blending module" );
438         Destroy( VLC_OBJECT(p_intf) );
439         return VLC_EGENERIC;
440     }
441 #endif
442
443     /* Load text renderer module */
444     if( OpenTextRenderer( p_intf ) )
445     {
446         msg_Err( p_intf, "Unable to load text rendering module" );
447         Destroy( VLC_OBJECT(p_intf) );
448         return VLC_EGENERIC;
449     }
450
451     p_sys->b_render = true;
452     p_sys->b_need_update = true;
453
454     return VLC_SUCCESS;
455 }
456
457 /*****************************************************************************
458  * Destroy: destroy FB interface thread output method
459  *****************************************************************************
460  * Terminate an output method created by Create
461  *****************************************************************************/
462 static void Destroy( vlc_object_t *p_this )
463 {
464     intf_thread_t *p_intf = (intf_thread_t *)p_this;
465     intf_sys_t *p_sys = p_intf->p_sys;
466     int i;
467
468
469     p_sys->b_need_update = false;
470     p_sys->b_render = false;
471     p_sys->b_clear = false;
472
473     var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
474     var_Destroy( p_intf, "fbosd-alpha" );
475
476     var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
477     var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
478     var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
479     var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
480     var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
481     var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
482     var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
483     var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
484     var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
485     var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
486     var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
487
488     var_Destroy( p_intf, "fbosd-x" );
489     var_Destroy( p_intf, "fbosd-y" );
490     var_Destroy( p_intf, "fbosd-position" );
491     var_Destroy( p_intf, "fbosd-image" );
492     var_Destroy( p_intf, "fbosd-text" );
493     var_Destroy( p_intf, "fbosd-font-size" );
494     var_Destroy( p_intf, "fbosd-font-color" );
495     var_Destroy( p_intf, "fbosd-font-opacity" );
496     var_Destroy( p_intf, "fbosd-clear" );
497     var_Destroy( p_intf, "fbosd-render" );
498     var_Destroy( p_intf, "fbosd-display" );
499
500     var_Destroy( p_intf, "fbosd-aspect-ratio" );
501
502     CloseDisplay( p_intf );
503
504     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
505     {
506         free( p_sys->render[i].psz_string );
507         p_sys->render[i].i_state = FBOSD_STATE_FREE;
508         text_style_Delete( p_sys->render[i].p_text_style );
509     }
510
511 #if defined(FBOSD_BLENDING)
512     if( p_sys->p_blend ) CloseBlending( p_intf );
513 #endif
514     if( p_sys->p_text )  CloseTextRenderer( p_intf );
515
516     if( p_sys->p_image )
517         image_HandlerDelete( p_sys->p_image );
518     if( p_sys->p_overlay )
519         picture_Release( p_sys->p_overlay );
520
521     text_style_Delete( p_sys->p_style );
522     free( p_sys );
523 }
524
525 #if defined(FBOSD_BLENDING)
526 static int OpenBlending( intf_thread_t *p_intf )
527 {
528     if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
529
530     p_intf->p_sys->p_blend =
531             vlc_object_create( p_intf, sizeof(filter_t) );
532     vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
533     p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
534         p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
535     p_intf->p_sys->p_blend->fmt_out.video.i_sar_num =
536             p_intf->p_sys->fmt_out.i_sar_num;
537     p_intf->p_sys->p_blend->fmt_out.video.i_sar_den =
538             p_intf->p_sys->fmt_out.i_sar_den;
539     p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
540             p_intf->p_sys->fmt_out.i_chroma;
541     if( var_InheritInteger( p_intf, "freetype-yuvp" ) )
542         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
543                 VLC_CODEC_YUVP;
544     else
545         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
546                 VLC_CODEC_YUVA;
547
548     p_intf->p_sys->p_blend->p_module =
549         module_need( p_intf->p_sys->p_blend, "video blending", NULL, false );
550
551     if( !p_intf->p_sys->p_blend->p_module )
552         return VLC_EGENERIC;
553
554     return VLC_SUCCESS;
555 }
556
557 static void CloseBlending( intf_thread_t *p_intf )
558 {
559     if( p_intf->p_sys->p_blend )
560     {
561         if( p_intf->p_sys->p_blend->p_module )
562             module_unneed( p_intf->p_sys->p_blend,
563                            p_intf->p_sys->p_blend->p_module );
564
565         vlc_object_detach( p_intf->p_sys->p_blend );
566         vlc_object_release( p_intf->p_sys->p_blend );
567     }
568 }
569 #endif
570
571 static int OpenTextRenderer( intf_thread_t *p_intf )
572 {
573     char *psz_modulename = NULL;
574
575     if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
576
577     p_intf->p_sys->p_text =
578             vlc_object_create( p_intf, sizeof(filter_t) );
579     vlc_object_attach( p_intf->p_sys->p_text, p_intf );
580
581     p_intf->p_sys->p_text->fmt_out.video.i_width =
582         p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
583         p_intf->p_sys->i_width;
584     p_intf->p_sys->p_text->fmt_out.video.i_height =
585         p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
586         p_intf->p_sys->i_height;
587
588     psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
589     if( psz_modulename && *psz_modulename )
590     {
591         p_intf->p_sys->p_text->p_module =
592             module_need( p_intf->p_sys->p_text, "text renderer",
593                             psz_modulename, true );
594     }
595     if( !p_intf->p_sys->p_text->p_module )
596     {
597         p_intf->p_sys->p_text->p_module =
598             module_need( p_intf->p_sys->p_text, "text renderer", NULL, false );
599     }
600     free( psz_modulename );
601
602     if( !p_intf->p_sys->p_text->p_module )
603         return VLC_EGENERIC;
604
605     return VLC_SUCCESS;
606 }
607
608 static void CloseTextRenderer( intf_thread_t *p_intf )
609 {
610     if( p_intf->p_sys->p_text )
611     {
612         if( p_intf->p_sys->p_text->p_module )
613             module_unneed( p_intf->p_sys->p_text,
614                            p_intf->p_sys->p_text->p_module );
615
616         vlc_object_detach( p_intf->p_sys->p_text );
617         vlc_object_release( p_intf->p_sys->p_text );
618     }
619 }
620
621 /*****************************************************************************
622  * AllocatePicture:
623  * allocate a picture buffer for use with the overlay fb.
624  *****************************************************************************/
625 static picture_t *AllocatePicture( video_format_t *p_fmt )
626 {
627     picture_t *p_picture = picture_NewFromFormat( p_fmt );
628     if( !p_picture )
629         return NULL;
630
631     if( !p_fmt->p_palette &&
632         ( p_fmt->i_chroma == VLC_CODEC_YUVP ) )
633     {
634         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
635         if( !p_fmt->p_palette )
636         {
637             picture_Release( p_picture );
638             return NULL;
639         }
640     }
641     else
642     {
643         p_fmt->p_palette = NULL;
644     }
645
646     return p_picture;
647 }
648
649 /*****************************************************************************
650  * DeAllocatePicture:
651  * Deallocate a picture buffer and free all associated memory.
652  *****************************************************************************/
653 static void DeAllocatePicture( picture_t *p_pic, video_format_t *p_fmt )
654 {
655     if( p_fmt )
656     {
657         free( p_fmt->p_palette );
658         p_fmt->p_palette = NULL;
659     }
660
661     if( p_pic )
662         picture_Release( p_pic );
663 }
664
665 /*****************************************************************************
666  * SetOverlayTransparency: Set the transparency for this overlay fb,
667  * - true is make transparent
668  * - false is make non tranparent
669  *****************************************************************************/
670 static void SetOverlayTransparency( intf_thread_t *p_intf,
671                                     bool b_transparent )
672 {
673     intf_sys_t *p_sys = p_intf->p_sys;
674     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
675                         * p_sys->i_bytes_per_pixel;
676     size_t i_page_size = (p_sys->i_page_size > i_size) ?
677                             i_size : p_sys->i_page_size;
678
679     if( p_sys->p_overlay )
680     {
681         msg_Dbg( p_intf, "Make overlay %s",
682                  b_transparent ? "transparent" : "opaque" );
683         if( b_transparent )
684             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
685         else
686             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
687     }
688 }
689
690 #if defined(FBOSD_BLENDING)
691 /*****************************************************************************
692  * BlendPicture: Blend two pictures together..
693  *****************************************************************************/
694 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
695                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
696                          picture_t *p_pic_dst )
697 {
698     intf_sys_t *p_sys = p_intf->p_sys;
699     if( p_sys->p_blend && p_sys->p_blend->p_module )
700     {
701         int i_x_offset = p_sys->i_x;
702         int i_y_offset = p_sys->i_y;
703
704         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
705
706         /* Update the output picture size */
707         p_sys->p_blend->fmt_out.video.i_width =
708             p_sys->p_blend->fmt_out.video.i_visible_width =
709                 p_fmt_dst->i_width;
710         p_sys->p_blend->fmt_out.video.i_height =
711             p_sys->p_blend->fmt_out.video.i_visible_height =
712                 p_fmt_dst->i_height;
713
714         i_x_offset = __MAX( i_x_offset, 0 );
715         i_y_offset = __MAX( i_y_offset, 0 );
716
717         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
718             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
719             p_sys->i_alpha );
720
721         return VLC_SUCCESS;
722     }
723     return VLC_EGENERIC;
724 }
725
726 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
727 {
728     uint8_t *p_begin = NULL, *p_end = NULL;
729     uint8_t i_skip = 0;
730
731     if( *p_pic && ((*p_pic)->i_planes != 1) )
732     {
733         msg_Err( p_intf,
734                  "cannot invert alpha channel too many planes %d (only 1 supported)",
735                  (*p_pic)->i_planes );
736         return VLC_EGENERIC;
737     }
738
739     switch( fmt.i_chroma )
740     {
741         case VLC_CODEC_RGB24:
742             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
743             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
744                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
745             i_skip = 3;
746             break;
747         case VLC_CODEC_RGB32:
748             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
749             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
750                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
751             i_skip = 4;
752             break;
753         default:
754             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
755                     (char *)&fmt.i_chroma );
756             return VLC_EGENERIC;
757     }
758
759     for( ; p_begin < p_end; p_begin += i_skip )
760     {
761         uint8_t i_opacity = 0;
762
763         if( *p_begin != 0xFF )
764             i_opacity = 255 - *p_begin;
765         *p_begin = i_opacity;
766     }
767     /* end of kludge */
768     return VLC_SUCCESS;
769 }
770 #endif
771
772 /*****************************************************************************
773  * RenderPicture: Render the picture into the p_dest buffer.
774  * We don't take transparent pixels into account, so we don't have to blend
775  * the two images together.
776  *****************************************************************************/
777 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
778                           picture_t *p_src, picture_t *p_dest )
779 {
780     int i;
781     VLC_UNUSED( p_intf );
782
783     if( !p_dest && !p_src ) return VLC_EGENERIC;
784
785     for( i = 0; i < p_src->i_planes ; i++ )
786     {
787         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
788         {
789             /* There are margins, but with the same width : perfect ! */
790             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
791                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
792         }
793         else
794         {
795             /* We need to proceed line by line */
796             uint8_t *p_in  = p_src->p[i].p_pixels;
797             uint8_t *p_out = p_dest->p[i].p_pixels;
798
799             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
800             int i_x_clip, i_y_clip;
801
802             /* Check boundaries, clip the image if necessary */
803             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
804             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
805
806             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
807             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
808 #if defined(FBOSD_DEBUG)
809             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
810                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
811                      i_x_offset, i_y_offset, i_x, i_x_clip );
812 #endif
813             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
814                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
815             {
816                 int i_line;
817
818                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
819                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
820                 {
821                     vlc_memcpy( p_out + i_x, p_in,
822                                 p_src->p[i].i_visible_pitch - i_x_clip );
823                     p_in += p_src->p[i].i_pitch;
824                     p_out += p_dest->p[i].i_pitch;
825                 }
826             }
827         }
828     }
829     return VLC_SUCCESS;
830 }
831
832 /*****************************************************************************
833  * RenderText - Render text to the desired picture format
834  *****************************************************************************/
835 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
836                               text_style_t *p_style, video_format_t *p_fmt )
837 {
838     intf_sys_t *p_sys = p_intf->p_sys;
839     subpicture_region_t *p_region;
840     picture_t *p_dest = NULL;
841
842     if( !psz_string ) return p_dest;
843
844     if( p_sys->p_text && p_sys->p_text->p_module )
845     {
846         video_format_t fmt;
847
848         memset( &fmt, 0, sizeof(fmt) );
849         fmt.i_chroma = VLC_CODEC_TEXT;
850         fmt.i_width  = fmt.i_visible_width = 0;
851         fmt.i_height = fmt.i_visible_height = 0;
852         fmt.i_x_offset = 0;
853         fmt.i_y_offset = 0;
854
855         p_region = subpicture_region_New( &fmt );
856         if( !p_region )
857             return p_dest;
858
859         p_region->psz_text = strdup( psz_string );
860         if( !p_region->psz_text )
861         {
862             subpicture_region_Delete( p_region );
863             return NULL;
864         }
865         p_region->p_style = text_style_Duplicate( p_style );
866         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
867
868         if( p_sys->p_text->pf_render_text )
869         {
870             video_format_t fmt_out;
871
872             memset( &fmt_out, 0, sizeof(video_format_t) );
873
874             p_sys->p_text->pf_render_text( p_sys->p_text,
875                                            p_region, p_region );
876
877 #if defined(FBOSD_BLENDING)
878             fmt_out = p_region->fmt;
879             fmt_out.i_bits_per_pixel = 32;
880             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
881
882             /* FIXME not needed to copy the picture anymore no ? */
883             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
884             if( !p_dest )
885             {
886                 subpicture_region_Delete( p_region );
887                 return NULL;
888             }
889             picture_Copy( p_dest, p_region->p_picture );
890 #else
891             fmt_out.i_chroma = p_fmt->i_chroma;
892             p_dest = ConvertImage( p_intf, p_region->p_picture,
893                                    &p_region->fmt, &fmt_out );
894 #endif
895             subpicture_region_Delete( p_region );
896             return p_dest;
897         }
898         subpicture_region_Delete( p_region );
899     }
900     return p_dest;
901 }
902
903 /*****************************************************************************
904  * LoadImage: Load an image from file into a picture buffer.
905  *****************************************************************************/
906 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
907                              char *psz_file )
908 {
909     picture_t  *p_pic = NULL;
910
911     if( psz_file && p_intf->p_sys->p_image )
912     {
913         video_format_t fmt_in, fmt_out;
914
915         memset( &fmt_in, 0, sizeof(fmt_in) );
916         memset( &fmt_out, 0, sizeof(fmt_out) );
917
918         fmt_out.i_chroma = p_fmt->i_chroma;
919         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
920                                &fmt_in, &fmt_out );
921
922         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
923                  fmt_out.i_width, fmt_out.i_height,
924                  (char *)&p_fmt->i_chroma );
925     }
926     return p_pic;
927 }
928
929 #if ! defined(FBOSD_BLENDING)
930 /*****************************************************************************
931  * Convertmage: Convert image to another fourcc
932  *****************************************************************************/
933 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
934                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
935 {
936     intf_sys_t *p_sys = p_intf->p_sys;
937     picture_t  *p_old = NULL;
938
939     if( p_sys->p_image )
940     {
941         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
942
943         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
944                  p_fmt_out->i_width, p_fmt_out->i_height,
945                  (char *)&p_fmt_out->i_chroma );
946     }
947     return p_old;
948 }
949 #endif
950
951 /*****************************************************************************
952  * Init: initialize framebuffer video thread output method
953  *****************************************************************************/
954 static int Init( intf_thread_t *p_intf )
955 {
956     intf_sys_t *p_sys = p_intf->p_sys;
957
958     /* Initialize the output structure: RGB with square pixels, whatever
959      * the input format is, since it's the only format we know */
960     switch( p_sys->var_info.bits_per_pixel )
961     {
962     case 8: /* FIXME: set the palette */
963         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB8; break;
964     case 15:
965         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB15; break;
966     case 16:
967         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB16; break;
968     case 24:
969         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB24; break;
970     case 32:
971         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB32; break;
972     default:
973         msg_Err( p_intf, "unknown screen depth %i",
974                  p_sys->var_info.bits_per_pixel );
975         return VLC_EGENERIC;
976     }
977
978     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
979     p_sys->fmt_out.i_width  = p_sys->i_width;
980     p_sys->fmt_out.i_height = p_sys->i_height;
981
982     /* Assume we have square pixels */
983     if( p_sys->i_aspect < 0 )
984     {
985         p_sys->fmt_out.i_sar_num = 1;
986         p_sys->fmt_out.i_sar_den = 1;
987     }
988     else
989     {
990         p_sys->fmt_out.i_sar_num = p_sys->i_aspect    * p_sys->i_height;
991         p_sys->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR * p_sys->i_width;
992     }
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 = var_InheritString( 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 = utf8_open( psz_device, O_RDWR );
1065     if( p_sys->i_fd == -1 )
1066     {
1067         msg_Err( p_intf, "cannot open %s (%m)", psz_device );
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 (%m)" );
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->p_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->p_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     text_style_Delete( render->p_text_style );
1223     render->p_text_style = text_style_New();
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].p_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].p_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].p_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 }