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