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