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