]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
Don't leak picture_t
[vlc] / modules / gui / fbosd.c
1 /*****************************************************************************
2  * fbosd.c : framebuffer osd plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2007-2008, the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman
8  * Copied from modules/video_output/fb.c by Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc/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         memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
707         if( b_transparent )
708             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
709     }
710 }
711
712 #if defined(FBOSD_BLENDING)
713 /*****************************************************************************
714  * BlendPicture: Blend two pictures together..
715  *****************************************************************************/
716 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
717                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
718                          picture_t *p_pic_dst )
719 {
720     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
721     if( p_sys->p_blend && p_sys->p_blend->p_module )
722     {
723         int i_x_offset = p_sys->i_x;
724         int i_y_offset = p_sys->i_y;
725
726         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
727
728         /* Update the output picture size */
729         p_sys->p_blend->fmt_out.video.i_width =
730             p_sys->p_blend->fmt_out.video.i_visible_width =
731                 p_fmt_dst->i_width;
732         p_sys->p_blend->fmt_out.video.i_height =
733             p_sys->p_blend->fmt_out.video.i_visible_height =
734                 p_fmt_dst->i_height;
735
736         i_x_offset = __MAX( i_x_offset, 0 );
737         i_y_offset = __MAX( i_y_offset, 0 );
738
739         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
740             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
741             p_sys->i_alpha );
742
743         return VLC_SUCCESS;
744     }
745     return VLC_EGENERIC;
746 }
747
748 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
749 {
750     uint8_t *p_begin = NULL, *p_end = NULL;
751     uint8_t i_skip = 0;
752
753     if( *p_pic && ((*p_pic)->i_planes != 1) )
754     {
755         msg_Err( p_intf,
756                  "cannot invert alpha channel too many planes %d (only 1 supported)",
757                  (*p_pic)->i_planes );
758         return VLC_EGENERIC;
759     }
760
761     switch( fmt.i_chroma )
762     {
763         case VLC_FOURCC('R','V','2','4'):
764             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
765             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
766                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
767             i_skip = 3;
768             break;
769         case VLC_FOURCC('R','V','3','2'):
770             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
771             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
772                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
773             i_skip = 4;
774             break;
775         default:
776             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
777                     (char *)&fmt.i_chroma );
778             return VLC_EGENERIC;
779     }
780
781     for( ; p_begin < p_end; p_begin += i_skip )
782     {
783         uint8_t i_opacity;
784
785         if( i_opacity != 0xFF )
786             i_opacity = 255 - *p_begin;
787         *p_begin = i_opacity;
788     }
789     /* end of kludge */
790     return VLC_SUCCESS;
791 }
792 #endif
793
794 /*****************************************************************************
795  * RenderPicture: Render the picture into the p_dest buffer.
796  * We don't take transparent pixels into account, so we don't have to blend
797  * the two images together.
798  *****************************************************************************/
799 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
800                           picture_t *p_src, picture_t *p_dest )
801 {
802     int i;
803     VLC_UNUSED( p_intf );
804
805     if( !p_dest && !p_src ) return VLC_EGENERIC;
806
807     for( i = 0; i < p_src->i_planes ; i++ )
808     {
809         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
810         {
811             /* There are margins, but with the same width : perfect ! */
812             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
813                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
814         }
815         else
816         {
817             /* We need to proceed line by line */
818             uint8_t *p_in  = p_src->p[i].p_pixels;
819             uint8_t *p_out = p_dest->p[i].p_pixels;
820
821             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
822             int i_x_clip, i_y_clip;
823
824             /* Check boundaries, clip the image if necessary */
825             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
826             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
827
828             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
829             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
830 #if defined(FBOSD_DEBUG)
831             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
832                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
833                      i_x_offset, i_y_offset, i_x, i_x_clip );
834 #endif
835             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
836                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
837             {
838                 int i_line;
839
840                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
841                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
842                 {
843                     vlc_memcpy( p_out + i_x, p_in,
844                                 p_src->p[i].i_visible_pitch - i_x_clip );
845                     p_in += p_src->p[i].i_pitch;
846                     p_out += p_dest->p[i].i_pitch;
847                 }
848             }
849         }
850     }
851     return VLC_SUCCESS;
852 }
853
854 /*****************************************************************************
855  * RenderText - Render text to the desired picture format
856  *****************************************************************************/
857 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
858                               text_style_t *p_style, video_format_t *p_fmt )
859 {
860     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
861     subpicture_region_t *p_region;
862     picture_t *p_dest = NULL;
863
864     if( !psz_string ) return p_dest;
865
866     if( p_sys->p_text && p_sys->p_text->p_module )
867     {
868         p_region = (subpicture_region_t *) malloc( sizeof(subpicture_region_t) );
869         if( !p_region )
870             return p_dest;
871
872         memset( p_region, 0, sizeof(subpicture_region_t) );
873
874         p_region->psz_text = strdup( psz_string );
875         p_region->p_style = p_style;
876
877         p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
878         p_region->fmt.i_aspect = 0;
879         p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
880         p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
881         p_region->fmt.i_x_offset = 0;
882         p_region->fmt.i_y_offset = 0;
883
884         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
885
886         if( p_sys->p_text->pf_render_text )
887         {
888             video_format_t fmt_out;
889
890             memset( &fmt_out, 0, sizeof(video_format_t) );
891
892             p_sys->p_text->pf_render_text( p_sys->p_text,
893                                            p_region, p_region );
894
895 #if defined(FBOSD_BLENDING)
896             fmt_out = p_region->fmt;
897             fmt_out.i_bits_per_pixel = 32;
898             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
899
900             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
901             if( !p_dest )
902             {
903                 if( p_region->picture.pf_release )
904                     p_region->picture.pf_release( &p_region->picture );
905                 free( p_region->psz_text );
906                 free( p_region );
907                 return NULL;
908             }
909             vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
910 #else
911             fmt_out.i_chroma = p_fmt->i_chroma;
912             p_dest = ConvertImage( p_intf, &p_region->picture,
913                                    &p_region->fmt, &fmt_out );
914 #endif
915             if( p_region->picture.pf_release )
916                 p_region->picture.pf_release( &p_region->picture );
917             free( p_region->psz_text );
918             free( p_region );
919             return p_dest;
920         }
921         free( p_region->psz_text );
922         free( p_region );
923     }
924     return p_dest;
925 }
926
927 /*****************************************************************************
928  * LoadImage: Load an image from file into a picture buffer.
929  *****************************************************************************/
930 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
931                              char *psz_file )
932 {
933     picture_t  *p_pic = NULL;
934
935     if( psz_file && p_intf->p_sys->p_image )
936     {
937         video_format_t fmt_in, fmt_out;
938
939         memset( &fmt_in, 0, sizeof(fmt_in) );
940         memset( &fmt_out, 0, sizeof(fmt_out) );
941
942         fmt_out.i_chroma = p_fmt->i_chroma;
943         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
944                                &fmt_in, &fmt_out );
945
946         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
947                  fmt_out.i_width, fmt_out.i_height,
948                  (char *)&p_fmt->i_chroma );
949     }
950     return p_pic;
951 }
952
953 #if ! defined(FBOSD_BLENDING)
954 /*****************************************************************************
955  * Convertmage: Convert image to another fourcc
956  *****************************************************************************/
957 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
958                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
959 {
960     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
961     picture_t  *p_old = NULL;
962
963     if( p_sys->p_image )
964     {
965         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
966
967         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
968                  p_fmt_out->i_width, p_fmt_out->i_height,
969                  (char *)&p_fmt_out->i_chroma );
970     }
971     return p_old;
972 }
973 #endif
974
975 /*****************************************************************************
976  * Init: initialize framebuffer video thread output method
977  *****************************************************************************/
978 static int Init( intf_thread_t *p_intf )
979 {
980     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
981
982     /* Initialize the output structure: RGB with square pixels, whatever
983      * the input format is, since it's the only format we know */
984     switch( p_sys->var_info.bits_per_pixel )
985     {
986     case 8: /* FIXME: set the palette */
987         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
988     case 15:
989         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
990     case 16:
991         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
992     case 24:
993         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
994     case 32:
995         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
996     default:
997         msg_Err( p_intf, "unknown screen depth %i",
998                  p_sys->var_info.bits_per_pixel );
999         return VLC_EGENERIC;
1000     }
1001
1002     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
1003     p_sys->fmt_out.i_width  = p_sys->i_width;
1004     p_sys->fmt_out.i_height = p_sys->i_height;
1005
1006     /* Assume we have square pixels */
1007     if( p_sys->i_aspect < 0 )
1008     {
1009         p_sys->fmt_out.i_aspect = ( p_sys->i_width
1010                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
1011     }
1012     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
1013
1014     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1015
1016     /* Allocate overlay buffer */
1017     p_sys->p_overlay = AllocatePicture( VLC_OBJECT(p_intf),
1018                                         &p_sys->fmt_out );
1019     if( !p_sys->p_overlay ) return VLC_EGENERIC;
1020
1021     SetOverlayTransparency( p_intf, true );
1022
1023     /* We know the chroma, allocate a buffer which will be used
1024      * to write to the overlay framebuffer */
1025     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1026     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1027     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1028
1029     if( p_sys->var_info.xres_virtual )
1030     {
1031         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1032                              * p_sys->i_bytes_per_pixel;
1033     }
1034     else
1035     {
1036         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1037                              * p_sys->i_bytes_per_pixel;
1038     }
1039
1040     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1041                                  * p_sys->i_bytes_per_pixel;
1042
1043     p_sys->p_overlay->i_planes = 1;
1044
1045     return VLC_SUCCESS;
1046 }
1047
1048 /*****************************************************************************
1049  * End: terminate framebuffer interface
1050  *****************************************************************************/
1051 static void End( intf_thread_t *p_intf )
1052 {
1053     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1054
1055     /* CleanUp */
1056     SetOverlayTransparency( p_intf, false );
1057     if( p_sys->p_overlay )
1058     {
1059         int ret;
1060         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels, p_sys->i_page_size );
1061         if( ret < 0 )
1062             msg_Err( p_intf, "unable to clear overlay" );
1063     }
1064
1065     DeAllocatePicture( VLC_OBJECT(p_intf), p_intf->p_sys->p_overlay,
1066                        &p_intf->p_sys->fmt_out );
1067     p_intf->p_sys->p_overlay = NULL;
1068 }
1069
1070 /*****************************************************************************
1071  * OpenDisplay: initialize framebuffer
1072  *****************************************************************************/
1073 static int OpenDisplay( intf_thread_t *p_intf )
1074 {
1075     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1076     char *psz_device;                             /* framebuffer device path */
1077     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1078
1079     /* Open framebuffer device */
1080     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1081     {
1082         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1083         return VLC_EGENERIC;
1084     }
1085
1086     p_sys->i_fd = open( psz_device, O_RDWR );
1087     if( p_sys->i_fd == -1 )
1088     {
1089         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1090         free( psz_device );
1091         return VLC_EGENERIC;
1092     }
1093     free( psz_device );
1094
1095     /* Get framebuffer device information */
1096     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1097     {
1098         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1099         close( p_sys->i_fd );
1100         return VLC_EGENERIC;
1101     }
1102
1103     /* Get some info on the framebuffer itself */
1104     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1105     {
1106         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1107         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1108     }
1109
1110     /* FIXME: if the image is full-size, it gets cropped on the left
1111      * because of the xres / xres_virtual slight difference */
1112     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1113              p_sys->var_info.xres, p_sys->var_info.yres,
1114              p_sys->var_info.xres_virtual,
1115              p_sys->var_info.yres_virtual );
1116
1117     p_sys->fmt_out.i_width = p_sys->i_width;
1118     p_sys->fmt_out.i_height = p_sys->i_height;
1119
1120     p_sys->p_palette = NULL;
1121     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1122
1123     switch( p_sys->var_info.bits_per_pixel )
1124     {
1125     case 8:
1126         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1127         if( !p_sys->p_palette )
1128         {
1129             close( p_sys->i_fd );
1130             return VLC_ENOMEM;
1131         }
1132         p_sys->fb_cmap.start = 0;
1133         p_sys->fb_cmap.len = 256;
1134         p_sys->fb_cmap.red = p_sys->p_palette;
1135         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1136         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1137         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1138
1139         /* Save the colormap */
1140         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1141
1142         p_sys->i_bytes_per_pixel = 1;
1143         break;
1144
1145     case 15:
1146     case 16:
1147         p_sys->i_bytes_per_pixel = 2;
1148         break;
1149
1150     case 24:
1151         p_sys->i_bytes_per_pixel = 3;
1152         break;
1153
1154     case 32:
1155         p_sys->i_bytes_per_pixel = 4;
1156         break;
1157
1158     default:
1159         msg_Err( p_intf, "screen depth %d is not supported",
1160                          p_sys->var_info.bits_per_pixel );
1161
1162         close( p_sys->i_fd );
1163         return VLC_EGENERIC;
1164     }
1165
1166     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1167                          * p_sys->i_bytes_per_pixel;
1168
1169     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1170              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1171              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1172     return VLC_SUCCESS;
1173 }
1174
1175 /*****************************************************************************
1176  * CloseDisplay: terminate FB interface thread
1177  *****************************************************************************/
1178 static void CloseDisplay( intf_thread_t *p_intf )
1179 {
1180     intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1181
1182     /* Restore palette */
1183     if( p_sys->var_info.bits_per_pixel == 8 )
1184     {
1185         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1186         free( p_sys->p_palette );
1187         p_sys->p_palette = NULL;
1188     }
1189
1190     /* Close fb */
1191     close( p_sys->i_fd );
1192 }
1193
1194 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1195 {
1196     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1197
1198     if( render->i_state != FBOSD_STATE_RENDER ) return;
1199     if( !render->psz_string ) return;
1200
1201     if( render->i_type == FBOSD_RENDER_IMAGE )
1202     {
1203         picture_t *p_pic;
1204         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1205         if( p_pic )
1206         {
1207             RenderPicture( p_intf, render->i_x, render->i_y,
1208                            p_pic, p_sys->p_overlay );
1209             p_pic->pf_release( p_pic );
1210         }
1211     }
1212     else if( render->i_type == FBOSD_RENDER_TEXT )
1213     {
1214         picture_t *p_text;
1215 #if defined(FBOSD_BLENDING)
1216         video_format_t fmt_in;
1217         memset( &fmt_in, 0, sizeof(video_format_t) );
1218         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1219                              &fmt_in );
1220         if( p_text )
1221         {
1222             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1223                           p_text, p_sys->p_overlay );
1224             msg_Dbg( p_intf, "releasing picture" );
1225             DeAllocatePicture( VLC_OBJECT( p_intf ), p_text, &fmt_in );
1226         }
1227 #else
1228         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1229                              &p_sys->fmt_out );
1230         if( p_text )
1231         {
1232             RenderPicture( p_intf, render->i_x, render->i_y,
1233                            p_text, p_sys->p_overlay );
1234             p_text->pf_release( p_text );
1235         }
1236 #endif
1237     }
1238 }
1239
1240 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1241 {
1242     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1243
1244     vlc_memcpy( &render->text_style, &default_text_style,
1245                 sizeof( text_style_t ) );
1246     free( render->psz_string );
1247     render->psz_string = NULL;
1248
1249     render->i_x = p_sys->i_x;
1250     render->i_y = p_sys->i_y;
1251     render->i_pos = p_sys->i_pos;
1252     render->i_alpha = p_sys->i_alpha;
1253     render->b_absolute = p_sys->b_absolute;
1254     render->i_state = FBOSD_STATE_FREE;
1255 }
1256
1257 static bool isRendererReady( intf_thread_t *p_intf )
1258 {
1259     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1260     int i;
1261
1262     /* Check if there are more items to render */
1263     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1264     {
1265         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1266             return false;
1267     }
1268     return true;
1269 }
1270
1271 /*****************************************************************************
1272  * Run: rc thread
1273  *****************************************************************************
1274  * This part of the interface is in a separate thread so that we can call
1275  * exec() from within it without annoying the rest of the program.
1276  *****************************************************************************/
1277 static void Run( intf_thread_t *p_intf )
1278 {
1279     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1280
1281     while( !intf_ShouldDie( p_intf ) )
1282     {
1283         int i;
1284
1285         /* Is there somthing to render? */
1286         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1287         {
1288             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1289             {
1290                 Render( p_intf, &p_sys->render[i] );
1291                 RenderClear( p_intf, &p_sys->render[i] );
1292             }
1293         }
1294
1295         if( p_sys->b_clear )
1296         {
1297             SetOverlayTransparency( p_intf, true );
1298
1299             var_SetString( p_intf, "fbosd-image", "" );
1300             var_SetString( p_intf, "fbosd-text", "" );
1301
1302             p_sys->b_clear = false;
1303             p_sys->b_need_update = true;
1304         }
1305
1306         if( p_sys->b_need_update && p_sys->p_overlay &&
1307             isRendererReady( p_intf ) )
1308         {
1309             int ret;
1310 #if defined(FBOSD_BLENDING)
1311             /* Reverse alpha channel to work around FPGA bug */
1312             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1313 #endif
1314             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1315                          p_sys->i_page_size );
1316             if( ret < 0 )
1317                 msg_Err( p_intf, "unable to write to overlay" );
1318             lseek( p_sys->i_fd, 0, SEEK_SET );
1319
1320             /* clear the picture */
1321             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1322             p_sys->b_need_update = false;
1323         }
1324
1325         if( vlc_CPU() & CPU_CAPABILITY_FPU )
1326             msleep( INTF_IDLE_SLEEP );
1327         else
1328             msleep( 500 );
1329     }
1330
1331     End( p_intf );
1332 }
1333
1334 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1335                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1336 {
1337     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1338     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1339     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1340
1341     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1342     {
1343         p_sys->b_need_update = true;
1344     }
1345     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1346     {
1347         int i;
1348         /* Clear the entire render list */
1349         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1350         {
1351             RenderClear( p_intf, &p_sys->render[i] );
1352         }
1353         p_sys->b_clear = true;
1354     }
1355     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1356     {
1357         int i;
1358         /* Are we already busy with on slot ? */
1359         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1360         {
1361             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1362             {
1363                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1364                 break;
1365             }
1366         }
1367     }
1368     else
1369     {
1370         int i;
1371         /* Are we already busy with on slot ? */
1372         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1373         {
1374             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1375                 break;
1376         }
1377         /* No, then find first FREE slot */
1378         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1379         {
1380             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1381             {
1382                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1383                     break;
1384             }
1385             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1386             {
1387                 msg_Warn( p_this, "render space depleated" );
1388                 return VLC_SUCCESS;
1389             }
1390         }
1391         /* Found a free slot */
1392         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1393         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1394         {
1395             free( p_sys->render[i].psz_string );
1396             p_sys->render[i].psz_string = strdup( newval.psz_string );
1397             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1398         }
1399         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1400         {
1401             free( p_sys->render[i].psz_string );
1402             p_sys->render[i].psz_string = strdup( newval.psz_string );
1403             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1404         }
1405         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1406         {
1407             p_sys->render[i].b_absolute = false;
1408             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1409                                     newval.i_int : p_sys->i_width;
1410         }
1411         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1412         {
1413             p_sys->render[i].b_absolute = false;
1414             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1415                                     newval.i_int : p_sys->i_height;
1416         }
1417         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1418         {
1419             p_sys->render[i].b_absolute = true;
1420             p_sys->render[i].i_pos = newval.i_int;
1421         }
1422         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1423         {
1424             p_sys->render[i].text_style.i_font_size = newval.i_int;
1425         }
1426         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1427         {
1428             p_sys->render[i].text_style.i_font_color = newval.i_int;
1429         }
1430         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1431         {
1432             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1433         }
1434 #if defined(FBOSD_BLENDING)
1435         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1436         {
1437             p_sys->render[i].i_alpha = newval.i_int;
1438         }
1439 #endif
1440     }
1441     return VLC_SUCCESS;
1442 }