]> git.sesse.net Git - vlc/blob - modules/video_filter/distort.c
10a570282173fb70c7acde6bf4dae70a43941dc3
[vlc] / modules / video_filter / distort.c
1 /*****************************************************************************
2  * distort.c : Misc video effects plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003 VideoLAN
5  * $Id$
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 <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <math.h>                                            /* sin(), cos() */
31
32 #include <vlc/vlc.h>
33 #include <vlc/vout.h>
34
35 #include "filter_common.h"
36
37 #define DISTORT_MODE_WAVE    1
38 #define DISTORT_MODE_RIPPLE  2
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  Create    ( vlc_object_t * );
44 static void Destroy   ( vlc_object_t * );
45
46 static int  Init      ( vout_thread_t * );
47 static void End       ( vout_thread_t * );
48 static void Render    ( vout_thread_t *, picture_t * );
49
50 static void DistortWave    ( vout_thread_t *, picture_t *, picture_t * );
51 static void DistortRipple  ( vout_thread_t *, picture_t *, picture_t * );
52
53 static int  SendEvents   ( vlc_object_t *, char const *,
54                            vlc_value_t, vlc_value_t, void * );
55
56 /*****************************************************************************
57  * Module descriptor
58  *****************************************************************************/
59 #define MODE_TEXT N_("Distort mode")
60 #define MODE_LONGTEXT N_("Distort mode, one of \"wave\" and \"ripple\"")
61
62 static char *mode_list[] = { "wave", "ripple" };
63 static char *mode_list_text[] = { N_("Wave"), N_("Ripple") };
64
65 vlc_module_begin();
66     set_description( _("Distort video filter") );
67     set_capability( "video filter", 0 );
68
69     add_string( "distort-mode", "wave", NULL, MODE_TEXT, MODE_LONGTEXT,
70                 VLC_FALSE );
71         change_string_list( mode_list, mode_list_text, 0 );
72
73     add_shortcut( "distort" );
74     set_callbacks( Create, Destroy );
75 vlc_module_end();
76
77 /*****************************************************************************
78  * vout_sys_t: Distort video output method descriptor
79  *****************************************************************************
80  * This structure is part of the video output thread descriptor.
81  * It describes the Distort specific properties of an output thread.
82  *****************************************************************************/
83 struct vout_sys_t
84 {
85     int i_mode;
86     vout_thread_t *p_vout;
87
88     /* For the wave mode */
89     double  f_angle;
90     mtime_t last_date;
91 };
92
93 /*****************************************************************************
94  * Control: control facility for the vout (forwards to child vout)
95  *****************************************************************************/
96 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
97 {
98     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
99 }
100
101 /*****************************************************************************
102  * Create: allocates Distort video thread output method
103  *****************************************************************************
104  * This function allocates and initializes a Distort vout method.
105  *****************************************************************************/
106 static int Create( vlc_object_t *p_this )
107 {
108     vout_thread_t *p_vout = (vout_thread_t *)p_this;
109     char *psz_method, *psz_method_tmp;
110
111     /* Allocate structure */
112     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
113     if( p_vout->p_sys == NULL )
114     {
115         msg_Err( p_vout, "out of memory" );
116         return VLC_ENOMEM;
117     }
118
119     p_vout->pf_init = Init;
120     p_vout->pf_end = End;
121     p_vout->pf_manage = NULL;
122     p_vout->pf_render = Render;
123     p_vout->pf_display = NULL;
124     p_vout->pf_control = Control;
125
126     p_vout->p_sys->i_mode = 0;
127
128     if( !(psz_method = psz_method_tmp
129           = config_GetPsz( p_vout, "distort-mode" )) )
130     {
131         msg_Err( p_vout, "configuration variable %s empty, using 'wave'",
132                          "distort-mode" );
133         p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
134     }
135     else
136     {
137
138         if( !strcmp( psz_method, "wave" ) )
139         {
140             p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
141         }
142         else if( !strcmp( psz_method, "ripple" ) )
143         {
144             p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
145         }
146         else
147         {
148             msg_Err( p_vout, "no valid distort mode provided, "
149                              "using wave" );
150             p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
151         }
152     }
153     free( psz_method_tmp );
154
155     return VLC_SUCCESS;
156 }
157
158 /*****************************************************************************
159  * Init: initialize Distort video thread output method
160  *****************************************************************************/
161 static int Init( vout_thread_t *p_vout )
162 {
163     int i_index;
164     picture_t *p_pic;
165
166     I_OUTPUTPICTURES = 0;
167
168     /* Initialize the output structure */
169     p_vout->output.i_chroma = p_vout->render.i_chroma;
170     p_vout->output.i_width  = p_vout->render.i_width;
171     p_vout->output.i_height = p_vout->render.i_height;
172     p_vout->output.i_aspect = p_vout->render.i_aspect;
173
174     /* Try to open the real video output */
175     msg_Dbg( p_vout, "spawning the real video output" );
176
177     p_vout->p_sys->p_vout = vout_Create( p_vout,
178                            p_vout->render.i_width, p_vout->render.i_height,
179                            p_vout->render.i_chroma, p_vout->render.i_aspect );
180
181     /* Everything failed */
182     if( p_vout->p_sys->p_vout == NULL )
183     {
184         msg_Err( p_vout, "cannot open vout, aborting" );
185         return VLC_EGENERIC;
186     }
187
188     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
189
190     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
191
192     ADD_PARENT_CALLBACKS( SendEventsToChild );
193
194     p_vout->p_sys->f_angle = 0.0;
195     p_vout->p_sys->last_date = 0;
196
197     return VLC_SUCCESS;
198 }
199
200 /*****************************************************************************
201  * End: terminate Distort video thread output method
202  *****************************************************************************/
203 static void End( vout_thread_t *p_vout )
204 {
205     int i_index;
206
207     /* Free the fake output buffers we allocated */
208     for( i_index = I_OUTPUTPICTURES ; i_index ; )
209     {
210         i_index--;
211         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
212     }
213 }
214
215 /*****************************************************************************
216  * Destroy: destroy Distort video thread output method
217  *****************************************************************************
218  * Terminate an output method created by DistortCreateOutputMethod
219  *****************************************************************************/
220 static void Destroy( vlc_object_t *p_this )
221 {
222     vout_thread_t *p_vout = (vout_thread_t *)p_this;
223
224     if( p_vout->p_sys->p_vout )
225     {
226         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
227         vlc_object_detach( p_vout->p_sys->p_vout );
228         vout_Destroy( p_vout->p_sys->p_vout );
229     }
230
231     DEL_PARENT_CALLBACKS( SendEventsToChild );
232
233     free( p_vout->p_sys );
234 }
235
236 /*****************************************************************************
237  * Render: displays previously rendered output
238  *****************************************************************************
239  * This function send the currently rendered image to Distort image, waits
240  * until it is displayed and switch the two rendering buffers, preparing next
241  * frame.
242  *****************************************************************************/
243 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
244 {
245     picture_t *p_outpic;
246
247     /* This is a new frame. Get a structure from the video_output. */
248     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
249               == NULL )
250     {
251         if( p_vout->b_die || p_vout->b_error )
252         {
253             return;
254         }
255         msleep( VOUT_OUTMEM_SLEEP );
256     }
257
258     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
259
260     switch( p_vout->p_sys->i_mode )
261     {
262         case DISTORT_MODE_WAVE:
263             DistortWave( p_vout, p_pic, p_outpic );
264             break;
265
266         case DISTORT_MODE_RIPPLE:
267             DistortRipple( p_vout, p_pic, p_outpic );
268             break;
269
270         default:
271             break;
272     }
273
274     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
275 }
276
277 /*****************************************************************************
278  * DistortWave: draw a wave effect on the picture
279  *****************************************************************************/
280 static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
281                                                 picture_t *p_outpic )
282 {
283     int i_index;
284     double f_angle;
285     mtime_t new_date = mdate();
286
287     p_vout->p_sys->f_angle += (new_date - p_vout->p_sys->last_date) / 200000.0;
288     p_vout->p_sys->last_date = new_date;
289     f_angle = p_vout->p_sys->f_angle;
290
291     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
292     {
293         int i_line, i_num_lines, i_offset;
294         uint8_t black_pixel;
295         uint8_t *p_in, *p_out;
296
297         p_in = p_inpic->p[i_index].p_pixels;
298         p_out = p_outpic->p[i_index].p_pixels;
299
300         i_num_lines = p_inpic->p[i_index].i_visible_lines;
301
302         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
303
304         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
305         for( i_line = 0 ; i_line < i_num_lines ; i_line++ )
306         {
307             /* Calculate today's offset, don't go above 1/20th of the screen */
308             i_offset = (int)( (double)(p_inpic->p[i_index].i_visible_pitch)
309                          * sin( f_angle + 10.0 * (double)i_line
310                                                / (double)i_num_lines )
311                          / 20.0 );
312
313             if( i_offset )
314             {
315                 if( i_offset < 0 )
316                 {
317                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
318                              p_inpic->p[i_index].i_visible_pitch + i_offset );
319                     p_in += p_inpic->p[i_index].i_pitch;
320                     p_out += p_outpic->p[i_index].i_pitch;
321                     memset( p_out + i_offset, black_pixel, -i_offset );
322                 }
323                 else
324                 {
325                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
326                              p_inpic->p[i_index].i_visible_pitch - i_offset );
327                     memset( p_out, black_pixel, i_offset );
328                     p_in += p_inpic->p[i_index].i_pitch;
329                     p_out += p_outpic->p[i_index].i_pitch;
330                 }
331             }
332             else
333             {
334                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
335                                           p_inpic->p[i_index].i_visible_pitch );
336                 p_in += p_inpic->p[i_index].i_pitch;
337                 p_out += p_outpic->p[i_index].i_pitch;
338             }
339
340         }
341     }
342 }
343
344 /*****************************************************************************
345  * DistortRipple: draw a ripple effect at the bottom of the picture
346  *****************************************************************************/
347 static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
348                                                   picture_t *p_outpic )
349 {
350     int i_index;
351     double f_angle;
352     mtime_t new_date = mdate();
353
354     p_vout->p_sys->f_angle -= (p_vout->p_sys->last_date - new_date) / 100000.0;
355     p_vout->p_sys->last_date = new_date;
356     f_angle = p_vout->p_sys->f_angle;
357
358     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
359     {
360         int i_line, i_first_line, i_num_lines, i_offset;
361         uint8_t black_pixel;
362         uint8_t *p_in, *p_out;
363
364         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
365
366         i_num_lines = p_inpic->p[i_index].i_visible_lines;
367
368         i_first_line = i_num_lines * 4 / 5;
369
370         p_in = p_inpic->p[i_index].p_pixels;
371         p_out = p_outpic->p[i_index].p_pixels;
372
373         for( i_line = 0 ; i_line < i_first_line ; i_line++ )
374         {
375             p_vout->p_vlc->pf_memcpy( p_out, p_in,
376                                       p_inpic->p[i_index].i_visible_pitch );
377             p_in += p_inpic->p[i_index].i_pitch;
378             p_out += p_outpic->p[i_index].i_pitch;
379         }
380
381         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
382         for( i_line = i_first_line ; i_line < i_num_lines ; i_line++ )
383         {
384             /* Calculate today's offset, don't go above 1/20th of the screen */
385             i_offset = (int)( (double)(p_inpic->p[i_index].i_pitch)
386                          * sin( f_angle + 2.0 * (double)i_line
387                                               / (double)( 1 + i_line
388                                                             - i_first_line) )
389                          * (double)(i_line - i_first_line)
390                          / (double)i_num_lines
391                          / 8.0 );
392
393             if( i_offset )
394             {
395                 if( i_offset < 0 )
396                 {
397                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
398                              p_inpic->p[i_index].i_visible_pitch + i_offset );
399                     p_in -= p_inpic->p[i_index].i_pitch;
400                     p_out += p_outpic->p[i_index].i_pitch;
401                     memset( p_out + i_offset, black_pixel, -i_offset );
402                 }
403                 else
404                 {
405                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
406                              p_inpic->p[i_index].i_visible_pitch - i_offset );
407                     memset( p_out, black_pixel, i_offset );
408                     p_in -= p_inpic->p[i_index].i_pitch;
409                     p_out += p_outpic->p[i_index].i_pitch;
410                 }
411             }
412             else
413             {
414                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
415                                           p_inpic->p[i_index].i_visible_pitch );
416                 p_in -= p_inpic->p[i_index].i_pitch;
417                 p_out += p_outpic->p[i_index].i_pitch;
418             }
419
420         }
421     }
422 }
423
424 /*****************************************************************************
425  * SendEvents: forward mouse and keyboard events to the parent p_vout
426  *****************************************************************************/
427 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
428                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
429 {
430     var_Set( (vlc_object_t *)p_data, psz_var, newval );
431
432     return VLC_SUCCESS;
433 }
434
435 /*****************************************************************************
436  * SendEventsToChild: forward events to the child/children vout
437  *****************************************************************************/
438 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
439                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
440 {
441     vout_thread_t *p_vout = (vout_thread_t *)p_this;
442     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
443     return VLC_SUCCESS;
444 }