]> git.sesse.net Git - vlc/blob - modules/video_filter/logo.c
* modules/video_filter/logo.c: complete rewrite using the alpha-blending module.
[vlc] / modules / video_filter / logo.c
1 /*****************************************************************************
2  * logo.c : logo video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Simon Latapie <garf@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <png.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35
36 #include "vlc_filter.h"
37 #include "filter_common.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  Create    ( vlc_object_t * );
43 static void Destroy   ( vlc_object_t * );
44
45 static int  Init      ( vout_thread_t * );
46 static void End       ( vout_thread_t * );
47 static void Render    ( vout_thread_t *, picture_t * );
48
49 static int  SendEvents( vlc_object_t *, char const *,
50                         vlc_value_t, vlc_value_t, void * );
51 static int MouseEvent ( vlc_object_t *, char const *,
52                         vlc_value_t , vlc_value_t , void * );
53 static int Control    ( vout_thread_t *, int, va_list );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58 #define FILE_TEXT N_("Logo filename")
59 #define FILE_LONGTEXT N_("The file must be in PNG RGBA 8bits format (for now).")
60 #define POSX_TEXT N_("X coordinate of the logo")
61 #define POSX_LONGTEXT N_("You can move the logo by left-clicking on it." )
62 #define POSY_TEXT N_("Y coordinate of the logo")
63 #define POSY_LONGTEXT N_("You can move the logo by left-clicking on it." )
64 #define TRANS_TEXT N_("Transparency of the logo")
65 #define TRANS_LONGTEXT N_("You can set the logo transparency value here " \
66   "(from 0 for full transparency to 255 for full opacity)." )
67
68 vlc_module_begin();
69     set_description( _("Logo video filter") );
70     set_capability( "video filter", 0 );
71
72     add_file( "logo-file", NULL, NULL, FILE_TEXT, FILE_LONGTEXT, VLC_FALSE );
73     add_integer( "logo-x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_FALSE );
74     add_integer( "logo-y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_FALSE );
75     add_integer_with_range( "logo-transparency", 255, 0, 255, NULL,
76         TRANS_TEXT, TRANS_LONGTEXT, VLC_FALSE );
77
78     add_shortcut( "logo" );
79     set_callbacks( Create, Destroy );
80 vlc_module_end();
81
82 /*****************************************************************************
83  * vout_sys_t: logo video output method descriptor
84  *****************************************************************************
85  * This structure is part of the video output thread descriptor.
86  * It describes the Invert specific properties of an output thread.
87  *****************************************************************************/
88 struct vout_sys_t
89 {
90     vout_thread_t *p_vout;
91
92     filter_t *p_blend;
93     picture_t *p_pic;
94
95     int i_width, i_height;
96     int posx, posy;
97 };
98
99 /*****************************************************************************
100  * LoadPNG: loads the PNG logo into memory
101  *****************************************************************************/
102 static picture_t *LoadPNG( vlc_object_t *p_this )
103 {
104     picture_t *p_pic;
105     char *psz_filename;
106     vlc_value_t val;
107     FILE *file;
108     int i, j, i_trans;
109
110     png_uint_32 i_width, i_height;
111     int i_color_type, i_interlace_type, i_compression_type, i_filter_type;
112     int i_bit_depth;
113     png_bytep *p_row_pointers;
114     png_structp p_png;
115     png_infop p_info, p_end_info;
116
117     var_Create( p_this, "logo-file", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
118     var_Get( p_this, "logo-file", &val );
119     psz_filename = val.psz_string;
120     if( !psz_filename ) return 0;
121
122     if( !(file = fopen( psz_filename , "rb" )) )
123     {
124         msg_Err( p_this , "logo file (%s) not found", psz_filename );
125         free( psz_filename );
126         return 0;
127     }
128     free( psz_filename );
129
130     p_png = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
131     p_info = png_create_info_struct( p_png );
132     p_end_info = png_create_info_struct( p_png );
133     png_init_io( p_png, file );
134     png_read_info( p_png, p_info );
135     png_get_IHDR( p_png, p_info, &i_width, &i_height,
136                   &i_bit_depth, &i_color_type, &i_interlace_type,
137                   &i_compression_type, &i_filter_type);
138
139     p_row_pointers = malloc( sizeof(png_bytep) * i_height );
140     for( i = 0; i < (int)i_height; i++ )
141     {
142         p_row_pointers[i] = malloc( 4 * ( i_bit_depth + 7 ) / 8 * i_width );
143     }
144     png_read_image( p_png, p_row_pointers );
145     png_read_end( p_png, p_end_info );
146
147     fclose( file );
148     png_destroy_read_struct( &p_png, &p_info, &p_end_info );
149
150     /* Convert to YUVA */
151     p_pic = malloc( sizeof(picture_t) );
152     if( vout_AllocatePicture( p_this, p_pic, VLC_FOURCC('Y','U','V','A'),
153                               i_width, i_height, VOUT_ASPECT_FACTOR ) !=
154         VLC_SUCCESS )
155     {
156         free( p_row_pointers );
157         return 0;
158     }
159
160     var_Create(p_this, "logo-transparency", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
161     var_Get( p_this, "logo-transparency", &val );
162     i_trans = val.i_int;
163
164     for( j = 0; j < (int)i_height ; j++ )
165     {
166         for( i = 0; i < (int)i_width ; i++ )
167         {
168             uint8_t (*p)[4];
169             int i_offset = i + j * p_pic->p[Y_PLANE].i_pitch;
170
171             p = (void *)p_row_pointers[j];
172             p_pic->p[Y_PLANE].p_pixels[i_offset] =
173                 (p[i][0] * 257L + p[i][1] * 504 + p[i][2] * 98)/1000 + 16;
174             p_pic->p[U_PLANE].p_pixels[i_offset] =
175                 (p[i][2] * 439L - p[i][0] * 148 - p[i][1] * 291)/1000 + 128;
176             p_pic->p[V_PLANE].p_pixels[i_offset] =
177                 (p[i][0] * 439L - p[i][1] * 368 - p[i][2] * 71)/1000 + 128;
178             p_pic->p[A_PLANE].p_pixels[i_offset] = (p[i][3] * i_trans) / 255;
179         }
180     }
181
182     free( p_row_pointers );
183     return p_pic;
184 }
185
186 /*****************************************************************************
187  * Create: allocates logo video thread output method
188  *****************************************************************************
189  * This function allocates and initializes a Invert vout method.
190  *****************************************************************************/
191 static int Create( vlc_object_t *p_this )
192 {
193     vout_thread_t *p_vout = (vout_thread_t *)p_this;
194     vout_sys_t *p_sys;
195     vlc_value_t val;
196
197     /* Allocate structure */
198     p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
199     if( p_sys == NULL )
200     {
201         msg_Err( p_vout, "out of memory" );
202         return VLC_ENOMEM;
203     }
204
205     p_vout->pf_init = Init;
206     p_vout->pf_end = End;
207     p_vout->pf_manage = NULL;
208     p_vout->pf_render = Render;
209     p_vout->pf_display = NULL;
210     p_vout->pf_control = Control;
211
212     var_Create( p_this, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
213     var_Get( p_this, "logo-x", &val );
214     p_sys->posx = val.i_int;
215     var_Create( p_this, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
216     var_Get( p_this, "logo-y", &val );
217     p_sys->posy = val.i_int;
218
219     p_sys->p_pic = LoadPNG( p_this );
220     if( !p_sys->p_pic )
221     {
222         free( p_sys );
223         return VLC_EGENERIC;
224     }
225
226     p_sys->i_width = p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
227     p_sys->i_height = p_sys->p_pic->p[Y_PLANE].i_visible_lines;
228
229     return VLC_SUCCESS;
230 }
231
232 /*****************************************************************************
233  * Init: initialize logo video thread output method
234  *****************************************************************************/
235 static int Init( vout_thread_t *p_vout )
236 {
237     vout_sys_t *p_sys = p_vout->p_sys;
238     picture_t *p_pic;
239     int i_index;
240
241     I_OUTPUTPICTURES = 0;
242
243     /* Initialize the output structure */
244     p_vout->output.i_chroma = p_vout->render.i_chroma;
245     p_vout->output.i_width  = p_vout->render.i_width;
246     p_vout->output.i_height = p_vout->render.i_height;
247     p_vout->output.i_aspect = p_vout->render.i_aspect;
248
249     /* Load the video blending filter */
250     p_sys->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
251     vlc_object_attach( p_sys->p_blend, p_vout );
252     p_sys->p_blend->fmt_out.video.i_x_offset =
253         p_sys->p_blend->fmt_out.video.i_y_offset = 0;
254     p_sys->p_blend->fmt_in.video.i_x_offset =
255         p_sys->p_blend->fmt_in.video.i_y_offset = 0;
256     p_sys->p_blend->fmt_out.video.i_aspect = p_vout->render.i_aspect;
257     p_sys->p_blend->fmt_out.video.i_chroma = p_vout->output.i_chroma;
258     p_sys->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','A');
259     p_sys->p_blend->fmt_in.video.i_aspect = VOUT_ASPECT_FACTOR;
260     p_sys->p_blend->fmt_in.video.i_width =
261         p_sys->p_blend->fmt_in.video.i_visible_width =
262             p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
263     p_sys->p_blend->fmt_in.video.i_height =
264         p_sys->p_blend->fmt_in.video.i_visible_height =
265             p_sys->p_pic->p[Y_PLANE].i_visible_lines;
266     p_sys->p_blend->fmt_out.video.i_width =
267         p_sys->p_blend->fmt_out.video.i_visible_width =
268            p_vout->output.i_width;
269     p_sys->p_blend->fmt_out.video.i_height =
270         p_sys->p_blend->fmt_out.video.i_visible_height =
271             p_vout->output.i_height;
272
273     p_sys->p_blend->p_module =
274         module_Need( p_sys->p_blend, "video blending", 0, 0 );
275     if( !p_sys->p_blend->p_module )
276     {
277         msg_Err( p_vout, "can't open blending filter, aborting" );
278         vlc_object_detach( p_sys->p_blend );
279         vlc_object_destroy( p_sys->p_blend );
280         return VLC_EGENERIC;
281     }
282
283     /* Try to open the real video output */
284     msg_Dbg( p_vout, "spawning the real video output" );
285
286     p_sys->p_vout =
287         vout_Create( p_vout, p_vout->render.i_width, p_vout->render.i_height,
288                      p_vout->render.i_chroma, p_vout->render.i_aspect );
289
290     /* Everything failed */
291     if( p_sys->p_vout == NULL )
292     {
293         msg_Err( p_vout, "can't open vout, aborting" );
294         return VLC_EGENERIC;
295     }
296
297     var_AddCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
298     var_AddCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
299
300     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
301     ADD_CALLBACKS( p_sys->p_vout, SendEvents );
302     ADD_PARENT_CALLBACKS( SendEventsToChild );
303
304     return VLC_SUCCESS;
305 }
306
307 /*****************************************************************************
308  * End: terminate logo video thread output method
309  *****************************************************************************/
310 static void End( vout_thread_t *p_vout )
311 {
312     vout_sys_t *p_sys = p_vout->p_sys;
313     int i_index;
314
315     /* Free the fake output buffers we allocated */
316     for( i_index = I_OUTPUTPICTURES ; i_index ; )
317     {
318         i_index--;
319         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
320     }
321
322     var_DelCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
323     var_DelCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
324
325     if( p_sys->p_vout )
326     {
327         DEL_CALLBACKS( p_sys->p_vout, SendEvents );
328         vlc_object_detach( p_sys->p_vout );
329         vout_Destroy( p_sys->p_vout );
330     }
331
332     if( p_sys->p_blend->p_module )
333         module_Unneed( p_sys->p_blend, p_sys->p_blend->p_module );
334     vlc_object_detach( p_sys->p_blend );
335     vlc_object_destroy( p_sys->p_blend );
336 }
337
338 /*****************************************************************************
339  * Destroy: destroy logo video thread output method
340  *****************************************************************************
341  * Terminate an output method created by InvertCreateOutputMethod
342  *****************************************************************************/
343 static void Destroy( vlc_object_t *p_this )
344 {
345     vout_thread_t *p_vout = (vout_thread_t *)p_this;
346     vout_sys_t *p_sys = p_vout->p_sys;
347
348     DEL_PARENT_CALLBACKS( SendEventsToChild );
349
350     if( p_sys->p_pic && p_sys->p_pic->p_data_orig )
351         free( p_sys->p_pic->p_data_orig );
352     if( p_sys->p_pic ) free( p_sys->p_pic );
353
354     free( p_sys );
355 }
356
357 /*****************************************************************************
358  * Render: render the logo onto the video
359  *****************************************************************************/
360 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
361 {
362     vout_sys_t *p_sys = p_vout->p_sys;
363     picture_t *p_outpic;
364
365     /* This is a new frame. Get a structure from the video_output. */
366     while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
367     {
368         if( p_vout->b_die || p_vout->b_error ) return;
369         msleep( VOUT_OUTMEM_SLEEP );
370     }
371
372     vout_CopyPicture( p_vout, p_outpic, p_pic );
373     vout_DatePicture( p_sys->p_vout, p_outpic, p_pic->date );
374
375     p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
376                                     p_sys->p_pic, p_sys->posx, p_sys->posy );
377
378     vout_DisplayPicture( p_sys->p_vout, p_outpic );
379 }
380
381 /*****************************************************************************
382  * SendEvents: forward mouse and keyboard events to the parent p_vout
383  *****************************************************************************/
384 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
385                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
386 {
387     var_Set( (vlc_object_t *)p_data, psz_var, newval );
388     return VLC_SUCCESS;
389 }
390
391 /*****************************************************************************
392  * MouseEvent: callback for mouse events
393  *****************************************************************************/
394 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
395                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
396 {
397     vout_thread_t *p_vout = (vout_thread_t*)p_data;
398     vout_sys_t *p_sys = p_vout->p_sys;
399     vlc_value_t valb;
400     int i_delta;
401
402     var_Get( p_vout->p_sys->p_vout, "mouse-button-down", &valb );
403
404     i_delta = newval.i_int - oldval.i_int;
405
406     if( (valb.i_int & 0x1) == 0 )
407     {
408         return VLC_SUCCESS;
409     }
410
411     if( psz_var[6] == 'x' )
412     {
413         vlc_value_t valy;
414         var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
415         if( newval.i_int >= (int)p_sys->posx &&
416             valy.i_int >= (int)p_sys->posy &&
417             newval.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
418             valy.i_int <= (int)(p_sys->posy + p_sys->i_height) )
419         {
420             p_sys->posx = __MIN( __MAX( p_sys->posx + i_delta, 0 ),
421                           p_vout->output.i_width - p_sys->i_width );
422         }
423     }
424     else if( psz_var[6] == 'y' )
425     {
426         vlc_value_t valx;
427         var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
428         if( valx.i_int >= (int)p_sys->posx &&
429             newval.i_int >= (int)p_sys->posy &&
430             valx.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
431             newval.i_int <= (int)(p_sys->posy + p_sys->i_height) )
432         {
433             p_sys->posy = __MIN( __MAX( p_sys->posy + i_delta, 0 ),
434                           p_vout->output.i_height - p_sys->i_height );
435         }
436     }
437
438     return VLC_SUCCESS;
439 }
440
441 /*****************************************************************************
442  * Control: control facility for the vout (forwards to child vout)
443  *****************************************************************************/
444 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
445 {
446     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
447 }
448
449 /*****************************************************************************
450  * SendEventsToChild: forward events to the child/children vout
451  *****************************************************************************/
452 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
453                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
454 {
455     vout_thread_t *p_vout = (vout_thread_t *)p_this;
456     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
457     return VLC_SUCCESS;
458 }