]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
Merge branch 1.0-bugfix
[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*   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     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         p_sys->render[i].p_text_style = text_style_New();
398
399     p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
400     p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
401     p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
402
403     var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
404     var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
405     var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
406
407     /* Check if picture position was overridden */
408     p_sys->b_absolute = true;
409     if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
410     {
411         p_sys->b_absolute = false;
412         p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
413                         p_sys->i_y : p_sys->i_height;
414         p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
415                         p_sys->i_x : p_sys->i_width;
416     }
417
418     p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
419     p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
420     p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
421     p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
422
423     /* Initialize framebuffer */
424     if( OpenDisplay( p_intf ) )
425     {
426         Destroy( VLC_OBJECT(p_intf) );
427         return VLC_EGENERIC;
428     }
429
430     Init( p_intf );
431
432 #if defined(FBOSD_BLENDING)
433     /* Load the blending module */
434     if( OpenBlending( p_intf ) )
435     {
436         msg_Err( p_intf, "Unable to load image blending module" );
437         Destroy( VLC_OBJECT(p_intf) );
438         return VLC_EGENERIC;
439     }
440 #endif
441
442     /* Load text renderer module */
443     if( OpenTextRenderer( p_intf ) )
444     {
445         msg_Err( p_intf, "Unable to load text rendering module" );
446         Destroy( VLC_OBJECT(p_intf) );
447         return VLC_EGENERIC;
448     }
449
450     p_sys->b_render = true;
451     p_sys->b_need_update = true;
452
453     return VLC_SUCCESS;
454 }
455
456 /*****************************************************************************
457  * Destroy: destroy FB interface thread output method
458  *****************************************************************************
459  * Terminate an output method created by Create
460  *****************************************************************************/
461 static void Destroy( vlc_object_t *p_this )
462 {
463     intf_thread_t *p_intf = (intf_thread_t *)p_this;
464     intf_sys_t *p_sys = p_intf->p_sys;
465     int i;
466
467
468     p_sys->b_need_update = false;
469     p_sys->b_render = false;
470     p_sys->b_clear = false;
471
472     var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
473     var_Destroy( p_intf, "fbosd-alpha" );
474
475     var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
476     var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
477     var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
478     var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
479     var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
480     var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
481     var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
482     var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
483     var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
484     var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
485     var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
486
487     var_Destroy( p_intf, "fbosd-x" );
488     var_Destroy( p_intf, "fbosd-y" );
489     var_Destroy( p_intf, "fbosd-position" );
490     var_Destroy( p_intf, "fbosd-image" );
491     var_Destroy( p_intf, "fbosd-text" );
492     var_Destroy( p_intf, "fbosd-font-size" );
493     var_Destroy( p_intf, "fbosd-font-color" );
494     var_Destroy( p_intf, "fbosd-font-opacity" );
495     var_Destroy( p_intf, "fbosd-clear" );
496     var_Destroy( p_intf, "fbosd-render" );
497     var_Destroy( p_intf, "fbosd-display" );
498
499     var_Destroy( p_intf, "fbosd-aspect-ratio" );
500
501     CloseDisplay( p_intf );
502
503     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
504     {
505         free( p_sys->render[i].psz_string );
506         p_sys->render[i].i_state = FBOSD_STATE_FREE;
507         text_style_Delete( p_sys->render[i].p_text_style );
508     }
509
510 #if defined(FBOSD_BLENDING)
511     if( p_sys->p_blend ) CloseBlending( p_intf );
512 #endif
513     if( p_sys->p_text )  CloseTextRenderer( p_intf );
514
515     if( p_sys->p_image )
516         image_HandlerDelete( p_sys->p_image );
517     if( p_sys->p_overlay )
518         picture_Release( p_sys->p_overlay );
519
520     text_style_Delete( p_sys->p_style );
521     free( p_sys );
522 }
523
524 #if defined(FBOSD_BLENDING)
525 static int OpenBlending( intf_thread_t *p_intf )
526 {
527     if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
528
529     p_intf->p_sys->p_blend =
530             vlc_object_create( p_intf, sizeof(filter_t) );
531     vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
532     p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
533         p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
534     p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
535             p_intf->p_sys->fmt_out.i_aspect;
536     p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
537             p_intf->p_sys->fmt_out.i_chroma;
538     if( config_GetInt( p_intf, "freetype-yuvp" ) )
539         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
540                 VLC_CODEC_YUVP;
541     else
542         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
543                 VLC_CODEC_YUVA;
544
545     p_intf->p_sys->p_blend->p_module =
546         module_need( p_intf->p_sys->p_blend, "video blending", NULL, false );
547
548     if( !p_intf->p_sys->p_blend->p_module )
549         return VLC_EGENERIC;
550
551     return VLC_SUCCESS;
552 }
553
554 static void CloseBlending( intf_thread_t *p_intf )
555 {
556     if( p_intf->p_sys->p_blend )
557     {
558         if( p_intf->p_sys->p_blend->p_module )
559             module_unneed( p_intf->p_sys->p_blend,
560                            p_intf->p_sys->p_blend->p_module );
561
562         vlc_object_detach( p_intf->p_sys->p_blend );
563         vlc_object_release( p_intf->p_sys->p_blend );
564     }
565 }
566 #endif
567
568 static int OpenTextRenderer( intf_thread_t *p_intf )
569 {
570     char *psz_modulename = NULL;
571
572     if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
573
574     p_intf->p_sys->p_text =
575             vlc_object_create( p_intf, sizeof(filter_t) );
576     vlc_object_attach( p_intf->p_sys->p_text, p_intf );
577
578     p_intf->p_sys->p_text->fmt_out.video.i_width =
579         p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
580         p_intf->p_sys->i_width;
581     p_intf->p_sys->p_text->fmt_out.video.i_height =
582         p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
583         p_intf->p_sys->i_height;
584
585     psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
586     if( psz_modulename && *psz_modulename )
587     {
588         p_intf->p_sys->p_text->p_module =
589             module_need( p_intf->p_sys->p_text, "text renderer",
590                             psz_modulename, true );
591     }
592     if( !p_intf->p_sys->p_text->p_module )
593     {
594         p_intf->p_sys->p_text->p_module =
595             module_need( p_intf->p_sys->p_text, "text renderer", NULL, false );
596     }
597     free( psz_modulename );
598
599     if( !p_intf->p_sys->p_text->p_module )
600         return VLC_EGENERIC;
601
602     return VLC_SUCCESS;
603 }
604
605 static void CloseTextRenderer( intf_thread_t *p_intf )
606 {
607     if( p_intf->p_sys->p_text )
608     {
609         if( p_intf->p_sys->p_text->p_module )
610             module_unneed( p_intf->p_sys->p_text,
611                            p_intf->p_sys->p_text->p_module );
612
613         vlc_object_detach( p_intf->p_sys->p_text );
614         vlc_object_release( p_intf->p_sys->p_text );
615     }
616 }
617
618 /*****************************************************************************
619  * AllocatePicture:
620  * allocate a picture buffer for use with the overlay fb.
621  *****************************************************************************/
622 static picture_t *AllocatePicture( video_format_t *p_fmt )
623 {
624     picture_t *p_picture = picture_New( p_fmt->i_chroma,
625                                         p_fmt->i_width, p_fmt->i_height,
626                                         p_fmt->i_aspect );
627     if( !p_picture )
628         return NULL;
629
630     if( !p_fmt->p_palette &&
631         ( p_fmt->i_chroma == VLC_CODEC_YUVP ) )
632     {
633         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
634         if( !p_fmt->p_palette )
635         {
636             picture_Release( p_picture );
637             return NULL;
638         }
639     }
640     else
641     {
642         p_fmt->p_palette = NULL;
643     }
644
645     return p_picture;
646 }
647
648 /*****************************************************************************
649  * DeAllocatePicture:
650  * Deallocate a picture buffer and free all associated memory.
651  *****************************************************************************/
652 static void DeAllocatePicture( picture_t *p_pic, video_format_t *p_fmt )
653 {
654     if( p_fmt )
655     {
656         free( p_fmt->p_palette );
657         p_fmt->p_palette = NULL;
658     }
659
660     if( p_pic )
661         picture_Release( p_pic );
662 }
663
664 /*****************************************************************************
665  * SetOverlayTransparency: Set the transparency for this overlay fb,
666  * - true is make transparent
667  * - false is make non tranparent
668  *****************************************************************************/
669 static void SetOverlayTransparency( intf_thread_t *p_intf,
670                                     bool b_transparent )
671 {
672     intf_sys_t *p_sys = p_intf->p_sys;
673     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
674                         * p_sys->i_bytes_per_pixel;
675     size_t i_page_size = (p_sys->i_page_size > i_size) ?
676                             i_size : p_sys->i_page_size;
677
678     if( p_sys->p_overlay )
679     {
680         msg_Dbg( p_intf, "Make overlay %s",
681                  b_transparent ? "transparent" : "opaque" );
682         if( b_transparent )
683             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
684         else
685             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
686     }
687 }
688
689 #if defined(FBOSD_BLENDING)
690 /*****************************************************************************
691  * BlendPicture: Blend two pictures together..
692  *****************************************************************************/
693 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
694                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
695                          picture_t *p_pic_dst )
696 {
697     intf_sys_t *p_sys = p_intf->p_sys;
698     if( p_sys->p_blend && p_sys->p_blend->p_module )
699     {
700         int i_x_offset = p_sys->i_x;
701         int i_y_offset = p_sys->i_y;
702
703         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
704
705         /* Update the output picture size */
706         p_sys->p_blend->fmt_out.video.i_width =
707             p_sys->p_blend->fmt_out.video.i_visible_width =
708                 p_fmt_dst->i_width;
709         p_sys->p_blend->fmt_out.video.i_height =
710             p_sys->p_blend->fmt_out.video.i_visible_height =
711                 p_fmt_dst->i_height;
712
713         i_x_offset = __MAX( i_x_offset, 0 );
714         i_y_offset = __MAX( i_y_offset, 0 );
715
716         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
717             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
718             p_sys->i_alpha );
719
720         return VLC_SUCCESS;
721     }
722     return VLC_EGENERIC;
723 }
724
725 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
726 {
727     uint8_t *p_begin = NULL, *p_end = NULL;
728     uint8_t i_skip = 0;
729
730     if( *p_pic && ((*p_pic)->i_planes != 1) )
731     {
732         msg_Err( p_intf,
733                  "cannot invert alpha channel too many planes %d (only 1 supported)",
734                  (*p_pic)->i_planes );
735         return VLC_EGENERIC;
736     }
737
738     switch( fmt.i_chroma )
739     {
740         case VLC_CODEC_RGB24:
741             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
742             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
743                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
744             i_skip = 3;
745             break;
746         case VLC_CODEC_RGB32:
747             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
748             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
749                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
750             i_skip = 4;
751             break;
752         default:
753             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
754                     (char *)&fmt.i_chroma );
755             return VLC_EGENERIC;
756     }
757
758     for( ; p_begin < p_end; p_begin += i_skip )
759     {
760         uint8_t i_opacity = 0;
761
762         if( *p_begin != 0xFF )
763             i_opacity = 255 - *p_begin;
764         *p_begin = i_opacity;
765     }
766     /* end of kludge */
767     return VLC_SUCCESS;
768 }
769 #endif
770
771 /*****************************************************************************
772  * RenderPicture: Render the picture into the p_dest buffer.
773  * We don't take transparent pixels into account, so we don't have to blend
774  * the two images together.
775  *****************************************************************************/
776 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
777                           picture_t *p_src, picture_t *p_dest )
778 {
779     int i;
780     VLC_UNUSED( p_intf );
781
782     if( !p_dest && !p_src ) return VLC_EGENERIC;
783
784     for( i = 0; i < p_src->i_planes ; i++ )
785     {
786         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
787         {
788             /* There are margins, but with the same width : perfect ! */
789             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
790                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
791         }
792         else
793         {
794             /* We need to proceed line by line */
795             uint8_t *p_in  = p_src->p[i].p_pixels;
796             uint8_t *p_out = p_dest->p[i].p_pixels;
797
798             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
799             int i_x_clip, i_y_clip;
800
801             /* Check boundaries, clip the image if necessary */
802             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
803             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
804
805             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
806             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
807 #if defined(FBOSD_DEBUG)
808             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
809                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
810                      i_x_offset, i_y_offset, i_x, i_x_clip );
811 #endif
812             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
813                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
814             {
815                 int i_line;
816
817                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
818                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
819                 {
820                     vlc_memcpy( p_out + i_x, p_in,
821                                 p_src->p[i].i_visible_pitch - i_x_clip );
822                     p_in += p_src->p[i].i_pitch;
823                     p_out += p_dest->p[i].i_pitch;
824                 }
825             }
826         }
827     }
828     return VLC_SUCCESS;
829 }
830
831 /*****************************************************************************
832  * RenderText - Render text to the desired picture format
833  *****************************************************************************/
834 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
835                               text_style_t *p_style, video_format_t *p_fmt )
836 {
837     intf_sys_t *p_sys = p_intf->p_sys;
838     subpicture_region_t *p_region;
839     picture_t *p_dest = NULL;
840
841     if( !psz_string ) return p_dest;
842
843     if( p_sys->p_text && p_sys->p_text->p_module )
844     {
845         video_format_t fmt;
846
847         memset( &fmt, 0, sizeof(fmt) );
848         fmt.i_chroma = VLC_CODEC_TEXT;
849         fmt.i_aspect = 0;
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_aspect = ( p_sys->i_width
986                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
987     }
988     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
989
990     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
991
992     /* Allocate overlay buffer */
993     p_sys->p_overlay = AllocatePicture( &p_sys->fmt_out );
994     if( !p_sys->p_overlay ) return VLC_EGENERIC;
995
996     SetOverlayTransparency( p_intf, true );
997
998     /* We know the chroma, allocate a buffer which will be used
999      * to write to the overlay framebuffer */
1000     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1001     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1002     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1003
1004     if( p_sys->var_info.xres_virtual )
1005     {
1006         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1007                              * p_sys->i_bytes_per_pixel;
1008     }
1009     else
1010     {
1011         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1012                              * p_sys->i_bytes_per_pixel;
1013     }
1014
1015     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1016                                  * p_sys->i_bytes_per_pixel;
1017
1018     p_sys->p_overlay->i_planes = 1;
1019
1020     return VLC_SUCCESS;
1021 }
1022
1023 /*****************************************************************************
1024  * End: terminate framebuffer interface
1025  *****************************************************************************/
1026 static void End( intf_thread_t *p_intf )
1027 {
1028     intf_sys_t *p_sys = p_intf->p_sys;
1029
1030     /* CleanUp */
1031     SetOverlayTransparency( p_intf, false );
1032     if( p_sys->p_overlay )
1033     {
1034         int ret;
1035         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1036                      p_sys->i_page_size );
1037         if( ret < 0 )
1038             msg_Err( p_intf, "unable to clear overlay" );
1039     }
1040
1041     DeAllocatePicture( p_intf->p_sys->p_overlay,
1042                        &p_intf->p_sys->fmt_out );
1043     p_intf->p_sys->p_overlay = NULL;
1044 }
1045
1046 /*****************************************************************************
1047  * OpenDisplay: initialize framebuffer
1048  *****************************************************************************/
1049 static int OpenDisplay( intf_thread_t *p_intf )
1050 {
1051     intf_sys_t *p_sys = p_intf->p_sys;
1052     char *psz_device;                             /* framebuffer device path */
1053     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1054
1055     /* Open framebuffer device */
1056     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1057     {
1058         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1059         return VLC_EGENERIC;
1060     }
1061
1062     p_sys->i_fd = open( psz_device, O_RDWR );
1063     if( p_sys->i_fd == -1 )
1064     {
1065         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1066         free( psz_device );
1067         return VLC_EGENERIC;
1068     }
1069     free( psz_device );
1070
1071     /* Get framebuffer device information */
1072     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1073     {
1074         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1075         close( p_sys->i_fd );
1076         return VLC_EGENERIC;
1077     }
1078
1079     /* Get some info on the framebuffer itself */
1080     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1081     {
1082         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1083         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1084     }
1085
1086     /* FIXME: if the image is full-size, it gets cropped on the left
1087      * because of the xres / xres_virtual slight difference */
1088     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1089              p_sys->var_info.xres, p_sys->var_info.yres,
1090              p_sys->var_info.xres_virtual,
1091              p_sys->var_info.yres_virtual );
1092
1093     p_sys->fmt_out.i_width = p_sys->i_width;
1094     p_sys->fmt_out.i_height = p_sys->i_height;
1095
1096     p_sys->p_palette = NULL;
1097     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1098
1099     switch( p_sys->var_info.bits_per_pixel )
1100     {
1101     case 8:
1102         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1103         if( !p_sys->p_palette )
1104         {
1105             close( p_sys->i_fd );
1106             return VLC_ENOMEM;
1107         }
1108         p_sys->fb_cmap.start = 0;
1109         p_sys->fb_cmap.len = 256;
1110         p_sys->fb_cmap.red = p_sys->p_palette;
1111         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1112         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1113         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1114
1115         /* Save the colormap */
1116         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1117
1118         p_sys->i_bytes_per_pixel = 1;
1119         break;
1120
1121     case 15:
1122     case 16:
1123         p_sys->i_bytes_per_pixel = 2;
1124         break;
1125
1126     case 24:
1127         p_sys->i_bytes_per_pixel = 3;
1128         break;
1129
1130     case 32:
1131         p_sys->i_bytes_per_pixel = 4;
1132         break;
1133
1134     default:
1135         msg_Err( p_intf, "screen depth %d is not supported",
1136                          p_sys->var_info.bits_per_pixel );
1137
1138         close( p_sys->i_fd );
1139         return VLC_EGENERIC;
1140     }
1141
1142     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1143                          * p_sys->i_bytes_per_pixel;
1144
1145     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1146              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1147              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1148     return VLC_SUCCESS;
1149 }
1150
1151 /*****************************************************************************
1152  * CloseDisplay: terminate FB interface thread
1153  *****************************************************************************/
1154 static void CloseDisplay( intf_thread_t *p_intf )
1155 {
1156     intf_sys_t *p_sys = p_intf->p_sys;
1157
1158     /* Restore palette */
1159     if( p_sys->var_info.bits_per_pixel == 8 )
1160     {
1161         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1162         free( p_sys->p_palette );
1163         p_sys->p_palette = NULL;
1164     }
1165
1166     /* Close fb */
1167     close( p_sys->i_fd );
1168 }
1169
1170 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1171 {
1172     intf_sys_t *p_sys = p_intf->p_sys;
1173
1174     if( render->i_state != FBOSD_STATE_RENDER ) return;
1175     if( !render->psz_string ) return;
1176
1177     if( render->i_type == FBOSD_RENDER_IMAGE )
1178     {
1179         picture_t *p_pic;
1180         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1181         if( p_pic )
1182         {
1183             RenderPicture( p_intf, render->i_x, render->i_y,
1184                            p_pic, p_sys->p_overlay );
1185             picture_Release( p_pic );
1186         }
1187     }
1188     else if( render->i_type == FBOSD_RENDER_TEXT )
1189     {
1190         picture_t *p_text;
1191 #if defined(FBOSD_BLENDING)
1192         video_format_t fmt_in;
1193         memset( &fmt_in, 0, sizeof(video_format_t) );
1194         p_text = RenderText( p_intf, render->psz_string, render->p_text_style,
1195                              &fmt_in );
1196         if( p_text )
1197         {
1198             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1199                           p_text, p_sys->p_overlay );
1200             msg_Dbg( p_intf, "releasing picture" );
1201             DeAllocatePicture( p_text, &fmt_in );
1202         }
1203 #else
1204         p_text = RenderText( p_intf, render->psz_string, render->p_text_style,
1205                              &p_sys->fmt_out );
1206         if( p_text )
1207         {
1208             RenderPicture( p_intf, render->i_x, render->i_y,
1209                            p_text, p_sys->p_overlay );
1210             picture_Release( p_text );
1211         }
1212 #endif
1213     }
1214 }
1215
1216 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1217 {
1218     intf_sys_t *p_sys = p_intf->p_sys;
1219
1220     text_style_Delete( render->p_text_style );
1221     render->p_text_style = text_style_New();
1222     free( render->psz_string );
1223     render->psz_string = NULL;
1224
1225     render->i_x = p_sys->i_x;
1226     render->i_y = p_sys->i_y;
1227     render->i_pos = p_sys->i_pos;
1228     render->i_alpha = p_sys->i_alpha;
1229     render->b_absolute = p_sys->b_absolute;
1230     render->i_state = FBOSD_STATE_FREE;
1231 }
1232
1233 static bool isRendererReady( intf_thread_t *p_intf )
1234 {
1235     intf_sys_t *p_sys = p_intf->p_sys;
1236     int i;
1237
1238     /* Check if there are more items to render */
1239     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1240     {
1241         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1242             return false;
1243     }
1244     return true;
1245 }
1246
1247 /*****************************************************************************
1248  * Run: thread
1249  *****************************************************************************
1250  * This part of the interface is in a separate thread so that we can call
1251  * exec() from within it without annoying the rest of the program.
1252  *****************************************************************************/
1253 static void Run( intf_thread_t *p_intf )
1254 {
1255     intf_sys_t *p_sys = p_intf->p_sys;
1256     int canc = vlc_savecancel();
1257
1258     while( vlc_object_alive( p_intf ) )
1259     {
1260         int i;
1261
1262         /* Is there somthing to render? */
1263         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1264         {
1265             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1266             {
1267                 Render( p_intf, &p_sys->render[i] );
1268                 RenderClear( p_intf, &p_sys->render[i] );
1269             }
1270         }
1271
1272         if( p_sys->b_clear )
1273         {
1274             SetOverlayTransparency( p_intf, true );
1275
1276             var_SetString( p_intf, "fbosd-image", "" );
1277             var_SetString( p_intf, "fbosd-text", "" );
1278
1279             p_sys->b_clear = false;
1280             p_sys->b_need_update = true;
1281         }
1282
1283         if( p_sys->b_need_update && p_sys->p_overlay &&
1284             isRendererReady( p_intf ) )
1285         {
1286             int ret;
1287 #if defined(FBOSD_BLENDING)
1288             /* Reverse alpha channel to work around FPGA bug */
1289             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1290 #endif
1291             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1292                          p_sys->i_page_size );
1293             if( ret < 0 )
1294                 msg_Err( p_intf, "unable to write to overlay" );
1295             lseek( p_sys->i_fd, 0, SEEK_SET );
1296
1297             /* clear the picture */
1298             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1299             p_sys->b_need_update = false;
1300         }
1301
1302         msleep( INTF_IDLE_SLEEP );
1303     }
1304
1305     End( p_intf );
1306     vlc_restorecancel( canc );
1307 }
1308
1309 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1310                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1311 {
1312     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1313     intf_sys_t *p_sys = p_intf->p_sys;
1314     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1315
1316     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1317     {
1318         p_sys->b_need_update = true;
1319     }
1320     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1321     {
1322         int i;
1323         /* Clear the entire render list */
1324         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1325         {
1326             RenderClear( p_intf, &p_sys->render[i] );
1327         }
1328         p_sys->b_clear = true;
1329     }
1330     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1331     {
1332         int i;
1333         /* Are we already busy with on slot ? */
1334         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1335         {
1336             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1337             {
1338                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1339                 break;
1340             }
1341         }
1342     }
1343     else
1344     {
1345         int i;
1346         /* Are we already busy with on slot ? */
1347         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1348         {
1349             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1350                 break;
1351         }
1352         /* No, then find first FREE slot */
1353         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1354         {
1355             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1356             {
1357                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1358                     break;
1359             }
1360             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1361             {
1362                 msg_Warn( p_this, "render space depleated" );
1363                 return VLC_SUCCESS;
1364             }
1365         }
1366         /* Found a free slot */
1367         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1368         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1369         {
1370             free( p_sys->render[i].psz_string );
1371             p_sys->render[i].psz_string = strdup( newval.psz_string );
1372             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1373         }
1374         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1375         {
1376             free( p_sys->render[i].psz_string );
1377             p_sys->render[i].psz_string = strdup( newval.psz_string );
1378             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1379         }
1380         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1381         {
1382             p_sys->render[i].b_absolute = false;
1383             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1384                                     newval.i_int : p_sys->i_width;
1385         }
1386         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1387         {
1388             p_sys->render[i].b_absolute = false;
1389             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1390                                     newval.i_int : p_sys->i_height;
1391         }
1392         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1393         {
1394             p_sys->render[i].b_absolute = true;
1395             p_sys->render[i].i_pos = newval.i_int;
1396         }
1397         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1398         {
1399             p_sys->render[i].p_text_style->i_font_size = newval.i_int;
1400         }
1401         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1402         {
1403             p_sys->render[i].p_text_style->i_font_color = newval.i_int;
1404         }
1405         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1406         {
1407             p_sys->render[i].p_text_style->i_font_alpha = 255 - newval.i_int;
1408         }
1409         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1410         {
1411             p_sys->render[i].i_alpha = newval.i_int;
1412         }
1413     }
1414     return VLC_SUCCESS;
1415 }