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