]> git.sesse.net Git - vlc/blob - modules/video_filter/distort.c
Video filters and outputs strings (Refs:#438)
[vlc] / modules / video_filter / distort.c
1 /*****************************************************************************
2  * distort.c : Misc video effects plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Antoine Cellerier <dionoea -at- videolan -dot- 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <math.h>                                            /* sin(), cos() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35
36 #include "filter_common.h"
37 #include "vlc_image.h"
38
39 enum { WAVE, RIPPLE, GRADIENT, EDGE, HOUGH, PSYCHEDELIC };
40
41 /*****************************************************************************
42  * Local prototypes
43  *****************************************************************************/
44 static int  Create    ( vlc_object_t * );
45 static void Destroy   ( vlc_object_t * );
46
47 static int  Init      ( vout_thread_t * );
48 static void End       ( vout_thread_t * );
49 static void Render    ( vout_thread_t *, picture_t * );
50
51 static void DistortWave    ( vout_thread_t *, picture_t *, picture_t * );
52 static void DistortRipple  ( vout_thread_t *, picture_t *, picture_t * );
53 static void DistortGradient( vout_thread_t *, picture_t *, picture_t * );
54 static void DistortEdge    ( vout_thread_t *, picture_t *, picture_t * );
55 static void DistortHough   ( vout_thread_t *, picture_t *, picture_t * );
56 static void DistortPsychedelic( vout_thread_t *, picture_t *, picture_t * );
57
58 static int  SendEvents   ( vlc_object_t *, char const *,
59                            vlc_value_t, vlc_value_t, void * );
60
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64 #define MODE_TEXT N_("Distort mode")
65 #define MODE_LONGTEXT N_("Distort mode, one of \"wave\", \"ripple\", \"gradient\", \"edge\", \"hough\" and \"psychedelic\"")
66
67 #define GRADIENT_TEXT N_("Gradient image type")
68 #define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1). 0 will " \
69         "turn the image to white while 1 will keep colors." )
70
71 #define CARTOON_TEXT N_("Apply cartoon effect")
72 #define CARTOON_LONGTEXT N_("Apply cartoon effect. Used by \"gradient\" " \
73                             "and \"edge\".")
74
75 static char *mode_list[] = { "wave", "ripple", "gradient", "edge", "hough",
76                              "psychedelic" };
77 static char *mode_list_text[] = { N_("Wave"), N_("Ripple"), N_("Gradient"),
78                                   N_("Edge"), N_("Hough"), N_("Psychedelic") };
79
80 vlc_module_begin();
81     set_description( _("Distort video filter") );
82     set_shortname( N_( "Distortion" ));
83     set_capability( "video filter", 0 );
84     set_category( CAT_VIDEO );
85     set_subcategory( SUBCAT_VIDEO_VFILTER );
86
87     add_string( "distort-mode", "wave", NULL, MODE_TEXT, MODE_LONGTEXT,
88                 VLC_FALSE );
89         change_string_list( mode_list, mode_list_text, 0 );
90
91     add_integer_with_range( "distort-gradient-type", 0, 0, 1, NULL,
92                 GRADIENT_TEXT, GRADIENT_LONGTEXT, VLC_FALSE );
93     add_bool( "distort-cartoon", 1, NULL,
94                 CARTOON_TEXT, CARTOON_LONGTEXT, VLC_FALSE );
95
96     add_shortcut( "distort" );
97     set_callbacks( Create, Destroy );
98 vlc_module_end();
99
100 /*****************************************************************************
101  * vout_sys_t: Distort video output method descriptor
102  *****************************************************************************
103  * This structure is part of the video output thread descriptor.
104  * It describes the Distort specific properties of an output thread.
105  *****************************************************************************/
106 struct vout_sys_t
107 {
108     int i_mode;
109     vout_thread_t *p_vout;
110
111     /* For the wave mode */
112     double  f_angle;
113     mtime_t last_date;
114
115     /* For the gradient mode */
116     int i_gradient_type;
117     vlc_bool_t b_cartoon;
118
119     /* For pyschedelic mode */
120     image_handler_t *p_image;
121     unsigned int x, y, scale;
122     int xinc, yinc, scaleinc;
123     uint8_t u,v;
124
125     /* For hough mode */
126     int *p_pre_hough;
127 };
128
129 /*****************************************************************************
130  * Control: control facility for the vout (forwards to child vout)
131  *****************************************************************************/
132 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
133 {
134     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
135 }
136
137 /*****************************************************************************
138  * Create: allocates Distort video thread output method
139  *****************************************************************************
140  * This function allocates and initializes a Distort vout method.
141  *****************************************************************************/
142 static int Create( vlc_object_t *p_this )
143 {
144     vout_thread_t *p_vout = (vout_thread_t *)p_this;
145     char *psz_method, *psz_method_tmp;
146
147     /* Allocate structure */
148     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
149     if( p_vout->p_sys == NULL )
150     {
151         msg_Err( p_vout, "out of memory" );
152         return VLC_ENOMEM;
153     }
154
155     p_vout->pf_init = Init;
156     p_vout->pf_end = End;
157     p_vout->pf_manage = NULL;
158     p_vout->pf_render = Render;
159     p_vout->pf_display = NULL;
160     p_vout->pf_control = Control;
161
162     p_vout->p_sys->i_mode = 0;
163     p_vout->p_sys->p_pre_hough = NULL;
164
165     if( !(psz_method = psz_method_tmp
166           = config_GetPsz( p_vout, "distort-mode" )) )
167     {
168         msg_Err( p_vout, "configuration variable %s empty, using 'wave'",
169                          "distort-mode" );
170         p_vout->p_sys->i_mode = WAVE;
171     }
172     else
173     {
174         if( !strcmp( psz_method, "wave" ) )
175         {
176             p_vout->p_sys->i_mode = WAVE;
177         }
178         else if( !strcmp( psz_method, "ripple" ) )
179         {
180             p_vout->p_sys->i_mode = RIPPLE;
181         }
182         else if( !strcmp( psz_method, "gradient" ) )
183         {
184             p_vout->p_sys->i_mode = GRADIENT;
185         }
186         else if( !strcmp( psz_method, "edge" ) )
187         {
188             p_vout->p_sys->i_mode = EDGE;
189         }
190         else if( !strcmp( psz_method, "hough" ) )
191         {
192             p_vout->p_sys->i_mode = HOUGH;
193         }
194         else if( !strcmp( psz_method, "psychedelic" ) )
195         {
196             p_vout->p_sys->i_mode = PSYCHEDELIC;
197             p_vout->p_sys->x = 10;
198             p_vout->p_sys->y = 10;
199             p_vout->p_sys->scale = 1;
200             p_vout->p_sys->xinc = 1;
201             p_vout->p_sys->yinc = 1;
202             p_vout->p_sys->scaleinc = 1;
203             p_vout->p_sys->u = 0;
204             p_vout->p_sys->v = 0;
205         }
206         else
207         {
208             msg_Err( p_vout, "no valid distort mode provided, "
209                              "using wave" );
210             p_vout->p_sys->i_mode = WAVE;
211         }
212     }
213     free( psz_method_tmp );
214
215     p_vout->p_sys->i_gradient_type =
216         config_GetInt( p_vout, "distort-gradient-type" );
217     p_vout->p_sys->b_cartoon =
218         config_GetInt( p_vout, "distort-cartoon" );
219
220     return VLC_SUCCESS;
221 }
222
223 /*****************************************************************************
224  * Init: initialize Distort video thread output method
225  *****************************************************************************/
226 static int Init( vout_thread_t *p_vout )
227 {
228     int i_index;
229     picture_t *p_pic;
230     video_format_t fmt = {0};
231
232     I_OUTPUTPICTURES = 0;
233
234     /* Initialize the output structure */
235     p_vout->output.i_chroma = p_vout->render.i_chroma;
236     p_vout->output.i_width  = p_vout->render.i_width;
237     p_vout->output.i_height = p_vout->render.i_height;
238     p_vout->output.i_aspect = p_vout->render.i_aspect;
239     p_vout->fmt_out = p_vout->fmt_in;
240     fmt = p_vout->fmt_out;
241
242     /* Try to open the real video output */
243     msg_Dbg( p_vout, "spawning the real video output" );
244
245     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
246
247     /* Everything failed */
248     if( p_vout->p_sys->p_vout == NULL )
249     {
250         msg_Err( p_vout, "cannot open vout, aborting" );
251         return VLC_EGENERIC;
252     }
253
254     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
255
256     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
257
258     ADD_PARENT_CALLBACKS( SendEventsToChild );
259
260     p_vout->p_sys->f_angle = 0.0;
261     p_vout->p_sys->last_date = 0;
262
263     p_vout->p_sys->p_image = NULL;
264
265     return VLC_SUCCESS;
266 }
267
268 /*****************************************************************************
269  * End: terminate Distort video thread output method
270  *****************************************************************************/
271 static void End( vout_thread_t *p_vout )
272 {
273     int i_index;
274
275     /* Free the fake output buffers we allocated */
276     for( i_index = I_OUTPUTPICTURES ; i_index ; )
277     {
278         i_index--;
279         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
280     }
281 }
282
283 /*****************************************************************************
284  * Destroy: destroy Distort video thread output method
285  *****************************************************************************
286  * Terminate an output method created by DistortCreateOutputMethod
287  *****************************************************************************/
288 static void Destroy( vlc_object_t *p_this )
289 {
290     vout_thread_t *p_vout = (vout_thread_t *)p_this;
291
292     if( p_vout->p_sys->p_vout )
293     {
294         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
295         vlc_object_detach( p_vout->p_sys->p_vout );
296         vout_Destroy( p_vout->p_sys->p_vout );
297     }
298
299     DEL_PARENT_CALLBACKS( SendEventsToChild );
300
301     if(p_vout->p_sys->p_pre_hough)
302         free(p_vout->p_sys->p_pre_hough);
303
304     if( p_vout->p_sys->p_image )
305         image_HandlerDelete( p_vout->p_sys->p_image );
306
307     free( p_vout->p_sys );
308 }
309
310 /*****************************************************************************
311  * Render: displays previously rendered output
312  *****************************************************************************
313  * This function send the currently rendered image to Distort image, waits
314  * until it is displayed and switch the two rendering buffers, preparing next
315  * frame.
316  *****************************************************************************/
317 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
318 {
319     picture_t *p_outpic;
320
321     /* This is a new frame. Get a structure from the video_output. */
322     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
323               == NULL )
324     {
325         if( p_vout->b_die || p_vout->b_error )
326         {
327             return;
328         }
329         msleep( VOUT_OUTMEM_SLEEP );
330     }
331
332     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
333
334     switch( p_vout->p_sys->i_mode )
335     {
336         case WAVE:
337             DistortWave( p_vout, p_pic, p_outpic );
338             break;
339
340         case RIPPLE:
341             DistortRipple( p_vout, p_pic, p_outpic );
342             break;
343
344         case EDGE:
345             DistortEdge( p_vout, p_pic, p_outpic );
346             break;
347
348         case GRADIENT:
349             DistortGradient( p_vout, p_pic, p_outpic );
350             break;
351
352         case HOUGH:
353             DistortHough( p_vout, p_pic, p_outpic );
354             break;
355
356         case PSYCHEDELIC:
357             DistortPsychedelic( p_vout, p_pic, p_outpic );
358             break;
359
360         default:
361             break;
362     }
363
364     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
365 }
366
367 /*****************************************************************************
368  * DistortWave: draw a wave effect on the picture
369  *****************************************************************************/
370 static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
371                                                 picture_t *p_outpic )
372 {
373     int i_index;
374     double f_angle;
375     mtime_t new_date = mdate();
376
377     p_vout->p_sys->f_angle += (new_date - p_vout->p_sys->last_date) / 200000.0;
378     p_vout->p_sys->last_date = new_date;
379     f_angle = p_vout->p_sys->f_angle;
380
381     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
382     {
383         int i_line, i_num_lines, i_offset;
384         uint8_t black_pixel;
385         uint8_t *p_in, *p_out;
386
387         p_in = p_inpic->p[i_index].p_pixels;
388         p_out = p_outpic->p[i_index].p_pixels;
389
390         i_num_lines = p_inpic->p[i_index].i_visible_lines;
391
392         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
393
394         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
395         for( i_line = 0 ; i_line < i_num_lines ; i_line++ )
396         {
397             /* Calculate today's offset, don't go above 1/20th of the screen */
398             i_offset = (int)( (double)(p_inpic->p[i_index].i_visible_pitch)
399                          * sin( f_angle + 10.0 * (double)i_line
400                                                / (double)i_num_lines )
401                          / 20.0 );
402
403             if( i_offset )
404             {
405                 if( i_offset < 0 )
406                 {
407                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
408                              p_inpic->p[i_index].i_visible_pitch + i_offset );
409                     p_in += p_inpic->p[i_index].i_pitch;
410                     p_out += p_outpic->p[i_index].i_pitch;
411                     memset( p_out + i_offset, black_pixel, -i_offset );
412                 }
413                 else
414                 {
415                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
416                              p_inpic->p[i_index].i_visible_pitch - i_offset );
417                     memset( p_out, black_pixel, i_offset );
418                     p_in += p_inpic->p[i_index].i_pitch;
419                     p_out += p_outpic->p[i_index].i_pitch;
420                 }
421             }
422             else
423             {
424                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
425                                           p_inpic->p[i_index].i_visible_pitch );
426                 p_in += p_inpic->p[i_index].i_pitch;
427                 p_out += p_outpic->p[i_index].i_pitch;
428             }
429
430         }
431     }
432 }
433
434 /*****************************************************************************
435  * DistortRipple: draw a ripple effect at the bottom of the picture
436  *****************************************************************************/
437 static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
438                                                   picture_t *p_outpic )
439 {
440     int i_index;
441     double f_angle;
442     mtime_t new_date = mdate();
443
444     p_vout->p_sys->f_angle -= (p_vout->p_sys->last_date - new_date) / 100000.0;
445     p_vout->p_sys->last_date = new_date;
446     f_angle = p_vout->p_sys->f_angle;
447
448     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
449     {
450         int i_line, i_first_line, i_num_lines, i_offset;
451         uint8_t black_pixel;
452         uint8_t *p_in, *p_out;
453
454         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
455
456         i_num_lines = p_inpic->p[i_index].i_visible_lines;
457
458         i_first_line = i_num_lines * 4 / 5;
459
460         p_in = p_inpic->p[i_index].p_pixels;
461         p_out = p_outpic->p[i_index].p_pixels;
462
463         for( i_line = 0 ; i_line < i_first_line ; i_line++ )
464         {
465             p_vout->p_vlc->pf_memcpy( p_out, p_in,
466                                       p_inpic->p[i_index].i_visible_pitch );
467             p_in += p_inpic->p[i_index].i_pitch;
468             p_out += p_outpic->p[i_index].i_pitch;
469         }
470
471         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
472         for( i_line = i_first_line ; i_line < i_num_lines ; i_line++ )
473         {
474             /* Calculate today's offset, don't go above 1/20th of the screen */
475             i_offset = (int)( (double)(p_inpic->p[i_index].i_pitch)
476                          * sin( f_angle + 2.0 * (double)i_line
477                                               / (double)( 1 + i_line
478                                                             - i_first_line) )
479                          * (double)(i_line - i_first_line)
480                          / (double)i_num_lines
481                          / 8.0 );
482
483             if( i_offset )
484             {
485                 if( i_offset < 0 )
486                 {
487                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
488                              p_inpic->p[i_index].i_visible_pitch + i_offset );
489                     p_in -= p_inpic->p[i_index].i_pitch;
490                     p_out += p_outpic->p[i_index].i_pitch;
491                     memset( p_out + i_offset, black_pixel, -i_offset );
492                 }
493                 else
494                 {
495                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
496                              p_inpic->p[i_index].i_visible_pitch - i_offset );
497                     memset( p_out, black_pixel, i_offset );
498                     p_in -= p_inpic->p[i_index].i_pitch;
499                     p_out += p_outpic->p[i_index].i_pitch;
500                 }
501             }
502             else
503             {
504                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
505                                           p_inpic->p[i_index].i_visible_pitch );
506                 p_in -= p_inpic->p[i_index].i_pitch;
507                 p_out += p_outpic->p[i_index].i_pitch;
508             }
509
510         }
511     }
512 }
513
514 /*****************************************************************************
515  * Gaussian Convolution
516  *****************************************************************************
517  *    Gaussian convolution ( sigma == 1.4 )
518  *
519  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
520  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
521  *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
522  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
523  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
524  *****************************************************************************/
525 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
526 {
527     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
528     int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
529     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
530     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
531
532     int x,y;
533     for( y = 2; y < i_num_lines - 2; y++ )
534     {
535         for( x = 2; x < i_src_visible - 2; x++ )
536         {
537             p_smooth[y*i_src_visible+x] = (uint32_t)(
538               /* 2 rows up */
539                 ( p_inpix[(y-2)*i_src_pitch+x-2]<<1 )
540               + ( p_inpix[(y-2)*i_src_pitch+x-1]<<2 )
541               + ( p_inpix[(y-2)*i_src_pitch+x]<<2 )
542               + ( p_inpix[(y-2)*i_src_pitch+x+1]<<2 )
543               + ( p_inpix[(y-2)*i_src_pitch+x+2]<<1 )
544               /* 1 row up */
545               + ( p_inpix[(y-1)*i_src_pitch+x-2]<<2 )
546               + ( p_inpix[(y-1)*i_src_pitch+x-1]<<3 )
547               + ( p_inpix[(y-1)*i_src_pitch+x]*12 )
548               + ( p_inpix[(y-1)*i_src_pitch+x+1]<<3 )
549               + ( p_inpix[(y-1)*i_src_pitch+x+2]<<2 )
550               /* */
551               + ( p_inpix[y*i_src_pitch+x-2]<<2 )
552               + ( p_inpix[y*i_src_pitch+x-1]*12 )
553               + ( p_inpix[y*i_src_pitch+x]<<4 )
554               + ( p_inpix[y*i_src_pitch+x+1]*12 )
555               + ( p_inpix[y*i_src_pitch+x+2]<<2 )
556               /* 1 row down */
557               + ( p_inpix[(y+1)*i_src_pitch+x-2]<<2 )
558               + ( p_inpix[(y+1)*i_src_pitch+x-1]<<3 )
559               + ( p_inpix[(y+1)*i_src_pitch+x]*12 )
560               + ( p_inpix[(y+1)*i_src_pitch+x+1]<<3 )
561               + ( p_inpix[(y+1)*i_src_pitch+x+2]<<2 )
562               /* 2 rows down */
563               + ( p_inpix[(y+2)*i_src_pitch+x-2]<<1 )
564               + ( p_inpix[(y+2)*i_src_pitch+x-1]<<2 )
565               + ( p_inpix[(y+2)*i_src_pitch+x]<<2 )
566               + ( p_inpix[(y+2)*i_src_pitch+x+1]<<2 )
567               + ( p_inpix[(y+2)*i_src_pitch+x+2]<<1 )
568               ) >> 7 /* 115 */;
569         }
570     }
571 }
572
573 /*****************************************************************************
574  * DistortGradient: Sobel
575  *****************************************************************************/
576 static void DistortGradient( vout_thread_t *p_vout, picture_t *p_inpic,
577                                                   picture_t *p_outpic )
578 {
579     int x, y;
580     int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
581     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
582     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
583     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
584
585     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
586     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
587
588     uint32_t *p_smooth = (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
589
590     if( !p_smooth ) return;
591
592     if( p_vout->p_sys->b_cartoon )
593     {
594         p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
595             p_inpic->p[U_PLANE].p_pixels,
596             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
597         p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
598             p_inpic->p[V_PLANE].p_pixels,
599             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
600     }
601     else
602     {
603         p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
604             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
605         p_vout->p_vlc->pf_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
606             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
607     }
608
609     GaussianConvolution( p_inpic, p_smooth );
610
611     /* Sobel gradient
612
613      | -1 0 1 |     |  1  2  1 |
614      | -2 0 2 | and |  0  0  0 |
615      | -1 0 1 |     | -1 -2 -1 | */
616
617     for( y = 1; y < i_num_lines - 1; y++ )
618     {
619         for( x = 1; x < i_src_visible - 1; x++ )
620         {
621             uint32_t a =
622             (
623               abs(
624                 ( ( p_smooth[(y-1)*i_src_visible+x]
625                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
626                + ( p_smooth[(y-1)*i_src_visible+x-1]
627                    - p_smooth[(y+1)*i_src_visible+x-1] )
628                + ( p_smooth[(y-1)*i_src_visible+x+1]
629                    - p_smooth[(y+1)*i_src_visible+x+1] )
630               )
631             +
632               abs(
633                 ( ( p_smooth[y*i_src_visible+x-1]
634                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
635                + ( p_smooth[(y-1)*i_src_visible+x-1]
636                    - p_smooth[(y-1)*i_src_visible+x+1] )
637                + ( p_smooth[(y+1)*i_src_visible+x-1]
638                    - p_smooth[(y+1)*i_src_visible+x+1] )
639               )
640             );
641             if( p_vout->p_sys->i_gradient_type )
642             {
643                 if( p_vout->p_sys->b_cartoon )
644                 {
645                     if( a > 60 )
646                     {
647                         p_outpix[y*i_dst_pitch+x] = 0x00;
648                     }
649                     else
650                     {
651                         if( p_smooth[y*i_src_visible+x] > 0xa0 )
652                             p_outpix[y*i_dst_pitch+x] =
653                                 0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
654                         else if( p_smooth[y*i_src_visible+x] > 0x70 )
655                             p_outpix[y*i_dst_pitch+x] =
656                                 0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
657                         else if( p_smooth[y*i_src_visible+x] > 0x28 )
658                             p_outpix[y*i_dst_pitch+x] =
659                                 0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
660                         else
661                             p_outpix[y*i_dst_pitch+x] =
662                                 0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
663                     }
664                 }
665                 else
666                 {
667                     if( a>>8 )
668                         p_outpix[y*i_dst_pitch+x] = 255;
669                     else
670                         p_outpix[y*i_dst_pitch+x] = (uint8_t)a;
671                 }
672             }
673             else
674             {
675                 if( a>>8 )
676                     p_outpix[y*i_dst_pitch+x] = 0;
677                 else
678                     p_outpix[y*i_dst_pitch+x] = (uint8_t)(255 - a);
679             }
680         }
681     }
682
683     if( p_smooth ) free( p_smooth );
684 }
685
686 /*****************************************************************************
687  * DistortEdge: Canny edge detection algorithm
688  *****************************************************************************
689  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
690  * (well ... my implementation isn't really the canny algorithm ... but some
691  * ideas are the same)
692  *****************************************************************************/
693 /* angle : | */
694 #define THETA_Y 0
695 /* angle : - */
696 #define THETA_X 1
697 /* angle : / */
698 #define THETA_P 2
699 /* angle : \ */
700 #define THETA_M 3
701 static void DistortEdge( vout_thread_t *p_vout, picture_t *p_inpic,
702                                                   picture_t *p_outpic )
703 {
704     int x, y;
705
706     int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
707     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
708     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
709     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
710
711     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
712     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
713
714     uint32_t *p_smooth = malloc( i_num_lines*i_src_visible * sizeof(uint32_t) );
715     uint32_t *p_grad = malloc( i_num_lines*i_src_visible *sizeof(uint32_t) );
716     uint8_t *p_theta = malloc( i_num_lines*i_src_visible *sizeof(uint8_t) );
717
718     if( !p_smooth || !p_grad || !p_theta ) return;
719
720     if( p_vout->p_sys->b_cartoon )
721     {
722         p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
723             p_inpic->p[U_PLANE].p_pixels,
724             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
725         p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
726             p_inpic->p[V_PLANE].p_pixels,
727             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
728     }
729     else
730     {
731         p_vout->p_vlc->pf_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
732               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
733         p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
734             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
735         memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
736             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
737     }
738
739     GaussianConvolution( p_inpic, p_smooth );
740
741     /* Sobel gradient
742
743      | -1 0 1 |     |  1  2  1 |
744      | -2 0 2 | and |  0  0  0 |
745      | -1 0 1 |     | -1 -2 -1 | */
746
747     for( y = 1; y < i_num_lines - 1; y++ )
748     {
749         for( x = 1; x < i_src_visible - 1; x++ )
750         {
751
752             int gradx =
753                 ( ( p_smooth[(y-1)*i_src_visible+x]
754                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
755                + ( p_smooth[(y-1)*i_src_visible+x-1]
756                    - p_smooth[(y+1)*i_src_visible+x-1] )
757                + ( p_smooth[(y-1)*i_src_visible+x+1]
758                    - p_smooth[(y+1)*i_src_visible+x+1] );
759             int grady =
760                 ( ( p_smooth[y*i_src_visible+x-1]
761                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
762                + ( p_smooth[(y-1)*i_src_visible+x-1]
763                    - p_smooth[(y-1)*i_src_visible+x+1] )
764                + ( p_smooth[(y+1)*i_src_visible+x-1]
765                    - p_smooth[(y+1)*i_src_visible+x+1] );
766
767             p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
768
769             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
770              * tan( 26,565051177 ) = 0.5
771              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
772              * tan( 63,434948823 ) 2 */
773             if( (grady<<1) > gradx )
774                 p_theta[y*i_src_visible+x] = THETA_P;
775             else if( (grady<<1) < -gradx )
776                 p_theta[y*i_src_visible+x] = THETA_M;
777             else if( !gradx || abs(grady) > abs(gradx)<<1 )
778                 p_theta[y*i_src_visible+x] = THETA_Y;
779             else
780                 p_theta[y*i_src_visible+x] = THETA_X;
781         }
782     }
783
784     /* edge computing */
785     for( y = 1; y < i_num_lines - 1; y++ )
786     {
787         for( x = 1; x < i_src_visible - 1; x++ )
788         {
789             if( p_grad[y*i_src_visible+x] > 40 )
790             {
791                 switch( p_theta[y*i_src_visible+x] )
792                 {
793                     case THETA_Y:
794                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
795                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
796                         {
797                             p_outpix[y*i_dst_pitch+x] = 0;
798                         } else goto colorize;
799                         break;
800                     case THETA_P:
801                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
802                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
803                         {
804                             p_outpix[y*i_dst_pitch+x] = 0;
805                         } else goto colorize;
806                         break;
807                     case THETA_M:
808                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
809                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
810                         {
811                             p_outpix[y*i_dst_pitch+x] = 0;
812                         } else goto colorize;
813                         break;
814                     case THETA_X:
815                         if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
816                             && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
817                         {
818                             p_outpix[y*i_dst_pitch+x] = 0;
819                         } else goto colorize;
820                         break;
821                 }
822             }
823             else
824             {
825                 colorize:
826                 if( p_vout->p_sys->b_cartoon )
827                 {
828                     if( p_smooth[y*i_src_visible+x] > 0xa0 )
829                         p_outpix[y*i_dst_pitch+x] = (uint8_t)
830                             0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
831                     else if( p_smooth[y*i_src_visible+x] > 0x70 )
832                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
833                             0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
834                     else if( p_smooth[y*i_src_visible+x] > 0x28 )
835                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
836                             0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
837                     else
838                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
839                             0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
840                 }
841             }
842         }
843     }
844     if( p_smooth ) free( p_smooth );
845     if( p_grad ) free( p_grad );
846     if( p_theta) free( p_theta );
847 }
848
849 /*****************************************************************************
850  * DistortHough
851  *****************************************************************************/
852 #define p_pre_hough p_vout->p_sys->p_pre_hough
853 static void DistortHough( vout_thread_t *p_vout, picture_t *p_inpic,
854                                                   picture_t *p_outpic )
855 {
856     int x, y, i;
857     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
858     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
859     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
860
861     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
862
863     int i_diag = sqrt( i_num_lines * i_num_lines +
864                         i_src_visible * i_src_visible);
865     int i_max, i_phi_max, i_rho, i_rho_max;
866     int i_nb_steps = 90;
867     double d_step = M_PI / i_nb_steps;
868     double d_sin;
869     double d_cos;
870     uint32_t *p_smooth;
871     int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
872     if( ! p_hough ) return;
873     p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
874     if( !p_smooth ) return;
875
876     if( ! p_pre_hough )
877     {
878         msg_Dbg(p_vout, "Starting precalculation");
879         p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
880         if( ! p_pre_hough ) return;
881         for( i = 0 ; i < i_nb_steps ; i++)
882         {
883             d_sin = sin(d_step * i);
884             d_cos = cos(d_step * i);
885             for( y = 0 ; y < i_num_lines ; y++ )
886                 for( x = 0 ; x < i_src_visible ; x++ )
887                 {
888                     p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
889                         ceil(x*d_sin + y*d_cos);
890                 }
891         }
892         msg_Dbg(p_vout, "Precalculation done");
893     }
894
895     memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
896
897     p_vout->p_vlc->pf_memcpy(
898         p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
899         p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
900     p_vout->p_vlc->pf_memcpy(
901         p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
902         p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
903     p_vout->p_vlc->pf_memcpy(
904         p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
905         p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
906
907     GaussianConvolution( p_inpic, p_smooth );
908
909     /* Sobel gradient
910
911      | -1 0 1 |     |  1  2  1 |
912      | -2 0 2 | and |  0  0  0 |
913      | -1 0 1 |     | -1 -2 -1 | */
914
915     i_max = 0;
916     i_rho_max = 0;
917     i_phi_max = 0;
918     for( y = 4; y < i_num_lines - 4; y++ )
919     {
920         for( x = 4; x < i_src_visible - 4; x++ )
921         {
922             uint32_t a =
923             (
924               abs(
925                 ( ( p_smooth[(y-1)*i_src_visible+x]
926                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
927                + ( p_smooth[(y-1)*i_src_visible+x-1]
928                    - p_smooth[(y+1)*i_src_visible+x-1] )
929                + ( p_smooth[(y-1)*i_src_visible+x+1]
930                    - p_smooth[(y+1)*i_src_visible+x+1] )
931               )
932             +
933               abs(
934                 ( ( p_smooth[y*i_src_visible+x-1]
935                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
936                + ( p_smooth[(y-1)*i_src_visible+x-1]
937                    - p_smooth[(y-1)*i_src_visible+x+1] )
938                + ( p_smooth[(y+1)*i_src_visible+x-1]
939                    - p_smooth[(y+1)*i_src_visible+x+1] )
940               )
941             );
942             if( a>>8 )
943             {
944                 for( i = 0 ; i < i_nb_steps ; i ++ )
945                 {
946                     i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
947                     if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
948                     {
949                         i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
950                         i_rho_max = i_rho;
951                         i_phi_max = i;
952                     }
953                 }
954             }
955         }
956     }
957
958     d_sin = sin(i_phi_max*d_step);
959     d_cos = cos(i_phi_max*d_step);
960     if( d_cos != 0 )
961     {
962         for( x = 0 ; x < i_src_visible ; x++ )
963         {
964             y = (i_rho_max - x * d_sin) / d_cos;
965             if( y >= 0 && y < i_num_lines )
966                 p_outpix[y*i_dst_pitch+x] = 255;
967         }
968     }
969
970     if( p_hough ) free( p_hough );
971     if( p_smooth ) free( p_smooth );
972 }
973 #undef p_pre_hough
974
975 /*****************************************************************************
976  * DistortPsychedelic
977  *****************************************************************************/
978 static void DistortPsychedelic( vout_thread_t *p_vout, picture_t *p_inpic,
979                                                   picture_t *p_outpic )
980 {
981     unsigned int w, h;
982     int x,y;
983     uint8_t u,v;
984
985     video_format_t fmt_out = {0};
986     picture_t *p_converted;
987
988     if( !p_vout->p_sys->p_image )
989         p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
990
991     /* chrominance */
992     u = p_vout->p_sys->u;
993     v = p_vout->p_sys->v;
994     for( y = 0; y<p_outpic->p[U_PLANE].i_lines; y++)
995     {
996         memset( p_outpic->p[U_PLANE].p_pixels+y*p_outpic->p[U_PLANE].i_pitch,
997                 u, p_outpic->p[U_PLANE].i_pitch );
998         memset( p_outpic->p[V_PLANE].p_pixels+y*p_outpic->p[V_PLANE].i_pitch,
999                 v, p_outpic->p[V_PLANE].i_pitch );
1000         if( v == 0 && u != 0 )
1001             u --;
1002         else if( u == 0xff )
1003             v --;
1004         else if( v == 0xff )
1005             u ++;
1006         else if( u == 0 )
1007             v ++;
1008     }
1009
1010     /* luminance */
1011     p_vout->p_vlc->pf_memcpy(
1012                 p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
1013                 p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
1014
1015
1016     /* image visualization */
1017     fmt_out = p_vout->fmt_out;
1018     fmt_out.i_width = p_vout->render.i_width*p_vout->p_sys->scale/150;
1019     fmt_out.i_height = p_vout->render.i_height*p_vout->p_sys->scale/150;
1020     p_converted = image_Convert( p_vout->p_sys->p_image, p_inpic,
1021                                  &(p_inpic->format), &fmt_out );
1022
1023 #define copyimage( plane, b ) \
1024     for( y=0; y<p_converted->p[plane].i_visible_lines; y++) { \
1025     for( x=0; x<p_converted->p[plane].i_visible_pitch; x++) { \
1026         int nx, ny; \
1027         if( p_vout->p_sys->yinc == 1 ) \
1028             ny= y; \
1029         else \
1030             ny = p_converted->p[plane].i_visible_lines-y; \
1031         if( p_vout->p_sys->xinc == 1 ) \
1032             nx = x; \
1033         else \
1034             nx = p_converted->p[plane].i_visible_pitch-x; \
1035         p_outpic->p[plane].p_pixels[(p_vout->p_sys->x*b+nx)+(ny+p_vout->p_sys->y*b)*p_outpic->p[plane].i_pitch ] = p_converted->p[plane].p_pixels[y*p_converted->p[plane].i_pitch+x]; \
1036     } }
1037     copyimage( Y_PLANE, 2 );
1038     copyimage( U_PLANE, 1 );
1039     copyimage( V_PLANE, 1 );
1040 #undef copyimage
1041
1042     p_converted->pf_release( p_converted );
1043
1044     p_vout->p_sys->x += p_vout->p_sys->xinc;
1045     p_vout->p_sys->y += p_vout->p_sys->yinc;
1046
1047     p_vout->p_sys->scale += p_vout->p_sys->scaleinc;
1048     if( p_vout->p_sys->scale >= 50 ) p_vout->p_sys->scaleinc = -1;
1049     if( p_vout->p_sys->scale <= 1 ) p_vout->p_sys->scaleinc = 1;
1050
1051     w = p_vout->render.i_width*p_vout->p_sys->scale/150;
1052     h = p_vout->render.i_height*p_vout->p_sys->scale/150;
1053     if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
1054         p_vout->p_sys->xinc = -1;
1055     if( p_vout->p_sys->x <= 0 )
1056         p_vout->p_sys->xinc = 1;
1057
1058     if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
1059         p_vout->p_sys->x = (p_vout->render.i_width-w)/2;
1060     if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
1061         p_vout->p_sys->y = (p_vout->render.i_height-h)/2;
1062
1063     if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
1064         p_vout->p_sys->yinc = -1;
1065     if( p_vout->p_sys->y <= 0 )
1066         p_vout->p_sys->yinc = 1;
1067
1068     for( y = 0; y< 16; y++ )
1069     {
1070         if( p_vout->p_sys->v == 0 && p_vout->p_sys->u != 0 )
1071             p_vout->p_sys->u -= 1;
1072         else if( p_vout->p_sys->u == 0xff )
1073             p_vout->p_sys->v -= 1;
1074         else if( p_vout->p_sys->v == 0xff )
1075             p_vout->p_sys->u += 1;
1076         else if( p_vout->p_sys->u == 0 )
1077             p_vout->p_sys->v += 1;
1078     }
1079 }
1080
1081
1082 /*****************************************************************************
1083  * SendEvents: forward mouse and keyboard events to the parent p_vout
1084  *****************************************************************************/
1085 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
1086                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1087 {
1088     var_Set( (vlc_object_t *)p_data, psz_var, newval );
1089
1090     return VLC_SUCCESS;
1091 }
1092
1093 /*****************************************************************************
1094  * SendEventsToChild: forward events to the child/children vout
1095  *****************************************************************************/
1096 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
1097                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1098 {
1099     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1100     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
1101     return VLC_SUCCESS;
1102 }