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