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