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