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