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