]> git.sesse.net Git - vlc/blob - src/video_output/video_widgets.c
Support for multiple OSD channels :
[vlc] / src / video_output / video_widgets.c
1 /*****************************************************************************
2  * video_widgets.c : OSD widgets manipulation functions
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Author: Yoann Peronneau <yoann@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                                /* free() */
28 #include <vlc/vout.h>
29 #include <osd.h>
30
31 #define STYLE_EMPTY 0
32 #define STYLE_FILLED 1
33
34 /*****************************************************************************
35  * Local prototypes
36  *****************************************************************************/
37 static void DrawRect( vout_thread_t *, subpicture_t *, int, int, int, int,
38                       short );
39 static void DrawTriangle( vout_thread_t *, subpicture_t *, int, int, int, int,
40                           short );
41 static void Render    ( vout_thread_t *, picture_t *, const subpicture_t * );
42 static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
43                         int );
44 static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
45                         int );
46 static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
47                         int );
48 static subpicture_t *vout_CreateWidget( vout_thread_t *, int );
49 static void FreeWidget( subpicture_t * );
50
51 /**
52  * Private data in a subpicture.
53  */
54 struct subpicture_sys_t
55 {
56     int     i_x;
57     int     i_y;
58     int     i_width;
59     int     i_height;
60     uint8_t *p_pic;
61 };
62
63 /*****************************************************************************
64  * Draws a rectangle at the given position in the subpic.
65  * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY).
66  *****************************************************************************/
67 static void DrawRect( vout_thread_t *p_vout, subpicture_t *p_subpic,
68                       int i_x1, int i_y1, int i_x2, int i_y2, short fill )
69 {
70     int x, y;
71     subpicture_sys_t *p_widget = p_subpic->p_sys;
72
73     if( fill == STYLE_FILLED )
74     {
75         for( y = i_y1; y <= i_y2; y++ )
76         {
77             for( x = i_x1; x <= i_x2; x++ )
78             {
79                 p_widget->p_pic[ x + p_widget->i_width * y ] = 1;
80             }
81         }
82     }
83     else
84     {
85         for( y = i_y1; y <= i_y2; y++ )
86         {
87             p_widget->p_pic[ i_x1 + p_widget->i_width * y ] = 1;
88             p_widget->p_pic[ i_x2 + p_widget->i_width * y ] = 1;
89         }
90         for( x = i_x1; x <= i_x2; x++ )
91         {
92             p_widget->p_pic[ x + p_widget->i_width * i_y1 ] = 1;
93             p_widget->p_pic[ x + p_widget->i_width * i_y2 ] = 1;
94         }
95     }
96 }
97
98 /*****************************************************************************
99  * Draws a triangle at the given position in the subpic.
100  * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY).
101  *****************************************************************************/
102 static void DrawTriangle( vout_thread_t *p_vout, subpicture_t *p_subpic,
103                           int i_x1, int i_y1, int i_x2, int i_y2, short fill )
104 {
105     int x, y, i_mid, h;
106     subpicture_sys_t *p_widget = p_subpic->p_sys;
107
108     i_mid = i_y1 + ( ( i_y2 - i_y1 ) >> 1 );
109
110     if( i_x2 >= i_x1 )
111     {
112         if( fill == STYLE_FILLED )
113         {
114             for( y = i_y1; y <= i_mid; y++ )
115             {
116                 h = y - i_y1;
117                 for( x = i_x1; x <= i_x1 + h && x <= i_x2; x++ )
118                 {
119                     p_widget->p_pic[ x + p_widget->i_width * y ] = 1;
120                     p_widget->p_pic[ x + p_widget->i_width * ( i_y2 - h ) ] = 1;
121                 }
122             }
123         }
124         else
125         {
126             for( y = i_y1; y <= i_mid; y++ )
127             {
128                 h = y - i_y1;
129                 p_widget->p_pic[ i_x1 + p_widget->i_width * y ] = 1;
130                 p_widget->p_pic[ i_x1 + h + p_widget->i_width * y ] = 1;
131                 p_widget->p_pic[ i_x1 + p_widget->i_width * ( i_y2 - h ) ] = 1;
132                 p_widget->p_pic[ i_x1 + h + p_widget->i_width * ( i_y2 - h ) ] = 1;
133             }
134         }
135     }
136     else
137     {
138         if( fill == STYLE_FILLED )
139         {
140             for( y = i_y1; y <= i_mid; y++ )
141             {
142                 h = y - i_y1;
143                 for( x = i_x1; x >= i_x1 - h && x >= i_x2; x-- )
144                 {
145                     p_widget->p_pic[ x + p_widget->i_width * y ] = 1;
146                     p_widget->p_pic[ x + p_widget->i_width * ( i_y2 - h ) ] = 1;
147                 }
148             }
149         }
150         else
151         {
152             for( y = i_y1; y <= i_mid; y++ )
153             {
154                 h = y - i_y1;
155                 p_widget->p_pic[ i_x1 + p_widget->i_width * y ] = 1;
156                 p_widget->p_pic[ i_x1 - h + p_widget->i_width * y ] = 1;
157                 p_widget->p_pic[ i_x1 + p_widget->i_width * ( i_y2 - h ) ] = 1;
158                 p_widget->p_pic[ i_x1 - h + p_widget->i_width * ( i_y2 - h ) ] = 1;
159             }
160         }
161     }
162 }
163
164 /*****************************************************************************
165  * Render: place widget in picture
166  *****************************************************************************
167  * This function merges the previously drawn widget into a picture
168  *****************************************************************************/
169 static void Render( vout_thread_t *p_vout, picture_t *p_pic,
170                     const subpicture_t *p_subpic )
171 {
172     int i_fade_alpha = 255;
173     mtime_t i_fade_start = ( p_subpic->i_stop + p_subpic->i_start ) / 2;
174     mtime_t i_now = mdate();
175
176     if( i_now >= i_fade_start )
177     {
178         i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
179                        ( p_subpic->i_stop - i_fade_start );
180     }
181
182     switch( p_vout->output.i_chroma )
183     {
184         /* I420 target, no scaling */
185         case VLC_FOURCC('I','4','2','0'):
186         case VLC_FOURCC('I','Y','U','V'):
187         case VLC_FOURCC('Y','V','1','2'):
188             RenderI420( p_vout, p_pic, p_subpic, i_fade_alpha );
189             break;
190         /* RV32 target, scaling */
191         case VLC_FOURCC('R','V','2','4'):
192         case VLC_FOURCC('R','V','3','2'):
193             RenderRV32( p_vout, p_pic, p_subpic, i_fade_alpha );
194             break;
195         /* NVidia or BeOS overlay, no scaling */
196         case VLC_FOURCC('Y','U','Y','2'):
197             RenderYUY2( p_vout, p_pic, p_subpic, i_fade_alpha );
198             break;
199
200         default:
201             msg_Err( p_vout, "unknown chroma, can't render SPU" );
202             break;
203     }
204 }
205
206 /**
207  * Draw a widget on a I420 (or similar) picture
208  */
209 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
210                     const subpicture_t *p_subpic, int i_fade_alpha )
211 {
212     subpicture_sys_t *p_widget = p_subpic->p_sys;
213     int i_plane, x, y, pen_x, pen_y;
214
215     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
216     {
217         uint8_t *p_in = p_pic->p[ i_plane ].p_pixels;
218         int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
219
220         if ( i_plane == 0 )
221         {
222             pen_x = p_widget->i_x;
223             pen_y = p_widget->i_y;
224 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
225 #define pixel p_in[ ( pen_y + y ) * i_pic_pitch + pen_x + x ]
226             for( y = 0; y < p_widget->i_height; y++ )
227             {
228                 for( x = 0; x < p_widget->i_width; x++ )
229                 {
230                     if( alpha == 0 ) continue;
231                     pen_y--;
232                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
233                     pen_y++; pen_x--;
234                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
235                     pen_x += 2;
236                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
237                     pen_y++; pen_x--;
238                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
239                     pen_y--;
240                 }
241             }
242             for( y = 0; y < p_widget->i_height; y++ )
243             {
244                 for( x = 0; x < p_widget->i_width; x++ )
245                 {
246                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
247                             ( 255 * alpha >> 8 );
248                  }
249              }
250 #undef alpha
251 #undef pixel
252         }
253         else
254         {
255             pen_x = p_widget->i_x >> 1;
256             pen_y = p_widget->i_y >> 1;
257 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
258 #define pixel p_in[ ( pen_y + (y >> 1) ) * i_pic_pitch + pen_x + (x >> 1) ]
259             for( y = 0; y < p_widget->i_height; y+=2 )
260             {
261                 for( x = 0; x < p_widget->i_width; x+=2 )
262                 {
263                     if( alpha == 0 ) continue;
264                     pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
265                         ( 0x80 * alpha >> 8 );
266                 }
267             }
268 #undef alpha
269 #undef pixel
270         }
271     }
272
273 }
274
275 /**
276  * Draw a widget on a YUY2 picture
277  */
278 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
279                         const subpicture_t *p_subpic, int i_fade_alpha )
280 {
281     subpicture_sys_t *p_widget = p_subpic->p_sys;
282     int x, y, pen_x, pen_y;
283     uint8_t *p_in = p_pic->p[0].p_pixels;
284     int i_pic_pitch = p_pic->p[0].i_pitch;
285
286     pen_x = p_widget->i_x;
287     pen_y = p_widget->i_y;
288 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
289 #define pixel p_in[ ( pen_y + y ) * i_pic_pitch + 2 * ( pen_x + x ) ]
290     for( y = 0; y < p_widget->i_height; y++ )
291     {
292         for( x = 0; x < p_widget->i_width; x++ )
293         {
294             pen_y--;
295             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
296             pen_y++; pen_x--;
297             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
298             pen_x += 2;
299             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
300             pen_y++; pen_x--;
301             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
302             pen_y--;
303         }
304     }
305     for( y = 0; y < p_widget->i_height; y++ )
306     {
307         for( x = 0; x < p_widget->i_width; x++ )
308         {
309             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
310                     ( 255 * alpha >> 8 );
311          }
312      }
313 #undef alpha
314 #undef pixel
315 }
316
317 /**
318  * Draw a widget on a RV32 picture
319  */
320 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
321                     const subpicture_t *p_subpic, int i_fade_alpha )
322 {
323     subpicture_sys_t *p_widget = p_subpic->p_sys;
324     int x, y, pen_x, pen_y;
325     uint8_t *p_in = p_pic->p[0].p_pixels;
326     int i_pic_pitch = p_pic->p[0].i_pitch;
327
328     pen_x = p_widget->i_x;
329     pen_y = p_widget->i_y;
330
331 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
332 #define pixel( c ) p_in[ ( pen_y + y ) * i_pic_pitch + 4 * ( pen_x + x ) + c ]
333     for(y = 0; y < p_widget->i_height; y++ )
334     {
335         for( x = 0; x < p_widget->i_width; x++ )
336         {
337             pen_y--;
338             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
339             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
340             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
341             pen_y++; pen_x--;
342             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
343             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
344             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
345             pen_x += 2;
346             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
347             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
348             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
349             pen_y++; pen_x--;
350             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
351             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
352             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
353             pen_y--;
354         }
355     }
356     for(y = 0; y < p_widget->i_height; y++ )
357     {
358         for( x = 0; x < p_widget->i_width; x++ )
359         {
360             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
361                 ( 255 * alpha >> 8 );
362             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
363                 ( 255 * alpha >> 8 );
364             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
365                 ( 255 * alpha >> 8 );
366         }
367     }
368 #undef alpha
369 #undef pixel
370 }
371
372 /*****************************************************************************
373  * Creates and initializes an OSD widget.
374  *****************************************************************************/
375 subpicture_t *vout_CreateWidget( vout_thread_t *p_vout, int i_channel )
376 {
377     subpicture_t *p_subpic;
378     subpicture_sys_t *p_widget;
379     mtime_t i_now = mdate();
380
381     p_subpic = 0;
382     p_widget = 0;
383
384     /* Create and initialize a subpicture */
385     p_subpic = vout_CreateSubPicture( p_vout, i_channel, GRAPH_CONTENT,
386                                       MEMORY_SUBPICTURE );
387     if( p_subpic == NULL )
388     {
389         return NULL;
390     }
391     p_subpic->pf_render = Render;
392     p_subpic->pf_destroy = FreeWidget;
393     p_subpic->i_start = i_now;
394     p_subpic->i_stop = i_now + 1200000;
395     p_subpic->b_ephemer = VLC_FALSE;
396
397     p_widget = malloc( sizeof(subpicture_sys_t) );
398     if( p_widget == NULL )
399     {
400         FreeWidget( p_subpic );
401         vout_DestroySubPicture( p_vout, p_subpic );
402         return NULL;
403     }
404     p_subpic->p_sys = p_widget;
405
406     return p_subpic;
407 }
408
409 /*****************************************************************************
410  * Displays an OSD slider.
411  * Types are: OSD_HOR_SLIDER and OSD_VERT_SLIDER.
412  *****************************************************************************/
413 void vout_OSDSlider( vlc_object_t *p_caller, int i_channel, int i_position,
414                      short i_type )
415 {
416     vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT,
417                                              FIND_ANYWHERE );
418     subpicture_t *p_subpic;
419     subpicture_sys_t *p_widget;
420     int i_x_margin, i_y_margin;
421
422     if( p_vout == NULL || !config_GetInt( p_caller, "osd" ) || i_position < 0 )
423     {
424         return;
425     }
426
427     p_subpic = vout_CreateWidget( p_vout, i_channel );
428     if( p_subpic == NULL )
429     {
430         return;
431     }
432     p_widget = p_subpic->p_sys;
433
434     i_y_margin = p_vout->render.i_height / 10;
435     i_x_margin = i_y_margin;
436     if( i_type == OSD_HOR_SLIDER )
437     {
438         p_widget->i_width = p_vout->render.i_width - 2 * i_x_margin;
439         p_widget->i_height = p_vout->render.i_height / 20;
440         p_widget->i_x = i_x_margin;
441         p_widget->i_y = p_vout->render.i_height - i_y_margin -
442                         p_widget->i_height;
443     }
444     else
445     {
446         p_widget->i_width = p_vout->render.i_width / 40;
447         p_widget->i_height = p_vout->render.i_height - 2 * i_y_margin;
448         p_widget->i_x = p_vout->render.i_width - i_x_margin -
449                         p_widget->i_width;
450         p_widget->i_y = i_y_margin;
451     }
452
453     p_widget->p_pic = (uint8_t *)malloc( p_widget->i_width *
454                                          p_widget->i_height );
455     if( p_widget->p_pic == NULL )
456     {
457         FreeWidget( p_subpic );
458         vout_DestroySubPicture( p_vout, p_subpic );
459         return;
460     }
461     memset( p_widget->p_pic, 0, p_widget->i_width * p_widget->i_height );
462
463     if( i_type == OSD_HOR_SLIDER )
464     {
465         int i_x_pos = ( p_widget->i_width - 2 ) * i_position / 100;
466         int i_y_pos = p_widget->i_height / 2;
467         DrawRect( p_vout, p_subpic, i_x_pos - 1, 2, i_x_pos + 1,
468                   p_widget->i_height - 3, STYLE_FILLED );
469         DrawRect( p_vout, p_subpic, 0, 0, p_widget->i_width - 1,
470                   p_widget->i_height - 1, STYLE_EMPTY );
471     }
472     else if( i_type == OSD_VERT_SLIDER )
473     {
474         int i_y_pos = p_widget->i_height / 2;
475         DrawRect( p_vout, p_subpic, 2, p_widget->i_height -
476                   ( p_widget->i_height - 2 ) * i_position / 100,
477                   p_widget->i_width - 3, p_widget->i_height - 3,
478                   STYLE_FILLED );
479         DrawRect( p_vout, p_subpic, 1, i_y_pos, 1, i_y_pos, STYLE_FILLED );
480         DrawRect( p_vout, p_subpic, p_widget->i_width - 2, i_y_pos,
481                   p_widget->i_width - 2, i_y_pos, STYLE_FILLED );
482         DrawRect( p_vout, p_subpic, 0, 0, p_widget->i_width - 1,
483                   p_widget->i_height - 1, STYLE_EMPTY );
484     }
485
486     vout_DisplaySubPicture( p_vout, p_subpic );
487
488     vlc_object_release( p_vout );
489     return;
490 }
491
492 /*****************************************************************************
493  * Displays an OSD icon.
494  * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON
495  *****************************************************************************/
496 void vout_OSDIcon( vlc_object_t *p_caller, short i_type )
497 {
498     vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT,
499                                              FIND_ANYWHERE );
500     subpicture_t *p_subpic;
501     subpicture_sys_t *p_widget;
502     int i_x_margin, i_y_margin, i_channel;
503
504     if( p_vout == NULL || !config_GetInt( p_caller, "osd" ) )
505     {
506         return;
507     }
508
509     switch( i_type )
510     {
511         case OSD_SPEAKER_ICON:
512             i_channel = VOLUME_CHAN;
513             break;
514         default:
515             i_channel = SOLO_CHAN;
516             break;
517     }
518
519     p_subpic = vout_CreateWidget( p_vout, i_channel );
520     if( p_subpic == NULL )
521     {
522         return;
523     }
524     p_widget = p_subpic->p_sys;
525
526     i_y_margin = p_vout->render.i_height / 15;
527     i_x_margin = i_y_margin;
528     p_widget->i_width = p_vout->render.i_width / 20;
529     p_widget->i_height = p_widget->i_width;
530     p_widget->i_x = p_vout->render.i_width - i_x_margin -
531                     p_widget->i_width;
532     p_widget->i_y = i_y_margin;
533
534     p_widget->p_pic = (uint8_t *)malloc( p_widget->i_width *
535                                          p_widget->i_height );
536     if( p_widget->p_pic == NULL )
537     {
538         FreeWidget( p_subpic );
539         vout_DestroySubPicture( p_vout, p_subpic );
540         return;
541     }
542     memset( p_widget->p_pic, 0, p_widget->i_width * p_widget->i_height );
543
544     if( i_type == OSD_PAUSE_ICON )
545     {
546         int i_bar_width = p_widget->i_width / 3;
547         DrawRect( p_vout, p_subpic, 0, 0, i_bar_width - 1,
548                   p_widget->i_height - 1, STYLE_FILLED );
549         DrawRect( p_vout, p_subpic, p_widget->i_width - i_bar_width, 0,
550                   p_widget->i_width - 1, p_widget->i_height - 1, STYLE_FILLED );
551     }
552     else if( i_type == OSD_PLAY_ICON )
553     {
554         int i_mid = p_widget->i_height >> 1;
555         int i_delta = ( p_widget->i_width - i_mid ) >> 1;
556         int i_y2 = ( ( p_widget->i_height - 1 ) >> 1 ) * 2;
557         DrawTriangle( p_vout, p_subpic, i_delta, 0,
558                       p_widget->i_width - i_delta, i_y2, STYLE_FILLED );
559     }
560     else if( i_type == OSD_SPEAKER_ICON || i_type == OSD_MUTE_ICON )
561     {
562         int i_mid = p_widget->i_height >> 1;
563         int i_delta = ( p_widget->i_width - i_mid ) >> 1;
564         int i_y2 = ( ( p_widget->i_height - 1 ) >> 1 ) * 2;
565         DrawRect( p_vout, p_subpic, i_delta, i_mid / 2,
566                   p_widget->i_width - i_delta,
567                   p_widget->i_height - 1 - i_mid / 2, STYLE_FILLED );
568         DrawTriangle( p_vout, p_subpic, p_widget->i_width - i_delta, 0,
569                       i_delta, i_y2, STYLE_FILLED );
570         if( i_type == OSD_MUTE_ICON )
571         {
572             int i;
573             for( i = 1; i < p_widget->i_width; i++ )
574             {
575                 int k = i + ( p_widget->i_height - i - 1 ) * p_widget->i_width;
576                 p_widget->p_pic[ k ] = 1 - p_widget->p_pic[ k ];
577             }
578         }
579     }
580
581     vout_DisplaySubPicture( p_vout, p_subpic );
582
583     vlc_object_release( p_vout );
584     return;
585 }
586
587 /**
588  * Frees the widget.
589  */
590 static void FreeWidget( subpicture_t *p_subpic )
591 {
592     subpicture_sys_t *p_widget = p_subpic->p_sys;
593
594     if( p_subpic->p_sys == NULL ) return;
595
596     if( p_widget->p_pic != NULL )
597     {
598         free( p_widget->p_pic );
599     }
600     free( p_widget );
601 }