* ALL: separation of the SPU engine from the VOUT.
[vlc.git] / 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 = spu_CreateSubpicture( p_vout->p_spu );
386     if( p_subpic == NULL )
387     {
388         return NULL;
389     }
390     p_subpic->i_channel = i_channel;
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         spu_DestroySubpicture( p_vout->p_spu, 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         spu_DestroySubpicture( p_vout->p_spu, 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         DrawRect( p_vout, p_subpic, i_x_pos - 1, 2, i_x_pos + 1,
467                   p_widget->i_height - 3, STYLE_FILLED );
468         DrawRect( p_vout, p_subpic, 0, 0, p_widget->i_width - 1,
469                   p_widget->i_height - 1, STYLE_EMPTY );
470     }
471     else if( i_type == OSD_VERT_SLIDER )
472     {
473         int i_y_pos = p_widget->i_height / 2;
474         DrawRect( p_vout, p_subpic, 2, p_widget->i_height -
475                   ( p_widget->i_height - 2 ) * i_position / 100,
476                   p_widget->i_width - 3, p_widget->i_height - 3,
477                   STYLE_FILLED );
478         DrawRect( p_vout, p_subpic, 1, i_y_pos, 1, i_y_pos, STYLE_FILLED );
479         DrawRect( p_vout, p_subpic, p_widget->i_width - 2, i_y_pos,
480                   p_widget->i_width - 2, i_y_pos, STYLE_FILLED );
481         DrawRect( p_vout, p_subpic, 0, 0, p_widget->i_width - 1,
482                   p_widget->i_height - 1, STYLE_EMPTY );
483     }
484
485     spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
486
487     vlc_object_release( p_vout );
488     return;
489 }
490
491 /*****************************************************************************
492  * Displays an OSD icon.
493  * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON
494  *****************************************************************************/
495 void vout_OSDIcon( vlc_object_t *p_caller, int i_channel, short i_type )
496 {
497     vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT,
498                                              FIND_ANYWHERE );
499     subpicture_t *p_subpic;
500     subpicture_sys_t *p_widget;
501     int i_x_margin, i_y_margin;
502
503     if( p_vout == NULL || !config_GetInt( p_caller, "osd" ) )
504     {
505         return;
506     }
507
508     p_subpic = vout_CreateWidget( p_vout, i_channel );
509     if( p_subpic == NULL )
510     {
511         return;
512     }
513     p_widget = p_subpic->p_sys;
514
515     i_y_margin = p_vout->render.i_height / 15;
516     i_x_margin = i_y_margin;
517     p_widget->i_width = p_vout->render.i_width / 20;
518     p_widget->i_height = p_widget->i_width;
519     p_widget->i_x = p_vout->render.i_width - i_x_margin -
520                     p_widget->i_width;
521     p_widget->i_y = i_y_margin;
522
523     p_widget->p_pic = (uint8_t *)malloc( p_widget->i_width *
524                                          p_widget->i_height );
525     if( p_widget->p_pic == NULL )
526     {
527         FreeWidget( p_subpic );
528         spu_DestroySubpicture( p_vout->p_spu, p_subpic );
529         return;
530     }
531     memset( p_widget->p_pic, 0, p_widget->i_width * p_widget->i_height );
532
533     if( i_type == OSD_PAUSE_ICON )
534     {
535         int i_bar_width = p_widget->i_width / 3;
536         DrawRect( p_vout, p_subpic, 0, 0, i_bar_width - 1,
537                   p_widget->i_height - 1, STYLE_FILLED );
538         DrawRect( p_vout, p_subpic, p_widget->i_width - i_bar_width, 0,
539                   p_widget->i_width - 1, p_widget->i_height - 1, STYLE_FILLED );
540     }
541     else if( i_type == OSD_PLAY_ICON )
542     {
543         int i_mid = p_widget->i_height >> 1;
544         int i_delta = ( p_widget->i_width - i_mid ) >> 1;
545         int i_y2 = ( ( p_widget->i_height - 1 ) >> 1 ) * 2;
546         DrawTriangle( p_vout, p_subpic, i_delta, 0,
547                       p_widget->i_width - i_delta, i_y2, STYLE_FILLED );
548     }
549     else if( i_type == OSD_SPEAKER_ICON || i_type == OSD_MUTE_ICON )
550     {
551         int i_mid = p_widget->i_height >> 1;
552         int i_delta = ( p_widget->i_width - i_mid ) >> 1;
553         int i_y2 = ( ( p_widget->i_height - 1 ) >> 1 ) * 2;
554         DrawRect( p_vout, p_subpic, i_delta, i_mid / 2,
555                   p_widget->i_width - i_delta,
556                   p_widget->i_height - 1 - i_mid / 2, STYLE_FILLED );
557         DrawTriangle( p_vout, p_subpic, p_widget->i_width - i_delta, 0,
558                       i_delta, i_y2, STYLE_FILLED );
559         if( i_type == OSD_MUTE_ICON )
560         {
561             int i;
562             for( i = 1; i < p_widget->i_width; i++ )
563             {
564                 int k = i + ( p_widget->i_height - i - 1 ) * p_widget->i_width;
565                 p_widget->p_pic[ k ] = 1 - p_widget->p_pic[ k ];
566             }
567         }
568     }
569
570     spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
571
572     vlc_object_release( p_vout );
573     return;
574 }
575
576 /**
577  * Frees the widget.
578  */
579 static void FreeWidget( subpicture_t *p_subpic )
580 {
581     subpicture_sys_t *p_widget = p_subpic->p_sys;
582
583     if( p_subpic->p_sys == NULL ) return;
584
585     if( p_widget->p_pic != NULL )
586     {
587         free( p_widget->p_pic );
588     }
589     free( p_widget );
590 }