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