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