Catch error case.
[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         if( !p_region->psz_text )
877         {
878             free( p_region );
879             return NULL;
880         }
881         p_region->p_style = p_style;
882
883         p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
884         p_region->fmt.i_aspect = 0;
885         p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
886         p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
887         p_region->fmt.i_x_offset = 0;
888         p_region->fmt.i_y_offset = 0;
889
890         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
891
892         if( p_sys->p_text->pf_render_text )
893         {
894             video_format_t fmt_out;
895
896             memset( &fmt_out, 0, sizeof(video_format_t) );
897
898             p_sys->p_text->pf_render_text( p_sys->p_text,
899                                            p_region, p_region );
900
901 #if defined(FBOSD_BLENDING)
902             fmt_out = p_region->fmt;
903             fmt_out.i_bits_per_pixel = 32;
904             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
905
906             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
907             if( !p_dest )
908             {
909                 if( p_region->picture.pf_release )
910                     p_region->picture.pf_release( &p_region->picture );
911                 free( p_region->psz_text );
912                 free( p_region );
913                 return NULL;
914             }
915             vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
916 #else
917             fmt_out.i_chroma = p_fmt->i_chroma;
918             p_dest = ConvertImage( p_intf, &p_region->picture,
919                                    &p_region->fmt, &fmt_out );
920 #endif
921             if( p_region->picture.pf_release )
922                 p_region->picture.pf_release( &p_region->picture );
923             free( p_region->psz_text );
924             free( p_region );
925             return p_dest;
926         }
927         free( p_region->psz_text );
928         free( p_region );
929     }
930     return p_dest;
931 }
932
933 /*****************************************************************************
934  * LoadImage: Load an image from file into a picture buffer.
935  *****************************************************************************/
936 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
937                              char *psz_file )
938 {
939     picture_t  *p_pic = NULL;
940
941     if( psz_file && p_intf->p_sys->p_image )
942     {
943         video_format_t fmt_in, fmt_out;
944
945         memset( &fmt_in, 0, sizeof(fmt_in) );
946         memset( &fmt_out, 0, sizeof(fmt_out) );
947
948         fmt_out.i_chroma = p_fmt->i_chroma;
949         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
950                                &fmt_in, &fmt_out );
951
952         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
953                  fmt_out.i_width, fmt_out.i_height,
954                  (char *)&p_fmt->i_chroma );
955     }
956     return p_pic;
957 }
958
959 #if ! defined(FBOSD_BLENDING)
960 /*****************************************************************************
961  * Convertmage: Convert image to another fourcc
962  *****************************************************************************/
963 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
964                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
965 {
966     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
967     picture_t  *p_old = NULL;
968
969     if( p_sys->p_image )
970     {
971         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
972
973         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
974                  p_fmt_out->i_width, p_fmt_out->i_height,
975                  (char *)&p_fmt_out->i_chroma );
976     }
977     return p_old;
978 }
979 #endif
980
981 /*****************************************************************************
982  * Init: initialize framebuffer video thread output method
983  *****************************************************************************/
984 static int Init( intf_thread_t *p_intf )
985 {
986     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
987
988     /* Initialize the output structure: RGB with square pixels, whatever
989      * the input format is, since it's the only format we know */
990     switch( p_sys->var_info.bits_per_pixel )
991     {
992     case 8: /* FIXME: set the palette */
993         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
994     case 15:
995         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
996     case 16:
997         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
998     case 24:
999         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
1000     case 32:
1001         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
1002     default:
1003         msg_Err( p_intf, "unknown screen depth %i",
1004                  p_sys->var_info.bits_per_pixel );
1005         return VLC_EGENERIC;
1006     }
1007
1008     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
1009     p_sys->fmt_out.i_width  = p_sys->i_width;
1010     p_sys->fmt_out.i_height = p_sys->i_height;
1011
1012     /* Assume we have square pixels */
1013     if( p_sys->i_aspect < 0 )
1014     {
1015         p_sys->fmt_out.i_aspect = ( p_sys->i_width
1016                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
1017     }
1018     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
1019
1020     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1021
1022     /* Allocate overlay buffer */
1023     p_sys->p_overlay = AllocatePicture( VLC_OBJECT(p_intf),
1024                                         &p_sys->fmt_out );
1025     if( !p_sys->p_overlay ) return VLC_EGENERIC;
1026
1027     SetOverlayTransparency( p_intf, true );
1028
1029     /* We know the chroma, allocate a buffer which will be used
1030      * to write to the overlay framebuffer */
1031     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1032     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1033     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1034
1035     if( p_sys->var_info.xres_virtual )
1036     {
1037         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1038                              * p_sys->i_bytes_per_pixel;
1039     }
1040     else
1041     {
1042         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1043                              * p_sys->i_bytes_per_pixel;
1044     }
1045
1046     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1047                                  * p_sys->i_bytes_per_pixel;
1048
1049     p_sys->p_overlay->i_planes = 1;
1050
1051     return VLC_SUCCESS;
1052 }
1053
1054 /*****************************************************************************
1055  * End: terminate framebuffer interface
1056  *****************************************************************************/
1057 static void End( intf_thread_t *p_intf )
1058 {
1059     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1060
1061     /* CleanUp */
1062     SetOverlayTransparency( p_intf, false );
1063     if( p_sys->p_overlay )
1064     {
1065         int ret;
1066         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1067                      p_sys->i_page_size );
1068         if( ret < 0 )
1069             msg_Err( p_intf, "unable to clear overlay" );
1070     }
1071
1072     DeAllocatePicture( VLC_OBJECT(p_intf), p_intf->p_sys->p_overlay,
1073                        &p_intf->p_sys->fmt_out );
1074     p_intf->p_sys->p_overlay = NULL;
1075 }
1076
1077 /*****************************************************************************
1078  * OpenDisplay: initialize framebuffer
1079  *****************************************************************************/
1080 static int OpenDisplay( intf_thread_t *p_intf )
1081 {
1082     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1083     char *psz_device;                             /* framebuffer device path */
1084     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1085
1086     /* Open framebuffer device */
1087     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1088     {
1089         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1090         return VLC_EGENERIC;
1091     }
1092
1093     p_sys->i_fd = open( psz_device, O_RDWR );
1094     if( p_sys->i_fd == -1 )
1095     {
1096         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1097         free( psz_device );
1098         return VLC_EGENERIC;
1099     }
1100     free( psz_device );
1101
1102     /* Get framebuffer device information */
1103     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1104     {
1105         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1106         close( p_sys->i_fd );
1107         return VLC_EGENERIC;
1108     }
1109
1110     /* Get some info on the framebuffer itself */
1111     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1112     {
1113         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1114         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1115     }
1116
1117     /* FIXME: if the image is full-size, it gets cropped on the left
1118      * because of the xres / xres_virtual slight difference */
1119     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1120              p_sys->var_info.xres, p_sys->var_info.yres,
1121              p_sys->var_info.xres_virtual,
1122              p_sys->var_info.yres_virtual );
1123
1124     p_sys->fmt_out.i_width = p_sys->i_width;
1125     p_sys->fmt_out.i_height = p_sys->i_height;
1126
1127     p_sys->p_palette = NULL;
1128     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1129
1130     switch( p_sys->var_info.bits_per_pixel )
1131     {
1132     case 8:
1133         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1134         if( !p_sys->p_palette )
1135         {
1136             close( p_sys->i_fd );
1137             return VLC_ENOMEM;
1138         }
1139         p_sys->fb_cmap.start = 0;
1140         p_sys->fb_cmap.len = 256;
1141         p_sys->fb_cmap.red = p_sys->p_palette;
1142         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1143         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1144         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1145
1146         /* Save the colormap */
1147         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1148
1149         p_sys->i_bytes_per_pixel = 1;
1150         break;
1151
1152     case 15:
1153     case 16:
1154         p_sys->i_bytes_per_pixel = 2;
1155         break;
1156
1157     case 24:
1158         p_sys->i_bytes_per_pixel = 3;
1159         break;
1160
1161     case 32:
1162         p_sys->i_bytes_per_pixel = 4;
1163         break;
1164
1165     default:
1166         msg_Err( p_intf, "screen depth %d is not supported",
1167                          p_sys->var_info.bits_per_pixel );
1168
1169         close( p_sys->i_fd );
1170         return VLC_EGENERIC;
1171     }
1172
1173     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1174                          * p_sys->i_bytes_per_pixel;
1175
1176     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1177              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1178              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1179     return VLC_SUCCESS;
1180 }
1181
1182 /*****************************************************************************
1183  * CloseDisplay: terminate FB interface thread
1184  *****************************************************************************/
1185 static void CloseDisplay( intf_thread_t *p_intf )
1186 {
1187     intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1188
1189     /* Restore palette */
1190     if( p_sys->var_info.bits_per_pixel == 8 )
1191     {
1192         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1193         free( p_sys->p_palette );
1194         p_sys->p_palette = NULL;
1195     }
1196
1197     /* Close fb */
1198     close( p_sys->i_fd );
1199 }
1200
1201 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1202 {
1203     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1204
1205     if( render->i_state != FBOSD_STATE_RENDER ) return;
1206     if( !render->psz_string ) return;
1207
1208     if( render->i_type == FBOSD_RENDER_IMAGE )
1209     {
1210         picture_t *p_pic;
1211         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1212         if( p_pic )
1213         {
1214             RenderPicture( p_intf, render->i_x, render->i_y,
1215                            p_pic, p_sys->p_overlay );
1216             p_pic->pf_release( p_pic );
1217         }
1218     }
1219     else if( render->i_type == FBOSD_RENDER_TEXT )
1220     {
1221         picture_t *p_text;
1222 #if defined(FBOSD_BLENDING)
1223         video_format_t fmt_in;
1224         memset( &fmt_in, 0, sizeof(video_format_t) );
1225         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1226                              &fmt_in );
1227         if( p_text )
1228         {
1229             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1230                           p_text, p_sys->p_overlay );
1231             msg_Dbg( p_intf, "releasing picture" );
1232             DeAllocatePicture( VLC_OBJECT( p_intf ), p_text, &fmt_in );
1233         }
1234 #else
1235         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1236                              &p_sys->fmt_out );
1237         if( p_text )
1238         {
1239             RenderPicture( p_intf, render->i_x, render->i_y,
1240                            p_text, p_sys->p_overlay );
1241             p_text->pf_release( p_text );
1242         }
1243 #endif
1244     }
1245 }
1246
1247 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1248 {
1249     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1250
1251     vlc_memcpy( &render->text_style, &default_text_style,
1252                 sizeof( text_style_t ) );
1253     free( render->psz_string );
1254     render->psz_string = NULL;
1255
1256     render->i_x = p_sys->i_x;
1257     render->i_y = p_sys->i_y;
1258     render->i_pos = p_sys->i_pos;
1259     render->i_alpha = p_sys->i_alpha;
1260     render->b_absolute = p_sys->b_absolute;
1261     render->i_state = FBOSD_STATE_FREE;
1262 }
1263
1264 static bool isRendererReady( intf_thread_t *p_intf )
1265 {
1266     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1267     int i;
1268
1269     /* Check if there are more items to render */
1270     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1271     {
1272         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1273             return false;
1274     }
1275     return true;
1276 }
1277
1278 /*****************************************************************************
1279  * Run: thread
1280  *****************************************************************************
1281  * This part of the interface is in a separate thread so that we can call
1282  * exec() from within it without annoying the rest of the program.
1283  *****************************************************************************/
1284 static void Run( intf_thread_t *p_intf )
1285 {
1286     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1287
1288     while( !intf_ShouldDie( p_intf ) )
1289     {
1290         int i;
1291
1292         /* Is there somthing to render? */
1293         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1294         {
1295             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1296             {
1297                 Render( p_intf, &p_sys->render[i] );
1298                 RenderClear( p_intf, &p_sys->render[i] );
1299             }
1300         }
1301
1302         if( p_sys->b_clear )
1303         {
1304             SetOverlayTransparency( p_intf, true );
1305
1306             var_SetString( p_intf, "fbosd-image", "" );
1307             var_SetString( p_intf, "fbosd-text", "" );
1308
1309             p_sys->b_clear = false;
1310             p_sys->b_need_update = true;
1311         }
1312
1313         if( p_sys->b_need_update && p_sys->p_overlay &&
1314             isRendererReady( p_intf ) )
1315         {
1316             int ret;
1317 #if defined(FBOSD_BLENDING)
1318             /* Reverse alpha channel to work around FPGA bug */
1319             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1320 #endif
1321             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1322                          p_sys->i_page_size );
1323             if( ret < 0 )
1324                 msg_Err( p_intf, "unable to write to overlay" );
1325             lseek( p_sys->i_fd, 0, SEEK_SET );
1326
1327             /* clear the picture */
1328             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1329             p_sys->b_need_update = false;
1330         }
1331
1332         if( vlc_CPU() & CPU_CAPABILITY_FPU )
1333             msleep( INTF_IDLE_SLEEP );
1334         else
1335             msleep( 500 );
1336     }
1337
1338     End( p_intf );
1339 }
1340
1341 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1342                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1343 {
1344     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1345     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1346     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1347
1348     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1349     {
1350         p_sys->b_need_update = true;
1351     }
1352     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1353     {
1354         int i;
1355         /* Clear the entire render list */
1356         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1357         {
1358             RenderClear( p_intf, &p_sys->render[i] );
1359         }
1360         p_sys->b_clear = true;
1361     }
1362     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1363     {
1364         int i;
1365         /* Are we already busy with on slot ? */
1366         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1367         {
1368             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1369             {
1370                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1371                 break;
1372             }
1373         }
1374     }
1375     else
1376     {
1377         int i;
1378         /* Are we already busy with on slot ? */
1379         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1380         {
1381             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1382                 break;
1383         }
1384         /* No, then find first FREE slot */
1385         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1386         {
1387             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1388             {
1389                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1390                     break;
1391             }
1392             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1393             {
1394                 msg_Warn( p_this, "render space depleated" );
1395                 return VLC_SUCCESS;
1396             }
1397         }
1398         /* Found a free slot */
1399         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1400         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1401         {
1402             free( p_sys->render[i].psz_string );
1403             p_sys->render[i].psz_string = strdup( newval.psz_string );
1404             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1405         }
1406         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1407         {
1408             free( p_sys->render[i].psz_string );
1409             p_sys->render[i].psz_string = strdup( newval.psz_string );
1410             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1411         }
1412         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1413         {
1414             p_sys->render[i].b_absolute = false;
1415             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1416                                     newval.i_int : p_sys->i_width;
1417         }
1418         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1419         {
1420             p_sys->render[i].b_absolute = false;
1421             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1422                                     newval.i_int : p_sys->i_height;
1423         }
1424         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1425         {
1426             p_sys->render[i].b_absolute = true;
1427             p_sys->render[i].i_pos = newval.i_int;
1428         }
1429         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1430         {
1431             p_sys->render[i].text_style.i_font_size = newval.i_int;
1432         }
1433         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1434         {
1435             p_sys->render[i].text_style.i_font_color = newval.i_int;
1436         }
1437         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1438         {
1439             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1440         }
1441 #if defined(FBOSD_BLENDING)
1442         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1443         {
1444             p_sys->render[i].i_alpha = newval.i_int;
1445         }
1446 #endif
1447     }
1448     return VLC_SUCCESS;
1449 }