]> git.sesse.net Git - vlc/blob - src/video_output/video_widgets.c
Added Play and Pause OSD Icons for hotkeys.
[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 RECT_EMPTY 0
32 #define RECT_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 * );
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 == RECT_FILLED) or empty (fill == RECT_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 == RECT_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 == RECT_FILLED) or empty (fill == RECT_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( fill == RECT_FILLED )
111     {
112         for( y = i_y1; y <= i_mid; y++ )
113         {
114             h = y - i_y1;
115             for( x = i_x1; x <= i_x1 + h && x <= i_x2; x++ )
116             {
117                 p_widget->p_pic[ x + p_widget->i_width * y ] = 1;
118                 p_widget->p_pic[ x + p_widget->i_width * ( i_y2 - h ) ] = 1;
119             }
120         }
121     }
122     else
123     {
124         for( y = i_y1; y <= i_mid; y++ )
125         {
126             h = y - i_y1;
127             p_widget->p_pic[ i_x1 + p_widget->i_width * y ] = 1;
128             p_widget->p_pic[ i_x1 + h + p_widget->i_width * y ] = 1;
129             p_widget->p_pic[ i_x1 + p_widget->i_width * ( i_y2 - h ) ] = 1;
130             p_widget->p_pic[ i_x1 + h + p_widget->i_width * ( i_y2 - h ) ] = 1;
131         }
132     }
133 }
134
135 /*****************************************************************************
136  * Render: place string in picture
137  *****************************************************************************
138  * This function merges the previously rendered freetype glyphs into a picture
139  *****************************************************************************/
140 static void Render( vout_thread_t *p_vout, picture_t *p_pic,
141                     const subpicture_t *p_subpic )
142 {
143     int i_fade_alpha = 255;
144     mtime_t i_fade_start = ( p_subpic->i_stop + p_subpic->i_start ) / 2;
145     mtime_t i_now = mdate();
146
147     if( i_now >= i_fade_start )
148     {
149         i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
150                        ( p_subpic->i_stop - i_fade_start );
151     }
152
153     switch( p_vout->output.i_chroma )
154     {
155         /* I420 target, no scaling */
156         case VLC_FOURCC('I','4','2','0'):
157         case VLC_FOURCC('I','Y','U','V'):
158         case VLC_FOURCC('Y','V','1','2'):
159             RenderI420( p_vout, p_pic, p_subpic, i_fade_alpha );
160             break;
161         /* RV32 target, scaling */
162         case VLC_FOURCC('R','V','2','4'):
163         case VLC_FOURCC('R','V','3','2'):
164             RenderRV32( p_vout, p_pic, p_subpic, i_fade_alpha );
165             break;
166         /* NVidia or BeOS overlay, no scaling */
167         case VLC_FOURCC('Y','U','Y','2'):
168             RenderYUY2( p_vout, p_pic, p_subpic, i_fade_alpha );
169             break;
170
171         default:
172             msg_Err( p_vout, "unknown chroma, can't render SPU" );
173             break;
174     }
175 }
176
177 /**
178  * Draw a widget on a I420 (or similar) picture
179  */
180 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
181                     const subpicture_t *p_subpic, int i_fade_alpha )
182 {
183     subpicture_sys_t *p_widget = p_subpic->p_sys;
184     int i_plane, x, y, pen_x, pen_y;
185
186     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
187     {
188         uint8_t *p_in = p_pic->p[ i_plane ].p_pixels;
189         int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
190
191         if ( i_plane == 0 )
192         {
193             pen_x = p_widget->i_x;
194             pen_y = p_widget->i_y;
195 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
196 #define pixel p_in[ ( pen_y + y ) * i_pic_pitch + pen_x + x ]
197             for( y = 0; y < p_widget->i_height; y++ )
198             {
199                 for( x = 0; x < p_widget->i_width; x++ )
200                 {
201                     if( alpha == 0 ) continue;
202                     pen_y--;
203                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
204                     pen_y++; pen_x--;
205                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
206                     pen_x += 2;
207                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
208                     pen_y++; pen_x--;
209                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
210                     pen_y--;
211                 }
212             }
213             for( y = 0; y < p_widget->i_height; y++ )
214             {
215                 for( x = 0; x < p_widget->i_width; x++ )
216                 {
217                     pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
218                             ( 255 * alpha >> 8 );
219                  }
220              }
221 #undef alpha
222 #undef pixel
223         }
224         else
225         {
226             pen_x = p_widget->i_x >> 1;
227             pen_y = p_widget->i_y >> 1;
228 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
229 #define pixel p_in[ ( pen_y + (y >> 1) ) * i_pic_pitch + pen_x + (x >> 1) ]
230             for( y = 0; y < p_widget->i_height; y+=2 )
231             {
232                 for( x = 0; x < p_widget->i_width; x+=2 )
233                 {
234                     if( alpha == 0 ) continue;
235                     pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
236                         ( 0x80 * alpha >> 8 );
237                 }
238             }
239 #undef alpha
240 #undef pixel
241         }
242     }
243
244 }
245
246 /**
247  * Draw a widget on a YUY2 picture
248  */
249 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
250                         const subpicture_t *p_subpic, int i_fade_alpha )
251 {
252     subpicture_sys_t *p_widget = p_subpic->p_sys;
253     int x, y, pen_x, pen_y;
254     uint8_t *p_in = p_pic->p[0].p_pixels;
255     int i_pic_pitch = p_pic->p[0].i_pitch;
256
257     pen_x = p_widget->i_x;
258     pen_y = p_widget->i_y;
259 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
260 #define pixel p_in[ ( pen_y + y ) * i_pic_pitch + 2 * ( pen_x + x ) ]
261     for( y = 0; y < p_widget->i_height; y++ )
262     {
263         for( x = 0; x < p_widget->i_width; x++ )
264         {
265             pen_y--;
266             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
267             pen_y++; pen_x--;
268             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
269             pen_x += 2;
270             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
271             pen_y++; pen_x--;
272             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
273             pen_y--;
274         }
275     }
276     for( y = 0; y < p_widget->i_height; y++ )
277     {
278         for( x = 0; x < p_widget->i_width; x++ )
279         {
280             pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
281                     ( 255 * alpha >> 8 );
282          }
283      }
284 #undef alpha
285 #undef pixel
286 }
287
288 /**
289  * Draw a widget on a RV32 picture
290  */
291 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
292                     const subpicture_t *p_subpic, int i_fade_alpha )
293 {
294     subpicture_sys_t *p_widget = p_subpic->p_sys;
295     int x, y, pen_x, pen_y;
296     uint8_t *p_in = p_pic->p[0].p_pixels;
297     int i_pic_pitch = p_pic->p[0].i_pitch;
298
299     pen_x = p_widget->i_x;
300     pen_y = p_widget->i_y;
301
302 #define alpha p_widget->p_pic[ x + y * p_widget->i_width ] * i_fade_alpha
303 #define pixel( c ) p_in[ ( pen_y + y ) * i_pic_pitch + 4 * ( pen_x + x ) + c ]
304     for(y = 0; y < p_widget->i_height; y++ )
305     {
306         for( x = 0; x < p_widget->i_width; x++ )
307         {
308             pen_y--;
309             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
310             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
311             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
312             pen_y++; pen_x--;
313             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
314             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
315             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
316             pen_x += 2;
317             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
318             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
319             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
320             pen_y++; pen_x--;
321             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
322             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
323             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
324             pen_y--;
325         }
326     }
327     for(y = 0; y < p_widget->i_height; y++ )
328     {
329         for( x = 0; x < p_widget->i_width; x++ )
330         {
331             pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
332                 ( 255 * alpha >> 8 );
333             pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
334                 ( 255 * alpha >> 8 );
335             pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
336                 ( 255 * alpha >> 8 );
337         }
338     }
339 #undef alpha
340 #undef pixel
341 }
342
343 /*****************************************************************************
344  * Creates and initializes an OSD widget.
345  *****************************************************************************/
346 subpicture_t *vout_CreateWidget( vout_thread_t *p_vout )
347 {
348     subpicture_t *p_subpic;
349     subpicture_sys_t *p_widget;
350     mtime_t i_now = mdate();
351
352     p_subpic = 0;
353     p_widget = 0;
354
355     /* Create and initialize a subpicture */
356     p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
357     if( p_subpic == NULL )
358     {
359         return NULL;
360     }
361     p_subpic->pf_render = Render;
362     p_subpic->pf_destroy = FreeWidget;
363     p_subpic->i_start = i_now;
364     p_subpic->i_stop = i_now + 1200000;
365     p_subpic->b_ephemer = VLC_FALSE;
366
367     p_widget = malloc( sizeof(subpicture_sys_t) );
368     if( p_widget == NULL )
369     {
370         FreeWidget( p_subpic );
371         vout_DestroySubPicture( p_vout, p_subpic );
372         return NULL;
373     }
374     p_subpic->p_sys = p_widget;
375
376     return p_subpic;
377 }
378
379 /*****************************************************************************
380  * Displays an OSD slider.
381  * Type 0 is position slider-like, and type 1 is volume slider-like.
382  *****************************************************************************/
383 void vout_OSDSlider( vlc_object_t *p_caller, int i_position, short i_type )
384 {
385     vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT,
386                                              FIND_ANYWHERE );
387     subpicture_t *p_subpic;
388     subpicture_sys_t *p_widget;
389     int i_x_margin, i_y_margin;
390
391     if( p_vout == NULL || !config_GetInt( p_caller, "osd" ) || i_position < 0 )
392     {
393         return;
394     }
395
396     p_subpic = vout_CreateWidget( p_vout );
397     if( p_subpic == NULL )
398     {
399         return;
400     }
401     p_widget = p_subpic->p_sys;
402
403     i_y_margin = p_vout->render.i_height / 10;
404     i_x_margin = i_y_margin;
405     if( i_type == 0 )
406     {
407         p_widget->i_width = p_vout->render.i_width - 2 * i_x_margin;
408         p_widget->i_height = p_vout->render.i_height / 20;
409         p_widget->i_x = i_x_margin;
410         p_widget->i_y = p_vout->render.i_height - i_y_margin -
411                         p_widget->i_height;
412     }
413     else
414     {
415         p_widget->i_width = p_vout->render.i_width / 40;
416         p_widget->i_height = p_vout->render.i_height - 2 * i_y_margin;
417         p_widget->i_x = p_vout->render.i_width - i_x_margin -
418                         p_widget->i_width;
419         p_widget->i_y = i_y_margin;
420     }
421
422     p_widget->p_pic = (uint8_t *)malloc( p_widget->i_width *
423                                          p_widget->i_height );
424     if( p_widget->p_pic == NULL )
425     {
426         FreeWidget( p_subpic );
427         vout_DestroySubPicture( p_vout, p_subpic );
428         return;
429     }
430     memset( p_widget->p_pic, 0, p_widget->i_width * p_widget->i_height );
431
432     if( i_type == 0 )
433     {
434         int i_x_pos = ( p_widget->i_width - 2 ) * i_position / 100;
435         int i_y_pos = p_widget->i_height / 2;
436         DrawRect( p_vout, p_subpic, i_x_pos - 1, 2, i_x_pos + 1,
437                   p_widget->i_height - 3, RECT_FILLED );
438         DrawRect( p_vout, p_subpic, 0, 0, p_widget->i_width - 1,
439                   p_widget->i_height - 1, RECT_EMPTY );
440     }
441     else if( i_type == 1 )
442     {
443         int i_y_pos = p_widget->i_height / 2;
444         DrawRect( p_vout, p_subpic, 2, p_widget->i_height -
445                   ( p_widget->i_height - 2 ) * i_position / 100,
446                   p_widget->i_width - 3, p_widget->i_height - 3,
447                   RECT_FILLED );
448         DrawRect( p_vout, p_subpic, 1, i_y_pos, 1, i_y_pos, RECT_FILLED );
449         DrawRect( p_vout, p_subpic, p_widget->i_width - 2, i_y_pos,
450                   p_widget->i_width - 2, i_y_pos, RECT_FILLED );
451         DrawRect( p_vout, p_subpic, 0, 0, p_widget->i_width - 1,
452                   p_widget->i_height - 1, RECT_EMPTY );
453     }
454
455     vlc_mutex_lock( &p_vout->change_lock );
456
457     if( p_vout->p_last_osd_message )
458     {
459         vout_DestroySubPicture( p_vout, p_vout->p_last_osd_message );
460     }
461     p_vout->p_last_osd_message = p_subpic;
462     vout_DisplaySubPicture( p_vout, p_subpic );
463
464     vlc_mutex_unlock( &p_vout->change_lock );
465
466     vlc_object_release( p_vout );
467     return;
468 }
469
470 /*****************************************************************************
471  * Displays an OSD icon.
472  * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON
473  *****************************************************************************/
474 void vout_OSDIcon( vlc_object_t *p_caller, short i_type )
475 {
476     vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT,
477                                              FIND_ANYWHERE );
478     subpicture_t *p_subpic;
479     subpicture_sys_t *p_widget;
480     int i_x_margin, i_y_margin;
481
482     if( p_vout == NULL || !config_GetInt( p_caller, "osd" ) )
483     {
484         return;
485     }
486
487     p_subpic = vout_CreateWidget( p_vout );
488     if( p_subpic == NULL )
489     {
490         return;
491     }
492     p_widget = p_subpic->p_sys;
493
494     i_y_margin = p_vout->render.i_height / 15;
495     i_x_margin = i_y_margin;
496     p_widget->i_width = p_vout->render.i_width / 22;
497     p_widget->i_height = p_widget->i_width;
498     p_widget->i_x = p_vout->render.i_width - i_x_margin -
499                     p_widget->i_width;
500     p_widget->i_y = i_y_margin;
501
502     p_widget->p_pic = (uint8_t *)malloc( p_widget->i_width *
503                                          p_widget->i_height );
504     if( p_widget->p_pic == NULL )
505     {
506         FreeWidget( p_subpic );
507         vout_DestroySubPicture( p_vout, p_subpic );
508         return;
509     }
510     memset( p_widget->p_pic, 0, p_widget->i_width * p_widget->i_height );
511
512     if( i_type == OSD_PAUSE_ICON )
513     {
514         int i_bar_width = p_widget->i_width / 3;
515         DrawRect( p_vout, p_subpic, 0, 0, i_bar_width - 1,
516                   p_widget->i_height - 1, RECT_FILLED );
517         DrawRect( p_vout, p_subpic, p_widget->i_width - i_bar_width, 0,
518                   p_widget->i_width - 1, p_widget->i_height - 1, RECT_FILLED );
519     }
520     else if( i_type == OSD_PLAY_ICON )
521     {
522         int i_mid = p_widget->i_height >> 1;
523         int i_delta = ( p_widget->i_width - i_mid ) >> 1;
524         int i_y2 = ( ( p_widget->i_height - 1 ) >> 1 ) * 2;
525         DrawTriangle( p_vout, p_subpic, i_delta, 0,
526                       p_widget->i_width - i_delta, i_y2, RECT_FILLED );
527     }
528
529     vlc_mutex_lock( &p_vout->change_lock );
530
531     if( p_vout->p_last_osd_message )
532     {
533         vout_DestroySubPicture( p_vout, p_vout->p_last_osd_message );
534     }
535     p_vout->p_last_osd_message = p_subpic;
536     vout_DisplaySubPicture( p_vout, p_subpic );
537
538     vlc_mutex_unlock( &p_vout->change_lock );
539
540     vlc_object_release( p_vout );
541     return;
542 }
543
544 /**
545  * Frees the widget.
546  */
547 static void FreeWidget( subpicture_t *p_subpic )
548 {
549     subpicture_sys_t *p_widget = p_subpic->p_sys;
550
551     if( p_subpic->p_sys == NULL ) return;
552
553     if( p_widget->p_pic != NULL )
554     {
555         free( p_widget->p_pic );
556     }
557     free( p_widget );
558 }