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