]> git.sesse.net Git - vlc/blob - plugins/filter/distort.c
bab126e669ca2839b6acfe15196db2cd01f0886a
[vlc] / plugins / filter / distort.c
1 /*****************************************************************************
2  * distort.c : Misc video effects plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: distort.c,v 1.13 2002/05/28 22:49:25 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.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 <errno.h>
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <math.h>                                            /* sin(), cos() */
32
33 #include <videolan/vlc.h>
34
35 #include "video.h"
36 #include "video_output.h"
37
38 #include "filter_common.h"
39
40 #define DISTORT_MODE_WAVE    1
41 #define DISTORT_MODE_RIPPLE  2
42
43 /*****************************************************************************
44  * Capabilities defined in the other files.
45  *****************************************************************************/
46 static void vout_getfunctions( function_list_t * p_function_list );
47
48 /*****************************************************************************
49  * Build configuration tree.
50  *****************************************************************************/
51 MODULE_CONFIG_START
52 ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
53 ADD_STRING  ( "distort-mode", "wave", NULL, N_("distort mode"),
54               N_("one of \"wave\" and \"ripple\"") )
55 MODULE_CONFIG_STOP
56
57 MODULE_INIT_START
58     SET_DESCRIPTION( _("miscellaneous video effects module") )
59     /* Capability score set to 0 because we don't want to be spawned
60      * as a video output unless explicitly requested to */
61     ADD_CAPABILITY( VOUT, 0 )
62     ADD_SHORTCUT( "distort" )
63 MODULE_INIT_STOP
64
65 MODULE_ACTIVATE_START
66     vout_getfunctions( &p_module->p_functions->vout );
67 MODULE_ACTIVATE_STOP
68
69 MODULE_DEACTIVATE_START
70 MODULE_DEACTIVATE_STOP
71
72 /*****************************************************************************
73  * vout_sys_t: Distort video output method descriptor
74  *****************************************************************************
75  * This structure is part of the video output thread descriptor.
76  * It describes the Distort specific properties of an output thread.
77  *****************************************************************************/
78 typedef struct vout_sys_s
79 {
80     int i_mode;
81     struct vout_thread_s *p_vout;
82
83     /* For the wave mode */
84     double  f_angle;
85     mtime_t last_date;
86
87 } vout_sys_t;
88
89 /*****************************************************************************
90  * Local prototypes
91  *****************************************************************************/
92 static int  vout_Create    ( struct vout_thread_s * );
93 static int  vout_Init      ( struct vout_thread_s * );
94 static void vout_End       ( struct vout_thread_s * );
95 static void vout_Destroy   ( struct vout_thread_s * );
96 static int  vout_Manage    ( struct vout_thread_s * );
97 static void vout_Render    ( struct vout_thread_s *, struct picture_s * );
98 static void vout_Display   ( struct vout_thread_s *, struct picture_s * );
99
100 static void DistortWave    ( struct vout_thread_s *, struct picture_s *,
101                                                      struct picture_s * );
102 static void DistortRipple  ( struct vout_thread_s *, struct picture_s *,
103                                                      struct picture_s * );
104
105 /*****************************************************************************
106  * Functions exported as capabilities. They are declared as static so that
107  * we don't pollute the namespace too much.
108  *****************************************************************************/
109 static void vout_getfunctions( function_list_t * p_function_list )
110 {
111     p_function_list->functions.vout.pf_create     = vout_Create;
112     p_function_list->functions.vout.pf_init       = vout_Init;
113     p_function_list->functions.vout.pf_end        = vout_End;
114     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
115     p_function_list->functions.vout.pf_manage     = vout_Manage;
116     p_function_list->functions.vout.pf_render     = vout_Render;
117     p_function_list->functions.vout.pf_display    = vout_Display;
118 }
119
120 /*****************************************************************************
121  * vout_Create: allocates Distort video thread output method
122  *****************************************************************************
123  * This function allocates and initializes a Distort vout method.
124  *****************************************************************************/
125 static int vout_Create( vout_thread_t *p_vout )
126 {
127     char *psz_method, *psz_method_tmp;
128
129     /* Allocate structure */
130     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
131     if( p_vout->p_sys == NULL )
132     {
133         intf_ErrMsg("error: %s", strerror(ENOMEM) );
134         return( 1 );
135     }
136     p_vout->p_sys->i_mode = 0;
137     /* Look what method was requested from command line*/
138     if( !(psz_method = psz_method_tmp
139           = config_GetPszVariable( "filter" )) )
140     {
141         intf_ErrMsg( "vout error: configuration variable %s empty",
142                      "filter" );
143         return( 1 );
144     }
145     while( *psz_method && *psz_method != ':' )
146     {
147         psz_method++;
148     }
149
150     if( !strcmp( psz_method, ":wave" ) )
151     {
152         p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
153     }
154     else if( !strcmp( psz_method, ":ripple" ) )
155     {
156         p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
157     }
158     free( psz_method_tmp );
159     if( !p_vout->p_sys->i_mode )
160     {
161         /* No method given in commandline. Look what method was
162          requested in configuration system */
163         if( !(psz_method = psz_method_tmp
164               = config_GetPszVariable( "distort-mode" )) )
165         {
166             intf_ErrMsg( "vout error: configuration variable %s empty "
167                          "using wave",
168                          "distort-mode" );
169             p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
170         }
171         else {
172         
173             if( !strcmp( psz_method, "wave" ) )
174             {
175                 p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
176             }
177             else if( !strcmp( psz_method, "ripple" ) )
178             {
179                 p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
180             }
181             
182             else
183             {
184                 intf_ErrMsg( "filter error: no valid distort mode provided, "
185                              "using distort:wave" );
186                 p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
187             }
188         }
189     }
190     free( psz_method_tmp );
191     
192     return( 0 );
193 }
194     
195 /*****************************************************************************
196  * vout_Init: initialize Distort video thread output method
197  *****************************************************************************/
198 static int vout_Init( vout_thread_t *p_vout )
199 {
200     int i_index;
201     char *psz_filter;
202     picture_t *p_pic;
203
204     I_OUTPUTPICTURES = 0;
205
206     /* Initialize the output structure */
207     p_vout->output.i_chroma = p_vout->render.i_chroma;
208     p_vout->output.i_width  = p_vout->render.i_width;
209     p_vout->output.i_height = p_vout->render.i_height;
210     p_vout->output.i_aspect = p_vout->render.i_aspect;
211
212     /* Try to open the real video output */
213     psz_filter = config_GetPszVariable( "filter" );
214     config_PutPszVariable( "filter", NULL );
215
216     intf_WarnMsg( 1, "filter: spawning the real video output" );
217
218     p_vout->p_sys->p_vout =
219         vout_CreateThread( NULL,
220                            p_vout->render.i_width, p_vout->render.i_height,
221                            p_vout->render.i_chroma, p_vout->render.i_aspect );
222
223     config_PutPszVariable( "filter", psz_filter );
224     if( psz_filter ) free( psz_filter );
225
226     /* Everything failed */
227     if( p_vout->p_sys->p_vout == NULL )
228     {
229         intf_ErrMsg( "filter error: can't open vout, aborting" );
230
231         return( 0 );
232     }
233  
234     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
235
236     p_vout->p_sys->f_angle = 0.0;
237     p_vout->p_sys->last_date = 0;
238
239     return( 0 );
240 }
241
242 /*****************************************************************************
243  * vout_End: terminate Distort video thread output method
244  *****************************************************************************/
245 static void vout_End( vout_thread_t *p_vout )
246 {
247     int i_index;
248
249     /* Free the fake output buffers we allocated */
250     for( i_index = I_OUTPUTPICTURES ; i_index ; )
251     {
252         i_index--;
253         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
254     }
255 }
256
257 /*****************************************************************************
258  * vout_Destroy: destroy Distort video thread output method
259  *****************************************************************************
260  * Terminate an output method created by DistortCreateOutputMethod
261  *****************************************************************************/
262 static void vout_Destroy( vout_thread_t *p_vout )
263 {
264     vout_DestroyThread( p_vout->p_sys->p_vout, NULL );
265
266     free( p_vout->p_sys );
267 }
268
269 /*****************************************************************************
270  * vout_Manage: handle Distort events
271  *****************************************************************************
272  * This function should be called regularly by video output thread. It manages
273  * console events. It returns a non null value on error.
274  *****************************************************************************/
275 static int vout_Manage( vout_thread_t *p_vout )
276 {
277     return( 0 );
278 }
279
280 /*****************************************************************************
281  * vout_Render: displays previously rendered output
282  *****************************************************************************
283  * This function send the currently rendered image to Distort image, waits
284  * until it is displayed and switch the two rendering buffers, preparing next
285  * frame.
286  *****************************************************************************/
287 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
288 {
289     picture_t *p_outpic;
290
291     /* This is a new frame. Get a structure from the video_output. */
292     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
293               == NULL )
294     {
295         if( p_vout->b_die || p_vout->b_error )
296         {
297             return;
298         }
299         msleep( VOUT_OUTMEM_SLEEP );
300     }
301
302     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
303
304     switch( p_vout->p_sys->i_mode )
305     {
306         case DISTORT_MODE_WAVE:
307             DistortWave( p_vout, p_pic, p_outpic );
308             break;
309
310         case DISTORT_MODE_RIPPLE:
311             DistortRipple( p_vout, p_pic, p_outpic );
312             break;
313
314         default:
315             break;
316     }
317
318     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
319 }
320
321 /*****************************************************************************
322  * vout_Display: displays previously rendered output
323  *****************************************************************************
324  * This function send the currently rendered image to Invert image, waits
325  * until it is displayed and switch the two rendering buffers, preparing next
326  * frame.
327  *****************************************************************************/
328 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
329 {
330     ;
331 }
332
333 /*****************************************************************************
334  * DistortWave: draw a wave effect on the picture
335  *****************************************************************************/
336 static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
337                                                 picture_t *p_outpic )
338 {
339     int i_index;
340     double f_angle;
341     mtime_t new_date = mdate();
342
343     p_vout->p_sys->f_angle += (new_date - p_vout->p_sys->last_date) / 200000.0;
344     p_vout->p_sys->last_date = new_date;
345     f_angle = p_vout->p_sys->f_angle;
346
347     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
348     {
349         int i_line, i_num_lines, i_offset;
350         u8 black_pixel;
351         u8 *p_in, *p_out;
352
353         p_in = p_inpic->p[i_index].p_pixels;
354         p_out = p_outpic->p[i_index].p_pixels;
355
356         i_num_lines = p_inpic->p[i_index].i_lines;
357
358         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
359
360         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
361         for( i_line = 0 ; i_line < i_num_lines ; i_line++ )
362         {
363             /* Calculate today's offset, don't go above 1/20th of the screen */
364             i_offset = (double)(p_inpic->p[i_index].i_pitch)
365                          * sin( f_angle + 10.0 * (double)i_line
366                                                / (double)i_num_lines )
367                          / 20.0;
368
369             if( i_offset )
370             {
371                 if( i_offset < 0 )
372                 {
373                     FAST_MEMCPY( p_out, p_in - i_offset,
374                                  p_inpic->p[i_index].i_pitch + i_offset );
375                     p_in += p_inpic->p[i_index].i_pitch;
376                     p_out += p_outpic->p[i_index].i_pitch;
377                     memset( p_out + i_offset, black_pixel, -i_offset );
378                 }
379                 else
380                 {
381                     FAST_MEMCPY( p_out + i_offset, p_in,
382                                  p_inpic->p[i_index].i_pitch - i_offset );
383                     memset( p_out, black_pixel, i_offset );
384                     p_in += p_inpic->p[i_index].i_pitch;
385                     p_out += p_outpic->p[i_index].i_pitch;
386                 }
387             }
388             else
389             {
390                 FAST_MEMCPY( p_out, p_in, p_inpic->p[i_index].i_pitch );
391                 p_in += p_inpic->p[i_index].i_pitch;
392                 p_out += p_outpic->p[i_index].i_pitch;
393             }
394
395         }
396     }
397 }
398
399 /*****************************************************************************
400  * DistortRipple: draw a ripple effect at the bottom of the picture
401  *****************************************************************************/
402 static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
403                                                   picture_t *p_outpic )
404 {
405     int i_index;
406     double f_angle;
407     mtime_t new_date = mdate();
408
409     p_vout->p_sys->f_angle -= (p_vout->p_sys->last_date - new_date) / 100000.0;
410     p_vout->p_sys->last_date = new_date;
411     f_angle = p_vout->p_sys->f_angle;
412
413     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
414     {
415         int i_line, i_first_line, i_num_lines, i_offset;
416         u8 black_pixel;
417         u8 *p_in, *p_out;
418
419         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
420
421         i_num_lines = p_inpic->p[i_index].i_lines;
422
423         i_first_line = i_num_lines * 4 / 5;
424
425         p_in = p_inpic->p[i_index].p_pixels;
426         p_out = p_outpic->p[i_index].p_pixels;
427
428         FAST_MEMCPY( p_out, p_in, i_first_line * p_inpic->p[i_index].i_pitch );
429
430         p_in += i_first_line * p_inpic->p[i_index].i_pitch;
431         p_out += i_first_line * p_outpic->p[i_index].i_pitch;
432
433         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
434         for( i_line = i_first_line ; i_line < i_num_lines ; i_line++ )
435         {
436             /* Calculate today's offset, don't go above 1/20th of the screen */
437             i_offset = (double)(p_inpic->p[i_index].i_pitch)
438                          * sin( f_angle + 2.0 * (double)i_line
439                                               / (double)( 1 + i_line
440                                                             - i_first_line) )
441                          * (double)(i_line - i_first_line)
442                          / (double)i_num_lines
443                          / 8.0;
444
445             if( i_offset )
446             {
447                 if( i_offset < 0 )
448                 {
449                     FAST_MEMCPY( p_out, p_in - i_offset,
450                                  p_inpic->p[i_index].i_pitch + i_offset );
451                     p_in -= p_inpic->p[i_index].i_pitch;
452                     p_out += p_outpic->p[i_index].i_pitch;
453                     memset( p_out + i_offset, black_pixel, -i_offset );
454                 }
455                 else
456                 {
457                     FAST_MEMCPY( p_out + i_offset, p_in,
458                                  p_inpic->p[i_index].i_pitch - i_offset );
459                     memset( p_out, black_pixel, i_offset );
460                     p_in -= p_inpic->p[i_index].i_pitch;
461                     p_out += p_outpic->p[i_index].i_pitch;
462                 }
463             }
464             else
465             {
466                 FAST_MEMCPY( p_out, p_in, p_inpic->p[i_index].i_pitch );
467                 p_in -= p_inpic->p[i_index].i_pitch;
468                 p_out += p_outpic->p[i_index].i_pitch;
469             }
470
471         }
472     }
473 }
474