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